From c5287ea60f123b21804778cf976a1aa2e848b61c Mon Sep 17 00:00:00 2001 From: tylunel Date: Wed, 21 Aug 2019 13:50:16 -0400 Subject: [PATCH 01/46] function getparams_desoto added to pvsystem module. This commit is just the copy from my previous work, slighty modified for removing the PEP8 warnings. --- pvlib/pvsystem.py | 171 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index f040b5f519..e071aee0f2 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -1213,6 +1213,177 @@ def iam_interp(aoi, theta_ref, iam_ref, method='linear', normalize=True): return iam +def getparams_desoto(I_sc, V_oc, I_mp, V_mp, alpha_sc, N_s, beta_oc=None): + """ + Calculates the five parameters for the single diode equation at + standard irradiance and standard cell temperature using the De Soto et al. + model described in [1]. The six values returned by getparams_desoto + can be used by calcparams_desoto to calculate the values at different + irradiance and cell temperature. + + Parameters + ---------- + I_sc : numeric + Short-circuit current at std conditions in A. + + V_oc : numeric + Open-circuit voltage at std conditions in V. + + I_mp : numeric + Module current at the maximum-power point at std conditions in A. + + V_mp : numeric + Module voltage at the maximum-power point at std conditions in V. + + alpha_sc : numeric + The short-circuit current (I_sc) temperature coefficient of the + module in units of %/K. It is converted in A/K for the computing + process. + + N_s : numeric + Number of cell in the module. + + beta_oc : numeric + The open-circuit voltage (V_oc) temperature coefficient of the + module in units of %/K. It is converted in V/K for the computing + process. + + Returns + ------- + Dictionnary with the following elements: + + photocurrent 'I_L_ref': numeric + Light-generated current in amperes at std conditions. + + saturation_current 'I_o_ref': numeric + Diode saturation curent in amperes at std conditions + + resistance_series 'R_s': numeric + Series resistance in ohms. Note that '_ref' is not mentionned + in the name because this resistance is not sensible to the + conditions of test. + + resistance_shunt 'R_sh_ref': numeric + Shunt resistance in ohms at std conditions. + + 'a_ref' (= nNsVth): numeric + Diode factor at std conditions. + The product of the usual diode ideality factor (n, unitless), + number of cells in series (Ns), and cell thermal voltage at + specified effective irradiance and cell temperature. + + 'alpha_sc': numeric + Caution!: Different from the input because of the unit. + The short-circuit current (I_sc) temperature coefficient of the + module in units of A/K. + + References + ---------- + [1] W. De Soto et al., "Improvement and validation of a model for + photovoltaic array performance", Solar Energy, vol 80, pp. 78-88, + 2006. + """ + # Constants + k = 1.381e-23 # Boltzmann + q = 1.602e-19 # electron charge in J/V + Tref = 25.0 + 273.15 + C = 0.0002677 + Eg = 1.796e-19 + + # Conversion from %/K to A/K & V/K + alpha_sc = alpha_sc*I_sc/100 + beta_oc = beta_oc*V_oc/100 + + def pv_fct(x, param): + """Returns the system of equations used for computing the + single-diode 5 parameters. + To avoid the confusion in names with variables of container function + the '_' of the variables were removed. + """ + + Isc = param[0] + Voc = param[1] + Imp = param[2] + Vmp = param[3] + betaoc = param[4] + alphasc = param[5] + + # five parameters vector + IL = x[0] + Io = x[1] + a = x[2] + Rsh = x[3] + Rs = x[4] + # five equation vector + y = [0, 0, 0, 0, 0] + + Idsc = Io*(np.exp(Isc*Rs/a) - 1.0) + Idoc = Io*(np.exp(Voc/a) - 1.0) + exm = np.exp((Vmp+Imp*Rs)/a) + Idm = Io*(exm - 1.0) + # 1st equation - short-circuit + y[0] = Isc - IL + Idsc + Isc*Rs/Rsh + # 2nd equation - open-circuit ref + y[1] = -IL + Idoc + Voc/Rsh + # 3rd equation - max Imp + y[2] = Imp - IL + Idm + (Vmp+Imp*Rs)/Rsh + # 4th equation - max Imp der + y[3] = Imp - Vmp*(Io*exm/a + 1.0/Rsh)/(1.0+Io*exm*Rs/a+Rs/Rsh) + # 5th equation - open-circuit T2 + if betaoc is None: # option 1 - doesn't work yet-DeSoto method + raise NotImplementedError( + 'The function getparams_desoto needs beta_oc ' + 'for computing the five parameters for now. This can be ' + 'avoided if the code followed the exact same procedure than ' + 'described in DeSoto&al(2006)' + ) + T2 = Tref + 2 + a2 = a*T2/Tref + IL2 = IL + alphasc*(T2-Tref) + Eg2 = Eg*(1-C*(T2-Tref)) + Io2 = Io*(T2/Tref)**3*np.exp(Eg2/(k*Tref) - Eg2/(k*T2)) + + def fct_5(Voc2): + return IL2-Io2*(np.exp(Voc2/a2)-1)-Voc2/Rsh + sol5 = scipy.optimize.root(fct_5, xi, args=param, method='lm') + else: + # option 2 - open-circuit T2 + T2 = Tref + 2 + Voc2 = (T2-Tref)*betaoc + Voc + a2 = a*T2/Tref + IL2 = IL + alphasc*(T2-Tref) + Eg2 = Eg*(1-C*(T2-Tref)) + Io2 = Io*(T2/Tref)**3*np.exp(Eg2/(k*Tref) - Eg2/(k*T2)) + Idoc2 = Io2*(np.exp(Voc2/a2) - 1.0) + y[4] = -IL2 + Idoc2 + Voc2/Rsh + + return y + + # initialization of variables for computing convergence + Rsh1 = 100.0 + a1 = 1.5*k*Tref*N_s/q + IL1 = I_sc + Io1 = (IL1-V_oc/Rsh1) / (np.exp(V_oc/a1)-1) + Rs1 = (a1*np.log((IL1-I_mp)/Io1+1) - V_mp)/I_mp + # xi : initial values vector + xi = np.array([IL1, Io1, a1, Rsh1, Rs1]) + # params of module + param = np.array([I_sc, V_oc, I_mp, V_mp, beta_oc, alpha_sc]) + # computing + res = scipy.optimize.root(pv_fct, xi, args=param, method='lm') + sol = res.x + + # results + res = {'I_L_ref': sol[0], + 'I_o_ref': sol[1], + 'a_ref': sol[2], + 'R_sh_ref': sol[3], + 'R_s': sol[4], + 'alpha_sc': alpha_sc + } + return res + + def calcparams_desoto(effective_irradiance, temp_cell, alpha_sc, a_ref, I_L_ref, I_o_ref, R_sh_ref, R_s, EgRef=1.121, dEgdT=-0.0002677, From 5a39fea5e4edc2a82b16c0a508bb15bd06e8f480 Mon Sep 17 00:00:00 2001 From: tylunel Date: Wed, 21 Aug 2019 15:07:33 -0400 Subject: [PATCH 02/46] getparams_desoto moved from pvsystem to singlediode. - getparams_desoto renamed in getparams_from_specs - some initializing values were changed in accordance with Duffie&Beckman2013 --- pvlib/singlediode.py | 182 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 181 insertions(+), 1 deletion(-) diff --git a/pvlib/singlediode.py b/pvlib/singlediode.py index 49f1f4ebe3..202afdbe73 100644 --- a/pvlib/singlediode.py +++ b/pvlib/singlediode.py @@ -12,10 +12,11 @@ # ImportError when 'brentq' method is specified for those methods. try: from scipy.optimize import brentq + from scipy.optimize import root except ImportError: def brentq(*a, **kw): raise ImportError( - "brentq couldn't be imported. Is SciPy installed?") + "brentq or root couldn't be imported. Is SciPy installed?") # FIXME: change this to newton when scipy-1.2 is released try: @@ -29,6 +30,185 @@ def brentq(*a, **kw): VOLTAGE_BUILTIN = 0.9 # [V] +def getparams_from_specs(I_sc, V_oc, I_mp, V_mp, alpha_sc, + beta_oc=None, N_s=60): + """ + Calculates the five parameters for the single diode equation at + standard irradiance and standard cell temperature using the De Soto et al. + procedure described in [1]. This procedure has the advantage of using + common specifications given by manufacturers in the datasheets of + PV modules. + The six values returned by getparams_desoto + can be used by calcparams_desoto to calculate the values at different + irradiance and cell temperature. + + Parameters + ---------- + I_sc : numeric + Short-circuit current at std conditions in A. + + V_oc : numeric + Open-circuit voltage at std conditions in V. + + I_mp : numeric + Module current at the maximum-power point at std conditions in A. + + V_mp : numeric + Module voltage at the maximum-power point at std conditions in V. + + alpha_sc : numeric + The short-circuit current (I_sc) temperature coefficient of the + module in units of %/K. It is converted in A/K for the computing + process. + + N_s : numeric + Number of cell in the module. + Optional input, but helps to insure the convergence of the computing. + + beta_oc : numeric + The open-circuit voltage (V_oc) temperature coefficient of the + module in units of %/K. It is converted in V/K for the computing + process. + Optional input, but makes faster the computing. + + Returns + ------- + Dictionnary with the following elements: + + photocurrent 'I_L_ref': numeric + Light-generated current in amperes at std conditions. + + saturation_current 'I_o_ref': numeric + Diode saturation curent in amperes at std conditions + + resistance_series 'R_s': numeric + Series resistance in ohms. Note that '_ref' is not mentionned + in the name because this resistance is not sensible to the + conditions of test. + + resistance_shunt 'R_sh_ref': numeric + Shunt resistance in ohms at std conditions. + + 'a_ref' (= nNsVth): numeric + Diode factor at std conditions. + The product of the usual diode ideality factor (n, unitless), + number of cells in series (Ns), and cell thermal voltage at + specified effective irradiance and cell temperature. + + 'alpha_sc': numeric + Caution!: Different from the input because of the unit. + The short-circuit current (I_sc) temperature coefficient of the + module in units of A/K. + + References + ---------- + [1] W. De Soto et al., "Improvement and validation of a model for + photovoltaic array performance", Solar Energy, vol 80, pp. 78-88, + 2006. + """ + # Constants + k = 1.381e-23 # Boltzmann + q = 1.602e-19 # electron charge in J/V + Tref = 25.0 + 273.15 + C = 0.0002677 + Eg = 1.796e-19 + + # Conversion from %/K to A/K & V/K + alpha_sc = alpha_sc*I_sc/100 + beta_oc = beta_oc*V_oc/100 + + def pv_fct(x, param): + """Returns the system of equations used for computing the + single-diode 5 parameters. + To avoid the confusion in names with variables of container function + the '_' of the variables were removed. + """ + + Isc = param[0] + Voc = param[1] + Imp = param[2] + Vmp = param[3] + betaoc = param[4] + alphasc = param[5] + + # five parameters vector + IL = x[0] + Io = x[1] + a = x[2] + Rsh = x[3] + Rs = x[4] + # five equation vector + y = [0, 0, 0, 0, 0] + + Idsc = Io*(np.exp(Isc*Rs/a) - 1.0) + Idoc = Io*(np.exp(Voc/a) - 1.0) + exm = np.exp((Vmp+Imp*Rs)/a) + Idm = Io*(exm - 1.0) + # 1st equation - short-circuit + y[0] = Isc - IL + Idsc + Isc*Rs/Rsh + # 2nd equation - open-circuit ref + y[1] = -IL + Idoc + Voc/Rsh + # 3rd equation - max Imp + y[2] = Imp - IL + Idm + (Vmp+Imp*Rs)/Rsh + # 4th equation - max Imp der + y[3] = Imp - Vmp*(Io*exm/a + 1.0/Rsh)/(1.0+Io*exm*Rs/a+Rs/Rsh) + # 5th equation - open-circuit T2 + if betaoc is None: # option 1 - doesn't work yet-DeSoto method + raise NotImplementedError( + 'The function getparams_desoto needs beta_oc ' + 'for computing the five parameters for now. This can be ' + 'avoided if the code followed the exact same procedure than ' + 'described in DeSoto&al(2006)' + ) + T2 = Tref + 2 + a2 = a*T2/Tref + IL2 = IL + alphasc*(T2-Tref) + Eg2 = Eg*(1-C*(T2-Tref)) + Io2 = Io*(T2/Tref)**3*np.exp(Eg2/(k*Tref) - Eg2/(k*T2)) + + def fct_5(Voc2): + return IL2-Io2*(np.exp(Voc2/a2)-1)-Voc2/Rsh + sol5 = root(fct_5, xi, args=param, method='lm') + else: + # option 2 - open-circuit T2 + T2 = Tref + 2 + Voc2 = (T2-Tref)*betaoc + Voc + a2 = a*T2/Tref + IL2 = IL + alphasc*(T2-Tref) + Eg2 = Eg*(1-C*(T2-Tref)) + Io2 = Io*(T2/Tref)**3*np.exp(Eg2/(k*Tref) - Eg2/(k*T2)) + Idoc2 = Io2*(np.exp(Voc2/a2) - 1.0) + y[4] = -IL2 + Idoc2 + Voc2/Rsh + + return y + + # initialization of variables for computing convergence: + # Values are taken from Duffie & Beckman (2013), p753 + Rsh1 = 100.0 + a1 = 1.5*k*Tref*N_s/q + IL1 = I_sc +# Io1 = (IL1-V_oc/Rsh1) / (np.exp(V_oc/a1)-1) + Io1 = I_sc * np.exp(-V_oc/a1) + Rs1 = (a1*np.log((IL1-I_mp)/Io1+1) - V_mp)/I_mp + # xi : initial values vector + xi = np.array([IL1, Io1, a1, Rsh1, Rs1]) + # params of module + param = np.array([I_sc, V_oc, I_mp, V_mp, beta_oc, alpha_sc]) + # computing + res = root(pv_fct, xi, args=param, method='lm') + sol = res.x + + # results + res = {'I_L_ref': sol[0], + 'I_o_ref': sol[1], + 'a_ref': sol[2], + 'R_sh_ref': sol[3], + 'R_s': sol[4], + 'alpha_sc': alpha_sc + } + return res + + def estimate_voc(photocurrent, saturation_current, nNsVth): """ Rough estimate of open circuit voltage useful for bounding searches for From 7b242018c5e21ca4a99e40289ee38fdc5f391007 Mon Sep 17 00:00:00 2001 From: tylunel Date: Thu, 22 Aug 2019 10:13:03 -0400 Subject: [PATCH 03/46] - Modification of getparams_from_specs so as it follows better the procedure of DeSoto&al(2006). --- pvlib/pvsystem.py | 171 ------------------------------------------- pvlib/singlediode.py | 136 +++++++++++++++------------------- 2 files changed, 58 insertions(+), 249 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index e071aee0f2..f040b5f519 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -1213,177 +1213,6 @@ def iam_interp(aoi, theta_ref, iam_ref, method='linear', normalize=True): return iam -def getparams_desoto(I_sc, V_oc, I_mp, V_mp, alpha_sc, N_s, beta_oc=None): - """ - Calculates the five parameters for the single diode equation at - standard irradiance and standard cell temperature using the De Soto et al. - model described in [1]. The six values returned by getparams_desoto - can be used by calcparams_desoto to calculate the values at different - irradiance and cell temperature. - - Parameters - ---------- - I_sc : numeric - Short-circuit current at std conditions in A. - - V_oc : numeric - Open-circuit voltage at std conditions in V. - - I_mp : numeric - Module current at the maximum-power point at std conditions in A. - - V_mp : numeric - Module voltage at the maximum-power point at std conditions in V. - - alpha_sc : numeric - The short-circuit current (I_sc) temperature coefficient of the - module in units of %/K. It is converted in A/K for the computing - process. - - N_s : numeric - Number of cell in the module. - - beta_oc : numeric - The open-circuit voltage (V_oc) temperature coefficient of the - module in units of %/K. It is converted in V/K for the computing - process. - - Returns - ------- - Dictionnary with the following elements: - - photocurrent 'I_L_ref': numeric - Light-generated current in amperes at std conditions. - - saturation_current 'I_o_ref': numeric - Diode saturation curent in amperes at std conditions - - resistance_series 'R_s': numeric - Series resistance in ohms. Note that '_ref' is not mentionned - in the name because this resistance is not sensible to the - conditions of test. - - resistance_shunt 'R_sh_ref': numeric - Shunt resistance in ohms at std conditions. - - 'a_ref' (= nNsVth): numeric - Diode factor at std conditions. - The product of the usual diode ideality factor (n, unitless), - number of cells in series (Ns), and cell thermal voltage at - specified effective irradiance and cell temperature. - - 'alpha_sc': numeric - Caution!: Different from the input because of the unit. - The short-circuit current (I_sc) temperature coefficient of the - module in units of A/K. - - References - ---------- - [1] W. De Soto et al., "Improvement and validation of a model for - photovoltaic array performance", Solar Energy, vol 80, pp. 78-88, - 2006. - """ - # Constants - k = 1.381e-23 # Boltzmann - q = 1.602e-19 # electron charge in J/V - Tref = 25.0 + 273.15 - C = 0.0002677 - Eg = 1.796e-19 - - # Conversion from %/K to A/K & V/K - alpha_sc = alpha_sc*I_sc/100 - beta_oc = beta_oc*V_oc/100 - - def pv_fct(x, param): - """Returns the system of equations used for computing the - single-diode 5 parameters. - To avoid the confusion in names with variables of container function - the '_' of the variables were removed. - """ - - Isc = param[0] - Voc = param[1] - Imp = param[2] - Vmp = param[3] - betaoc = param[4] - alphasc = param[5] - - # five parameters vector - IL = x[0] - Io = x[1] - a = x[2] - Rsh = x[3] - Rs = x[4] - # five equation vector - y = [0, 0, 0, 0, 0] - - Idsc = Io*(np.exp(Isc*Rs/a) - 1.0) - Idoc = Io*(np.exp(Voc/a) - 1.0) - exm = np.exp((Vmp+Imp*Rs)/a) - Idm = Io*(exm - 1.0) - # 1st equation - short-circuit - y[0] = Isc - IL + Idsc + Isc*Rs/Rsh - # 2nd equation - open-circuit ref - y[1] = -IL + Idoc + Voc/Rsh - # 3rd equation - max Imp - y[2] = Imp - IL + Idm + (Vmp+Imp*Rs)/Rsh - # 4th equation - max Imp der - y[3] = Imp - Vmp*(Io*exm/a + 1.0/Rsh)/(1.0+Io*exm*Rs/a+Rs/Rsh) - # 5th equation - open-circuit T2 - if betaoc is None: # option 1 - doesn't work yet-DeSoto method - raise NotImplementedError( - 'The function getparams_desoto needs beta_oc ' - 'for computing the five parameters for now. This can be ' - 'avoided if the code followed the exact same procedure than ' - 'described in DeSoto&al(2006)' - ) - T2 = Tref + 2 - a2 = a*T2/Tref - IL2 = IL + alphasc*(T2-Tref) - Eg2 = Eg*(1-C*(T2-Tref)) - Io2 = Io*(T2/Tref)**3*np.exp(Eg2/(k*Tref) - Eg2/(k*T2)) - - def fct_5(Voc2): - return IL2-Io2*(np.exp(Voc2/a2)-1)-Voc2/Rsh - sol5 = scipy.optimize.root(fct_5, xi, args=param, method='lm') - else: - # option 2 - open-circuit T2 - T2 = Tref + 2 - Voc2 = (T2-Tref)*betaoc + Voc - a2 = a*T2/Tref - IL2 = IL + alphasc*(T2-Tref) - Eg2 = Eg*(1-C*(T2-Tref)) - Io2 = Io*(T2/Tref)**3*np.exp(Eg2/(k*Tref) - Eg2/(k*T2)) - Idoc2 = Io2*(np.exp(Voc2/a2) - 1.0) - y[4] = -IL2 + Idoc2 + Voc2/Rsh - - return y - - # initialization of variables for computing convergence - Rsh1 = 100.0 - a1 = 1.5*k*Tref*N_s/q - IL1 = I_sc - Io1 = (IL1-V_oc/Rsh1) / (np.exp(V_oc/a1)-1) - Rs1 = (a1*np.log((IL1-I_mp)/Io1+1) - V_mp)/I_mp - # xi : initial values vector - xi = np.array([IL1, Io1, a1, Rsh1, Rs1]) - # params of module - param = np.array([I_sc, V_oc, I_mp, V_mp, beta_oc, alpha_sc]) - # computing - res = scipy.optimize.root(pv_fct, xi, args=param, method='lm') - sol = res.x - - # results - res = {'I_L_ref': sol[0], - 'I_o_ref': sol[1], - 'a_ref': sol[2], - 'R_sh_ref': sol[3], - 'R_s': sol[4], - 'alpha_sc': alpha_sc - } - return res - - def calcparams_desoto(effective_irradiance, temp_cell, alpha_sc, a_ref, I_L_ref, I_o_ref, R_sh_ref, R_s, EgRef=1.121, dEgdT=-0.0002677, diff --git a/pvlib/singlediode.py b/pvlib/singlediode.py index 202afdbe73..b08672311c 100644 --- a/pvlib/singlediode.py +++ b/pvlib/singlediode.py @@ -30,8 +30,7 @@ def brentq(*a, **kw): VOLTAGE_BUILTIN = 0.9 # [V] -def getparams_from_specs(I_sc, V_oc, I_mp, V_mp, alpha_sc, - beta_oc=None, N_s=60): +def getparams_from_specs(I_sc, V_oc, I_mp, V_mp, alpha_sc, beta_oc, N_s=60): """ Calculates the five parameters for the single diode equation at standard irradiance and standard cell temperature using the De Soto et al. @@ -90,7 +89,7 @@ def getparams_from_specs(I_sc, V_oc, I_mp, V_mp, alpha_sc, Shunt resistance in ohms at std conditions. 'a_ref' (= nNsVth): numeric - Diode factor at std conditions. + Modified ideality factor at std conditions. The product of the usual diode ideality factor (n, unitless), number of cells in series (Ns), and cell thermal voltage at specified effective irradiance and cell temperature. @@ -105,98 +104,79 @@ def getparams_from_specs(I_sc, V_oc, I_mp, V_mp, alpha_sc, [1] W. De Soto et al., "Improvement and validation of a model for photovoltaic array performance", Solar Energy, vol 80, pp. 78-88, 2006. + + [2] John A Duffie ,William A Beckman, "Solar Engineering of Thermal + Processes", Wiley, 2013 """ # Constants - k = 1.381e-23 # Boltzmann - q = 1.602e-19 # electron charge in J/V - Tref = 25.0 + 273.15 - C = 0.0002677 - Eg = 1.796e-19 + k = 1.381e-23 # Boltzmann constant, in J/K, or 8.617e-5 eV/K + q = 1.602e-19 # electron charge in J/V, or 1 eV + Tref = 25.0 + 273.15 # in K + C = 0.0002677 # valid for silicon + Eg = 1.796e-19 # in J, valid for silicon # Conversion from %/K to A/K & V/K alpha_sc = alpha_sc*I_sc/100 beta_oc = beta_oc*V_oc/100 - def pv_fct(x, param): + def pv_fct(params, specs): """Returns the system of equations used for computing the single-diode 5 parameters. - To avoid the confusion in names with variables of container function - the '_' of the variables were removed. + To avoid the confusion in names with variables of container + function the '_' of the variables were removed. """ - - Isc = param[0] - Voc = param[1] - Imp = param[2] - Vmp = param[3] - betaoc = param[4] - alphasc = param[5] - - # five parameters vector - IL = x[0] - Io = x[1] - a = x[2] - Rsh = x[3] - Rs = x[4] + # six input known variables + Isc = specs[0] + Voc = specs[1] + Imp = specs[2] + Vmp = specs[3] + betaoc = specs[4] + alphasc = specs[5] + # five parameters vector to find + IL = params[0] + Io = params[1] + a = params[2] + Rsh = params[3] + Rs = params[4] # five equation vector y = [0, 0, 0, 0, 0] - Idsc = Io*(np.exp(Isc*Rs/a) - 1.0) - Idoc = Io*(np.exp(Voc/a) - 1.0) - exm = np.exp((Vmp+Imp*Rs)/a) - Idm = Io*(exm - 1.0) - # 1st equation - short-circuit - y[0] = Isc - IL + Idsc + Isc*Rs/Rsh - # 2nd equation - open-circuit ref - y[1] = -IL + Idoc + Voc/Rsh - # 3rd equation - max Imp - y[2] = Imp - IL + Idm + (Vmp+Imp*Rs)/Rsh - # 4th equation - max Imp der - y[3] = Imp - Vmp*(Io*exm/a + 1.0/Rsh)/(1.0+Io*exm*Rs/a+Rs/Rsh) - # 5th equation - open-circuit T2 - if betaoc is None: # option 1 - doesn't work yet-DeSoto method - raise NotImplementedError( - 'The function getparams_desoto needs beta_oc ' - 'for computing the five parameters for now. This can be ' - 'avoided if the code followed the exact same procedure than ' - 'described in DeSoto&al(2006)' - ) - T2 = Tref + 2 - a2 = a*T2/Tref - IL2 = IL + alphasc*(T2-Tref) - Eg2 = Eg*(1-C*(T2-Tref)) - Io2 = Io*(T2/Tref)**3*np.exp(Eg2/(k*Tref) - Eg2/(k*T2)) - - def fct_5(Voc2): - return IL2-Io2*(np.exp(Voc2/a2)-1)-Voc2/Rsh - sol5 = root(fct_5, xi, args=param, method='lm') - else: - # option 2 - open-circuit T2 - T2 = Tref + 2 - Voc2 = (T2-Tref)*betaoc + Voc - a2 = a*T2/Tref - IL2 = IL + alphasc*(T2-Tref) - Eg2 = Eg*(1-C*(T2-Tref)) - Io2 = Io*(T2/Tref)**3*np.exp(Eg2/(k*Tref) - Eg2/(k*T2)) - Idoc2 = Io2*(np.exp(Voc2/a2) - 1.0) - y[4] = -IL2 + Idoc2 + Voc2/Rsh + # 1st equation - short-circuit - eq(3) in [1] + y[0] = Isc - IL + Io*(np.exp(Isc*Rs/a) - 1.0) + Isc*Rs/Rsh + # 2nd equation - open-circuit Tref - eq(4) in [1] + y[1] = -IL + Io*(np.exp(Voc/a) - 1.0) + Voc/Rsh + # 3rd equation - Imp & Vmp - eq(5) in [1] + y[2] = Imp - IL + Io*(np.exp((Vmp+Imp*Rs)/a) - 1.0) + \ + (Vmp+Imp*Rs)/Rsh + # 4th equation - Pmp derivated=0 - eq(6) in [1] + y[3] = Imp - Vmp * ((-Io/a)*np.exp((Vmp+Imp*Rs)/a) - 1.0/Rsh) / \ + (1.0 + (Io*Rs/a)*np.exp((Vmp+Imp*Rs)/a) + Rs/Rsh) + # 5th equation - open-circuit T2 - eq (4) at temperature T2 in [1] + T2 = Tref + 2 + Voc2 = (T2 - Tref)*betaoc + Voc # eq (7) in [1] + a2 = a*T2/Tref # eq (8) in [1] + IL2 = IL + alphasc*(T2-Tref) # eq (11) in [1] + Eg2 = Eg*(1-C*(T2-Tref)) # eq (10) in [1] + Io2 = Io * (T2/Tref)**3 * np.exp(1/k * (Eg/Tref-Eg2/T2)) # eq (9) + y[4] = -IL2 + Io2*(np.exp(Voc2/a2) - 1.0) + Voc2/Rsh # eq (4) at T2 return y - # initialization of variables for computing convergence: - # Values are taken from Duffie & Beckman (2013), p753 - Rsh1 = 100.0 - a1 = 1.5*k*Tref*N_s/q - IL1 = I_sc -# Io1 = (IL1-V_oc/Rsh1) / (np.exp(V_oc/a1)-1) - Io1 = I_sc * np.exp(-V_oc/a1) - Rs1 = (a1*np.log((IL1-I_mp)/Io1+1) - V_mp)/I_mp - # xi : initial values vector - xi = np.array([IL1, Io1, a1, Rsh1, Rs1]) - # params of module - param = np.array([I_sc, V_oc, I_mp, V_mp, beta_oc, alpha_sc]) + # initial guesses of variables for computing convergence: + # Values are taken [2], p753 + Rsh_i = 100.0 + a_i = 1.5*k*Tref*N_s/q + IL_i = I_sc + Io_i = I_sc * np.exp(-V_oc/a_i) + Rs_i = (a_i*np.log((IL_i-I_mp)/Io_i+1) - V_mp)/I_mp + # params_i : initial values vector + params_i = np.array([IL_i, Io_i, a_i, Rsh_i, Rs_i]) + + # specs of module + specs = np.array([I_sc, V_oc, I_mp, V_mp, beta_oc, alpha_sc]) + # computing - res = root(pv_fct, xi, args=param, method='lm') - sol = res.x + sol = root(pv_fct, x0=params_i, args=specs, method='lm').x # results res = {'I_L_ref': sol[0], From c16f1d0547ac84944a4d2598f39feefce6a05537 Mon Sep 17 00:00:00 2001 From: tylunel Date: Thu, 22 Aug 2019 15:37:22 -0400 Subject: [PATCH 04/46] - Bug corrected in DeSoto(2006) procedure - test_singlediode completed with a beginning of test_getparams_from_specs --- pvlib/singlediode.py | 5 +++-- pvlib/test/test_singlediode.py | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/pvlib/singlediode.py b/pvlib/singlediode.py index b08672311c..4c0dedeed0 100644 --- a/pvlib/singlediode.py +++ b/pvlib/singlediode.py @@ -148,8 +148,9 @@ def pv_fct(params, specs): # 3rd equation - Imp & Vmp - eq(5) in [1] y[2] = Imp - IL + Io*(np.exp((Vmp+Imp*Rs)/a) - 1.0) + \ (Vmp+Imp*Rs)/Rsh - # 4th equation - Pmp derivated=0 - eq(6) in [1] - y[3] = Imp - Vmp * ((-Io/a)*np.exp((Vmp+Imp*Rs)/a) - 1.0/Rsh) / \ + # 4th equation - Pmp derivated=0 - + # caution: eq(6) in [1] is incorrect, take eq23.2.6 in [2] + y[3] = Imp - Vmp * ((Io/a)*np.exp((Vmp+Imp*Rs)/a) + 1.0/Rsh) / \ (1.0 + (Io*Rs/a)*np.exp((Vmp+Imp*Rs)/a) + Rs/Rsh) # 5th equation - open-circuit T2 - eq (4) at temperature T2 in [1] T2 = Tref + 2 diff --git a/pvlib/test/test_singlediode.py b/pvlib/test/test_singlediode.py index 4ff7bc445f..318cc5b748 100644 --- a/pvlib/test/test_singlediode.py +++ b/pvlib/test/test_singlediode.py @@ -3,6 +3,7 @@ """ import numpy as np +import pvlib from pvlib import pvsystem from pvlib.singlediode import bishop88, estimate_voc, VOLTAGE_BUILTIN import pytest @@ -13,6 +14,19 @@ CECMOD = pvsystem.retrieve_sam('cecmod') +# run this test solely: +# pytest.main(['test_singlediode.py::test_getparams_from_specs']) +def test_getparams_from_specs(): + """test singlediode.getparams_from_specs with specs of the + Sunpower SPR-E20-327 module. + """ + params = pvlib.singlediode.getparams_from_specs(I_sc=6.46, V_oc=64.9, + I_mp=5.98, V_mp=54.7, + alpha_sc=0.05, + beta_oc=-0.270, + N_s=96) + + @requires_scipy def test_newton_spr_e20_327(): """test pvsystem.singlediode with Newton method on SPR-E20-327""" From e7ef571cd44e7f1ed3c4a2c084511096f56e2896 Mon Sep 17 00:00:00 2001 From: tylunel Date: Fri, 23 Aug 2019 08:41:44 -0400 Subject: [PATCH 05/46] - test_getparams_from_specs_desoto() finished - function renamed as above --- pvlib/singlediode.py | 2 +- pvlib/test/test_singlediode.py | 24 ++++++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/pvlib/singlediode.py b/pvlib/singlediode.py index 4c0dedeed0..54d2699b6a 100644 --- a/pvlib/singlediode.py +++ b/pvlib/singlediode.py @@ -30,7 +30,7 @@ def brentq(*a, **kw): VOLTAGE_BUILTIN = 0.9 # [V] -def getparams_from_specs(I_sc, V_oc, I_mp, V_mp, alpha_sc, beta_oc, N_s=60): +def getparams_from_specs_desoto(I_sc, V_oc, I_mp, V_mp, alpha_sc, beta_oc, N_s=60): """ Calculates the five parameters for the single diode equation at standard irradiance and standard cell temperature using the De Soto et al. diff --git a/pvlib/test/test_singlediode.py b/pvlib/test/test_singlediode.py index 318cc5b748..1e529ed00c 100644 --- a/pvlib/test/test_singlediode.py +++ b/pvlib/test/test_singlediode.py @@ -11,20 +11,28 @@ POA = 888 TCELL = 55 -CECMOD = pvsystem.retrieve_sam('cecmod') +#CECMOD = pvsystem.retrieve_sam('cecmod') # run this test solely: -# pytest.main(['test_singlediode.py::test_getparams_from_specs']) -def test_getparams_from_specs(): +# pytest.main(['test_singlediode.py::test_getparams_from_specs_desoto']) +def test_getparams_from_specs_desoto(): """test singlediode.getparams_from_specs with specs of the Sunpower SPR-E20-327 module. """ - params = pvlib.singlediode.getparams_from_specs(I_sc=6.46, V_oc=64.9, - I_mp=5.98, V_mp=54.7, - alpha_sc=0.05, - beta_oc=-0.270, - N_s=96) + params = pvlib.singlediode.getparams_from_specs_desoto( + I_sc=6.46, V_oc=64.9, I_mp=5.98, V_mp=54.7, alpha_sc=0.05, + beta_oc=-0.270, N_s=96 + ) + params_expected = {'I_L_ref': 6.47200, + 'I_o_ref': 6.05276e-12, + 'a_ref': 2.34662, + 'R_sh_ref': 248.450, + 'R_s': 0.461519, + 'alpha_sc': 0.003230} + np.testing.assert_allclose(list(params.values()), + list(params_expected.values()), + rtol=1e-4) @requires_scipy From cdb2a73b9867883758f78f2e25116f686d0996c1 Mon Sep 17 00:00:00 2001 From: tylunel Date: Mon, 9 Sep 2019 13:44:03 -0400 Subject: [PATCH 06/46] Not sure of all changes brought by this commit because of holidays. - function 'from_epw' added in location - 1 modification in singlediode - 1 modification in test_singlediode --- pvlib/location.py | 37 ++++++++++++++++++++++++++++++++++ pvlib/singlediode.py | 3 ++- pvlib/test/test_singlediode.py | 2 +- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/pvlib/location.py b/pvlib/location.py index 68b2062907..7cad58fe09 100644 --- a/pvlib/location.py +++ b/pvlib/location.py @@ -128,6 +128,43 @@ def from_tmy(cls, tmy_metadata, tmy_data=None, **kwargs): return new_object + @classmethod + def from_epw(cls, epw_metadata, epw_data=None, **kwargs): + """ + coded by Tanguy + + Create a Location object based on a metadata + dictionary from epw2 or epw3 data readers. + + Parameters + ---------- + epw_metadata : dict + Returned from epw.read_epw + epw_data : None or DataFrame, default None + Optionally attach the epw data to this object. + + Returns + ------- + Location object (or the child class of Location that you + called this method from). + """ + # not complete, but hopefully you get the idea. + # might need code to handle the difference between all data type, + # even formatted in epw + + latitude = epw_metadata['latitude'] + longitude = epw_metadata['longitude'] + + name = epw_metadata['city'] + + tz = epw_metadata['TZ'] + altitude = epw_metadata['altitude'] + + new_object = cls(latitude, longitude, tz=tz, altitude=altitude, + name=name, **kwargs) + + return new_object + def get_solarposition(self, times, pressure=None, temperature=12, **kwargs): """ diff --git a/pvlib/singlediode.py b/pvlib/singlediode.py index 54d2699b6a..2e98f8ad0e 100644 --- a/pvlib/singlediode.py +++ b/pvlib/singlediode.py @@ -30,7 +30,8 @@ def brentq(*a, **kw): VOLTAGE_BUILTIN = 0.9 # [V] -def getparams_from_specs_desoto(I_sc, V_oc, I_mp, V_mp, alpha_sc, beta_oc, N_s=60): +def getparams_from_specs_desoto(I_sc, V_oc, I_mp, V_mp, alpha_sc, beta_oc, + N_s=60): """ Calculates the five parameters for the single diode equation at standard irradiance and standard cell temperature using the De Soto et al. diff --git a/pvlib/test/test_singlediode.py b/pvlib/test/test_singlediode.py index 1e529ed00c..13b7aac346 100644 --- a/pvlib/test/test_singlediode.py +++ b/pvlib/test/test_singlediode.py @@ -11,7 +11,7 @@ POA = 888 TCELL = 55 -#CECMOD = pvsystem.retrieve_sam('cecmod') +CECMOD = pvsystem.retrieve_sam('cecmod') # run this test solely: From 0cfe13f92618536cdb6db67a86719c4a21f102af Mon Sep 17 00:00:00 2001 From: tylunel Date: Tue, 10 Sep 2019 09:55:27 -0400 Subject: [PATCH 07/46] - function '_parse_raw_sam_df' modified. The parser engine is now defined on 'python'. If not the pd.read_csv cannot work with me. --- pvlib/pvsystem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index f040b5f519..c51044e071 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -1791,7 +1791,7 @@ def retrieve_sam(name=None, path=None): def _parse_raw_sam_df(csvdata): - df = pd.read_csv(csvdata, index_col=0, skiprows=[1, 2]) + df = pd.read_csv(csvdata, index_col=0, skiprows=[1, 2], engine='python') colnames = df.columns.values.tolist() parsedcolnames = [] for cn in colnames: From 0c278298baaaa31b31754b369271c0e1456a0d76 Mon Sep 17 00:00:00 2001 From: tylunel Date: Wed, 11 Sep 2019 08:16:16 -0400 Subject: [PATCH 08/46] - ModelChain attribute 'diode_params' transformed from tuple containing pd.Series to DataFrame. Makes the use of diode_params easier for further calculations. --- pvlib/modelchain.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 1455a57ad3..b9e4fc4ea4 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -432,9 +432,11 @@ def desoto(self): self.system.calcparams_desoto(self.effective_irradiance, self.temps['temp_cell'])) - self.diode_params = (photocurrent, saturation_current, - resistance_series, - resistance_shunt, nNsVth) + self.diode_params = pd.DataFrame({'I_L': photocurrent, + 'I_o': saturation_current, + 'R_s': resistance_series, + 'R_sh': resistance_shunt, + 'nNsVth': nNsVth}) self.dc = self.system.singlediode( photocurrent, saturation_current, resistance_series, @@ -450,9 +452,11 @@ def cec(self): self.system.calcparams_cec(self.effective_irradiance, self.temps['temp_cell'])) - self.diode_params = (photocurrent, saturation_current, - resistance_series, - resistance_shunt, nNsVth) + self.diode_params = pd.DataFrame({'I_L': photocurrent, + 'I_o': saturation_current, + 'R_s': resistance_series, + 'R_sh': resistance_shunt, + 'nNsVth': nNsVth}) self.dc = self.system.singlediode( photocurrent, saturation_current, resistance_series, @@ -468,9 +472,11 @@ def pvsyst(self): self.system.calcparams_pvsyst(self.effective_irradiance, self.temps['temp_cell'])) - self.diode_params = (photocurrent, saturation_current, - resistance_series, - resistance_shunt, nNsVth) + self.diode_params = pd.DataFrame({'I_L': photocurrent, + 'I_o': saturation_current, + 'R_s': resistance_series, + 'R_sh': resistance_shunt, + 'nNsVth': nNsVth}) self.dc = self.system.singlediode( photocurrent, saturation_current, resistance_series, From d8653cd6e16c528087d5f6e4a5896b2887903e5c Mon Sep 17 00:00:00 2001 From: tylunel Date: Thu, 12 Sep 2019 13:17:49 -0400 Subject: [PATCH 09/46] - singlediode.get_params_from_specs_desoto() output changed. 'a_ref' is changed by 'nNsVth_ref' --- pvlib/singlediode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pvlib/singlediode.py b/pvlib/singlediode.py index 2e98f8ad0e..96ed30690b 100644 --- a/pvlib/singlediode.py +++ b/pvlib/singlediode.py @@ -89,7 +89,7 @@ def getparams_from_specs_desoto(I_sc, V_oc, I_mp, V_mp, alpha_sc, beta_oc, resistance_shunt 'R_sh_ref': numeric Shunt resistance in ohms at std conditions. - 'a_ref' (= nNsVth): numeric + 'nNsVth_ref' : numeric Modified ideality factor at std conditions. The product of the usual diode ideality factor (n, unitless), number of cells in series (Ns), and cell thermal voltage at @@ -183,7 +183,7 @@ def pv_fct(params, specs): # results res = {'I_L_ref': sol[0], 'I_o_ref': sol[1], - 'a_ref': sol[2], + 'nNsVth_ref': sol[2], 'R_sh_ref': sol[3], 'R_s': sol[4], 'alpha_sc': alpha_sc From bbb458085fbbd1fc9deb11820b8009f07caf1edf Mon Sep 17 00:00:00 2001 From: Lunel Date: Mon, 23 Sep 2019 18:26:12 -0400 Subject: [PATCH 10/46] read_epw changed. A line has been added to convert the precipitable water from mm to cm, in order to be compatible with other functions of pvlib --- pvlib/iotools/epw.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pvlib/iotools/epw.py b/pvlib/iotools/epw.py index 4a714d0f2d..ac5d86f5c0 100644 --- a/pvlib/iotools/epw.py +++ b/pvlib/iotools/epw.py @@ -99,7 +99,7 @@ def read_epw(filename, coerce_year=None): ceiling_height Height of cloud base above local terrain (7777=unlimited), meter present_weather_observation Indicator for remaining fields: If 0, then the observed weather codes are taken from the following field. If 9, then missing weather is assumed. present_weather_codes Present weather code, see [1], chapter 2.9.1.28 - precipitable_water Total precipitable water contained in a column of unit cross section from earth to top of atmosphere, cm + precipitable_water Total precipitable water contained in a column of unit cross section from earth to top of atmosphere, mm aerosol_optical_depth The broadband aerosol optical depth per unit of air mass due to extinction by aerosol component of atmosphere, unitless snow_depth Snow depth in centimeters on the day indicated, (999 = missing data) days_since_last_snowfall Number of days since last snowfall (maximum value of 88, where 88 = 88 or greater days; 99 = missing data) @@ -160,6 +160,10 @@ def read_epw(filename, coerce_year=None): if coerce_year is not None: data["year"] = coerce_year + # Convert precipitable water data from mm to cm + # https://energyplus.net/AuxiliaryPrograms.pdf -> p.14 + data['precipitable_water'] = data['precipitable_water']/10 + # create index that supplies correct date and time zone information dts = data[['month', 'day']].astype(str).apply(lambda x: x.str.zfill(2)) hrs = (data['hour'] - 1).astype(str).str.zfill(2) From 6b8fadf75298bc45df6bcbd90a1b7c33d173dbeb Mon Sep 17 00:00:00 2001 From: Lunel Date: Mon, 30 Sep 2019 17:32:16 -0400 Subject: [PATCH 11/46] - read_epw changed. If condition added to make the conversion only in the case of TMY3 --- pvlib/iotools/epw.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pvlib/iotools/epw.py b/pvlib/iotools/epw.py index ac5d86f5c0..7bfc80e363 100644 --- a/pvlib/iotools/epw.py +++ b/pvlib/iotools/epw.py @@ -160,9 +160,11 @@ def read_epw(filename, coerce_year=None): if coerce_year is not None: data["year"] = coerce_year - # Convert precipitable water data from mm to cm - # https://energyplus.net/AuxiliaryPrograms.pdf -> p.14 - data['precipitable_water'] = data['precipitable_water']/10 +# # Convert precipitable water data from mm to cm if data comes from TMY3 +# # https://energyplus.net/AuxiliaryPrograms.pdf -> p.14 +# if filename.split(".")[-2].lower() is 'tmy3': + if 'tmy3' in filename.lower(): + data['precipitable_water'] = data['precipitable_water']/10 # create index that supplies correct date and time zone information dts = data[['month', 'day']].astype(str).apply(lambda x: x.str.zfill(2)) From 47e1be49fc95d2c45a6307fb59f24dbcbbba54d4 Mon Sep 17 00:00:00 2001 From: Lunel Date: Wed, 2 Oct 2019 15:45:00 -0400 Subject: [PATCH 12/46] - argument diode_params changed from tuple to pd.DataFrame --- pvlib/modelchain.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 6aad00c1dd..63b0221eb5 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -8,6 +8,7 @@ from functools import partial import warnings +import pandas as pd from pvlib import (atmosphere, clearsky, pvsystem, solarposition, temperature, tools) @@ -451,9 +452,11 @@ def _singlediode(self, calcparams_model_function): calcparams_model_function(self.effective_irradiance, self.cell_temperature)) - self.diode_params = (photocurrent, saturation_current, - resistance_series, - resistance_shunt, nNsVth) + self.diode_params = pd.DataFrame({'I_L': photocurrent, + 'I_o': saturation_current, + 'R_s': resistance_series, + 'R_sh': resistance_shunt, + 'nNsVth': nNsVth}) self.dc = self.system.singlediode( photocurrent, saturation_current, resistance_series, From c1b62c495f30f3faab1911ad1ed2c7a6fb7c289f Mon Sep 17 00:00:00 2001 From: Lunel Date: Wed, 2 Oct 2019 17:15:36 -0400 Subject: [PATCH 13/46] - get_params_from_specs_desoto removed from singlediode.py --- pvlib/singlediode.py | 161 ------------------------------------------- 1 file changed, 161 deletions(-) diff --git a/pvlib/singlediode.py b/pvlib/singlediode.py index 951dcc3351..219ce1aa4f 100644 --- a/pvlib/singlediode.py +++ b/pvlib/singlediode.py @@ -30,167 +30,6 @@ def brentq(*a, **kw): VOLTAGE_BUILTIN = 0.9 # [V] -def getparams_from_specs_desoto(I_sc, V_oc, I_mp, V_mp, alpha_sc, beta_oc, - N_s=60): - """ - Calculates the five parameters for the single diode equation at - standard irradiance and standard cell temperature using the De Soto et al. - procedure described in [1]. This procedure has the advantage of using - common specifications given by manufacturers in the datasheets of - PV modules. - The six values returned by getparams_desoto - can be used by calcparams_desoto to calculate the values at different - irradiance and cell temperature. - - Parameters - ---------- - I_sc : numeric - Short-circuit current at std conditions in A. - - V_oc : numeric - Open-circuit voltage at std conditions in V. - - I_mp : numeric - Module current at the maximum-power point at std conditions in A. - - V_mp : numeric - Module voltage at the maximum-power point at std conditions in V. - - alpha_sc : numeric - The short-circuit current (I_sc) temperature coefficient of the - module in units of %/K. It is converted in A/K for the computing - process. - - N_s : numeric - Number of cell in the module. - Optional input, but helps to insure the convergence of the computing. - - beta_oc : numeric - The open-circuit voltage (V_oc) temperature coefficient of the - module in units of %/K. It is converted in V/K for the computing - process. - Optional input, but makes faster the computing. - - Returns - ------- - Dictionnary with the following elements: - - photocurrent 'I_L_ref': numeric - Light-generated current in amperes at std conditions. - - saturation_current 'I_o_ref': numeric - Diode saturation curent in amperes at std conditions - - resistance_series 'R_s': numeric - Series resistance in ohms. Note that '_ref' is not mentionned - in the name because this resistance is not sensible to the - conditions of test. - - resistance_shunt 'R_sh_ref': numeric - Shunt resistance in ohms at std conditions. - - 'nNsVth_ref' : numeric - Modified ideality factor at std conditions. - The product of the usual diode ideality factor (n, unitless), - number of cells in series (Ns), and cell thermal voltage at - specified effective irradiance and cell temperature. - - 'alpha_sc': numeric - Caution!: Different from the input because of the unit. - The short-circuit current (I_sc) temperature coefficient of the - module in units of A/K. - - References - ---------- - [1] W. De Soto et al., "Improvement and validation of a model for - photovoltaic array performance", Solar Energy, vol 80, pp. 78-88, - 2006. - - [2] John A Duffie ,William A Beckman, "Solar Engineering of Thermal - Processes", Wiley, 2013 - """ - # Constants - k = 1.381e-23 # Boltzmann constant, in J/K, or 8.617e-5 eV/K - q = 1.602e-19 # electron charge in J/V, or 1 eV - Tref = 25.0 + 273.15 # in K - C = 0.0002677 # valid for silicon - Eg = 1.796e-19 # in J, valid for silicon - - # Conversion from %/K to A/K & V/K - alpha_sc = alpha_sc*I_sc/100 - beta_oc = beta_oc*V_oc/100 - - def pv_fct(params, specs): - """Returns the system of equations used for computing the - single-diode 5 parameters. - To avoid the confusion in names with variables of container - function the '_' of the variables were removed. - """ - # six input known variables - Isc = specs[0] - Voc = specs[1] - Imp = specs[2] - Vmp = specs[3] - betaoc = specs[4] - alphasc = specs[5] - # five parameters vector to find - IL = params[0] - Io = params[1] - a = params[2] - Rsh = params[3] - Rs = params[4] - # five equation vector - y = [0, 0, 0, 0, 0] - - # 1st equation - short-circuit - eq(3) in [1] - y[0] = Isc - IL + Io*(np.exp(Isc*Rs/a) - 1.0) + Isc*Rs/Rsh - # 2nd equation - open-circuit Tref - eq(4) in [1] - y[1] = -IL + Io*(np.exp(Voc/a) - 1.0) + Voc/Rsh - # 3rd equation - Imp & Vmp - eq(5) in [1] - y[2] = Imp - IL + Io*(np.exp((Vmp+Imp*Rs)/a) - 1.0) + \ - (Vmp+Imp*Rs)/Rsh - # 4th equation - Pmp derivated=0 - - # caution: eq(6) in [1] is incorrect, take eq23.2.6 in [2] - y[3] = Imp - Vmp * ((Io/a)*np.exp((Vmp+Imp*Rs)/a) + 1.0/Rsh) / \ - (1.0 + (Io*Rs/a)*np.exp((Vmp+Imp*Rs)/a) + Rs/Rsh) - # 5th equation - open-circuit T2 - eq (4) at temperature T2 in [1] - T2 = Tref + 2 - Voc2 = (T2 - Tref)*betaoc + Voc # eq (7) in [1] - a2 = a*T2/Tref # eq (8) in [1] - IL2 = IL + alphasc*(T2-Tref) # eq (11) in [1] - Eg2 = Eg*(1-C*(T2-Tref)) # eq (10) in [1] - Io2 = Io * (T2/Tref)**3 * np.exp(1/k * (Eg/Tref-Eg2/T2)) # eq (9) - y[4] = -IL2 + Io2*(np.exp(Voc2/a2) - 1.0) + Voc2/Rsh # eq (4) at T2 - - return y - - # initial guesses of variables for computing convergence: - # Values are taken [2], p753 - Rsh_i = 100.0 - a_i = 1.5*k*Tref*N_s/q - IL_i = I_sc - Io_i = I_sc * np.exp(-V_oc/a_i) - Rs_i = (a_i*np.log((IL_i-I_mp)/Io_i+1) - V_mp)/I_mp - # params_i : initial values vector - params_i = np.array([IL_i, Io_i, a_i, Rsh_i, Rs_i]) - - # specs of module - specs = np.array([I_sc, V_oc, I_mp, V_mp, beta_oc, alpha_sc]) - - # computing - sol = root(pv_fct, x0=params_i, args=specs, method='lm').x - - # results - res = {'I_L_ref': sol[0], - 'I_o_ref': sol[1], - 'nNsVth_ref': sol[2], - 'R_sh_ref': sol[3], - 'R_s': sol[4], - 'alpha_sc': alpha_sc - } - return res - - def estimate_voc(photocurrent, saturation_current, nNsVth): """ Rough estimate of open circuit voltage useful for bounding searches for From 4e62d1a06a27600abb3800cc0f7bf0638fa085a9 Mon Sep 17 00:00:00 2001 From: Lunel Date: Wed, 2 Oct 2019 17:19:49 -0400 Subject: [PATCH 14/46] - function fit_sdm_desoto added. Still need to be formatted --- pvlib/ivtools.py | 159 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index 353a0b8a9a..1f4ae261da 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -262,6 +262,165 @@ def fit_sde_sandia(voltage, current, v_oc=None, i_sc=None, v_mp_i_mp=None, v_oc) +def fit_sdm_desoto(I_sc, V_oc, I_mp, V_mp, alpha_sc, beta_oc, + N_s=60): + """ + Calculates the five parameters for the single diode equation at + standard irradiance and standard cell temperature using the De Soto et al. + procedure described in [1]. This procedure has the advantage of using + common specifications given by manufacturers in the datasheets of + PV modules. + The six values returned by getparams_desoto + can be used by calcparams_desoto to calculate the values at different + irradiance and cell temperature. + + Parameters + ---------- + I_sc : numeric + Short-circuit current at std conditions in A. + + V_oc : numeric + Open-circuit voltage at std conditions in V. + + I_mp : numeric + Module current at the maximum-power point at std conditions in A. + + V_mp : numeric + Module voltage at the maximum-power point at std conditions in V. + + alpha_sc : numeric + The short-circuit current (I_sc) temperature coefficient of the + module in units of %/K. It is converted in A/K for the computing + process. + + N_s : numeric + Number of cell in the module. + Optional input, but helps to insure the convergence of the computing. + + beta_oc : numeric + The open-circuit voltage (V_oc) temperature coefficient of the + module in units of %/K. It is converted in V/K for the computing + process. + Optional input, but makes faster the computing. + + Returns + ------- + Dictionnary with the following elements: + + photocurrent 'I_L_ref': numeric + Light-generated current in amperes at std conditions. + + saturation_current 'I_o_ref': numeric + Diode saturation curent in amperes at std conditions + + resistance_series 'R_s': numeric + Series resistance in ohms. Note that '_ref' is not mentionned + in the name because this resistance is not sensible to the + conditions of test. + + resistance_shunt 'R_sh_ref': numeric + Shunt resistance in ohms at std conditions. + + 'nNsVth_ref' : numeric + Modified ideality factor at std conditions. + The product of the usual diode ideality factor (n, unitless), + number of cells in series (Ns), and cell thermal voltage at + specified effective irradiance and cell temperature. + + 'alpha_sc': numeric + Caution!: Different from the input because of the unit. + The short-circuit current (I_sc) temperature coefficient of the + module in units of A/K. + + References + ---------- + [1] W. De Soto et al., "Improvement and validation of a model for + photovoltaic array performance", Solar Energy, vol 80, pp. 78-88, + 2006. + + [2] John A Duffie ,William A Beckman, "Solar Engineering of Thermal + Processes", Wiley, 2013 + """ + # Constants + k = 1.381e-23 # Boltzmann constant, in J/K, or 8.617e-5 eV/K + q = 1.602e-19 # electron charge in J/V, or 1 eV + Tref = 25.0 + 273.15 # in K + C = 0.0002677 # valid for silicon + Eg = 1.796e-19 # in J, valid for silicon + + # Conversion from %/K to A/K & V/K + alpha_sc = alpha_sc*I_sc/100 + beta_oc = beta_oc*V_oc/100 + + def pv_fct(params, specs): + """Returns the system of equations used for computing the + single-diode 5 parameters. + To avoid the confusion in names with variables of container + function the '_' of the variables were removed. + """ + # six input known variables + Isc = specs[0] + Voc = specs[1] + Imp = specs[2] + Vmp = specs[3] + betaoc = specs[4] + alphasc = specs[5] + # five parameters vector to find + IL = params[0] + Io = params[1] + a = params[2] + Rsh = params[3] + Rs = params[4] + # five equation vector + y = [0, 0, 0, 0, 0] + + # 1st equation - short-circuit - eq(3) in [1] + y[0] = Isc - IL + Io*(np.exp(Isc*Rs/a) - 1.0) + Isc*Rs/Rsh + # 2nd equation - open-circuit Tref - eq(4) in [1] + y[1] = -IL + Io*(np.exp(Voc/a) - 1.0) + Voc/Rsh + # 3rd equation - Imp & Vmp - eq(5) in [1] + y[2] = Imp - IL + Io*(np.exp((Vmp+Imp*Rs)/a) - 1.0) + \ + (Vmp+Imp*Rs)/Rsh + # 4th equation - Pmp derivated=0 - + # caution: eq(6) in [1] is incorrect, take eq23.2.6 in [2] + y[3] = Imp - Vmp * ((Io/a)*np.exp((Vmp+Imp*Rs)/a) + 1.0/Rsh) / \ + (1.0 + (Io*Rs/a)*np.exp((Vmp+Imp*Rs)/a) + Rs/Rsh) + # 5th equation - open-circuit T2 - eq (4) at temperature T2 in [1] + T2 = Tref + 2 + Voc2 = (T2 - Tref)*betaoc + Voc # eq (7) in [1] + a2 = a*T2/Tref # eq (8) in [1] + IL2 = IL + alphasc*(T2-Tref) # eq (11) in [1] + Eg2 = Eg*(1-C*(T2-Tref)) # eq (10) in [1] + Io2 = Io * (T2/Tref)**3 * np.exp(1/k * (Eg/Tref-Eg2/T2)) # eq (9) + y[4] = -IL2 + Io2*(np.exp(Voc2/a2) - 1.0) + Voc2/Rsh # eq (4) at T2 + + return y + + # initial guesses of variables for computing convergence: + # Values are taken [2], p753 + Rsh_i = 100.0 + a_i = 1.5*k*Tref*N_s/q + IL_i = I_sc + Io_i = I_sc * np.exp(-V_oc/a_i) + Rs_i = (a_i*np.log((IL_i-I_mp)/Io_i+1) - V_mp)/I_mp + # params_i : initial values vector + params_i = np.array([IL_i, Io_i, a_i, Rsh_i, Rs_i]) + + # specs of module + specs = np.array([I_sc, V_oc, I_mp, V_mp, beta_oc, alpha_sc]) + + # computing + sol = root(pv_fct, x0=params_i, args=specs, method='lm').x + + # results + res = {'I_L_ref': sol[0], + 'I_o_ref': sol[1], + 'nNsVth_ref': sol[2], + 'R_sh_ref': sol[3], + 'R_s': sol[4], + 'alpha_sc': alpha_sc + } + return res def _find_mp(voltage, current): """ Finds voltage and current at maximum power point. From 747ed4805ba0b8abec523117dbb29511b35fc366 Mon Sep 17 00:00:00 2001 From: Lunel Date: Wed, 2 Oct 2019 17:22:18 -0400 Subject: [PATCH 15/46] - change on type of self.diode_params removed. Go check on branch diode_params_in_df for seeing it --- pvlib/modelchain.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 63b0221eb5..eaf9230bf7 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -452,11 +452,8 @@ def _singlediode(self, calcparams_model_function): calcparams_model_function(self.effective_irradiance, self.cell_temperature)) - self.diode_params = pd.DataFrame({'I_L': photocurrent, - 'I_o': saturation_current, - 'R_s': resistance_series, - 'R_sh': resistance_shunt, - 'nNsVth': nNsVth}) + self.diode_params = (photocurrent, saturation_current, + resistance_series, resistance_shunt, nNsVth) self.dc = self.system.singlediode( photocurrent, saturation_current, resistance_series, From 9ea2ed8aa27258cea4d472632f55ce2bf50a8e5b Mon Sep 17 00:00:00 2001 From: Lunel Date: Wed, 2 Oct 2019 18:35:36 -0400 Subject: [PATCH 16/46] - Function 'fit_sdm_desoto' cleaned and variables names named as in 'fit_sdm_cec_sam' --- pvlib/ivtools.py | 111 +++++++++++++++++++++++++++-------------------- 1 file changed, 63 insertions(+), 48 deletions(-) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index 1f4ae261da..95c633ac42 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -7,6 +7,12 @@ import numpy as np +try: + from scipy.optimize import root +except ImportError: + raise ImportError( + "'scipy.optimize.root' couldn't be imported. Is SciPy installed?") + def fit_sdm_cec_sam(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, gamma_pmp, cells_in_series, temp_ref=25): @@ -262,8 +268,8 @@ def fit_sde_sandia(voltage, current, v_oc=None, i_sc=None, v_mp_i_mp=None, v_oc) -def fit_sdm_desoto(I_sc, V_oc, I_mp, V_mp, alpha_sc, beta_oc, - N_s=60): +def fit_sdm_desoto(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, + cells_in_series=60, temp_ref=25): """ Calculates the five parameters for the single diode equation at standard irradiance and standard cell temperature using the De Soto et al. @@ -276,60 +282,59 @@ def fit_sdm_desoto(I_sc, V_oc, I_mp, V_mp, alpha_sc, beta_oc, Parameters ---------- - I_sc : numeric - Short-circuit current at std conditions in A. - - V_oc : numeric - Open-circuit voltage at std conditions in V. - - I_mp : numeric - Module current at the maximum-power point at std conditions in A. - - V_mp : numeric + celltype : str, case insensitive + Value is one of 'monosi', 'multisi', 'polysi' or'gaas'. + Others like 'cis', 'cigs', 'cdte', 'amorphous' are not implemented yet + v_mp : numeric Module voltage at the maximum-power point at std conditions in V. - + i_mp : numeric + Module current at the maximum-power point at std conditions in A. + v_oc : numeric + Open-circuit voltage at std conditions in V. + i_sc : numeric + Short-circuit current at std conditions in A. alpha_sc : numeric - The short-circuit current (I_sc) temperature coefficient of the + The short-circuit current (i_sc) temperature coefficient of the module in units of %/K. It is converted in A/K for the computing process. - - N_s : numeric + beta_voc : numeric + The open-circuit voltage (v_oc) temperature coefficient of the + module in units of %/K. It is converted in V/K for the computing + process. + cells_in_series : numeric, default 60 Number of cell in the module. Optional input, but helps to insure the convergence of the computing. + temp_ref : numeric, default 25 + Reference temperature condition [C] - beta_oc : numeric - The open-circuit voltage (V_oc) temperature coefficient of the - module in units of %/K. It is converted in V/K for the computing - process. - Optional input, but makes faster the computing. Returns ------- Dictionnary with the following elements: - photocurrent 'I_L_ref': numeric + * 'I_L_ref': numeric Light-generated current in amperes at std conditions. - saturation_current 'I_o_ref': numeric + * 'I_o_ref': numeric Diode saturation curent in amperes at std conditions - resistance_series 'R_s': numeric + * 'R_s': numeric Series resistance in ohms. Note that '_ref' is not mentionned in the name because this resistance is not sensible to the conditions of test. - resistance_shunt 'R_sh_ref': numeric + * 'R_sh_ref': numeric Shunt resistance in ohms at std conditions. - 'nNsVth_ref' : numeric + * 'nNsVth_ref' : numeric Modified ideality factor at std conditions. The product of the usual diode ideality factor (n, unitless), number of cells in series (Ns), and cell thermal voltage at specified effective irradiance and cell temperature. - 'alpha_sc': numeric + * 'alpha_sc': numeric Caution!: Different from the input because of the unit. - The short-circuit current (I_sc) temperature coefficient of the + The short-circuit current (i_sc) temperature coefficient of the module in units of A/K. References @@ -344,13 +349,23 @@ def fit_sdm_desoto(I_sc, V_oc, I_mp, V_mp, alpha_sc, beta_oc, # Constants k = 1.381e-23 # Boltzmann constant, in J/K, or 8.617e-5 eV/K q = 1.602e-19 # electron charge in J/V, or 1 eV - Tref = 25.0 + 273.15 # in K - C = 0.0002677 # valid for silicon - Eg = 1.796e-19 # in J, valid for silicon + + Tref = temp_ref + 273.15 # in K + + if celltype.lower() in ['monosi', 'polysi', 'multisi']: + C = 0.0002677 # valid for silicon + Eg = 1.796e-19 # in J, valid for silicon + elif celltype.lower() in ['gaas']: + C = 0.0003174 # valid for gallium arsenide + Eg = 2.163e-19 # in J, valid for gallium arsenide + elif celltype.lower() in ['cis', 'cigs', 'cdte', 'amorphous']: + raise NotImplementedError + else: + raise ValueError('Unknown cell type.') # Conversion from %/K to A/K & V/K - alpha_sc = alpha_sc*I_sc/100 - beta_oc = beta_oc*V_oc/100 + alpha_sc = alpha_sc*i_sc/100 + beta_voc = beta_voc*v_oc/100 def pv_fct(params, specs): """Returns the system of equations used for computing the @@ -382,7 +397,7 @@ def pv_fct(params, specs): y[2] = Imp - IL + Io*(np.exp((Vmp+Imp*Rs)/a) - 1.0) + \ (Vmp+Imp*Rs)/Rsh # 4th equation - Pmp derivated=0 - - # caution: eq(6) in [1] is incorrect, take eq23.2.6 in [2] + # caution: eq(6) in [1] seems to be incorrect, take eq23.2.6 in [2] y[3] = Imp - Vmp * ((Io/a)*np.exp((Vmp+Imp*Rs)/a) + 1.0/Rsh) / \ (1.0 + (Io*Rs/a)*np.exp((Vmp+Imp*Rs)/a) + Rs/Rsh) # 5th equation - open-circuit T2 - eq (4) at temperature T2 in [1] @@ -397,30 +412,30 @@ def pv_fct(params, specs): return y # initial guesses of variables for computing convergence: - # Values are taken [2], p753 + # Values are taken from [2], p753 Rsh_i = 100.0 - a_i = 1.5*k*Tref*N_s/q - IL_i = I_sc - Io_i = I_sc * np.exp(-V_oc/a_i) - Rs_i = (a_i*np.log((IL_i-I_mp)/Io_i+1) - V_mp)/I_mp + a_i = 1.5*k*Tref*cells_in_series/q + IL_i = i_sc + Io_i = i_sc * np.exp(-v_oc/a_i) + Rs_i = (a_i*np.log((IL_i-i_mp)/Io_i+1) - v_mp)/i_mp # params_i : initial values vector params_i = np.array([IL_i, Io_i, a_i, Rsh_i, Rs_i]) # specs of module - specs = np.array([I_sc, V_oc, I_mp, V_mp, beta_oc, alpha_sc]) + specs = np.array([i_sc, v_oc, i_mp, v_mp, beta_voc, alpha_sc]) # computing sol = root(pv_fct, x0=params_i, args=specs, method='lm').x # results - res = {'I_L_ref': sol[0], - 'I_o_ref': sol[1], - 'nNsVth_ref': sol[2], - 'R_sh_ref': sol[3], - 'R_s': sol[4], - 'alpha_sc': alpha_sc - } - return res + return {'I_L_ref': sol[0], + 'I_o_ref': sol[1], + 'nNsVth_ref': sol[2], + 'R_sh_ref': sol[3], + 'R_s': sol[4], + 'alpha_sc': alpha_sc} + + def _find_mp(voltage, current): """ Finds voltage and current at maximum power point. From bd39dbd0d3ef9e7de57aa9e0b453adbb03e2f4e6 Mon Sep 17 00:00:00 2001 From: Lunel Date: Wed, 2 Oct 2019 19:17:05 -0400 Subject: [PATCH 17/46] - all changes made on other files than ivtools.py removed (cleaning for comparing before PR) --- .gitignore | 1 - pvlib/iotools/epw.py | 8 +------- pvlib/location.py | 37 ---------------------------------- pvlib/modelchain.py | 4 ++-- pvlib/singlediode.py | 4 ++-- pvlib/test/test_singlediode.py | 22 -------------------- 6 files changed, 5 insertions(+), 71 deletions(-) diff --git a/.gitignore b/.gitignore index 8036240eac..ab0452c628 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,6 @@ __pycache__/ # emacs temp files *~ #*# -pvlib/temp.py # C extensions *.so diff --git a/pvlib/iotools/epw.py b/pvlib/iotools/epw.py index 7bfc80e363..4a714d0f2d 100644 --- a/pvlib/iotools/epw.py +++ b/pvlib/iotools/epw.py @@ -99,7 +99,7 @@ def read_epw(filename, coerce_year=None): ceiling_height Height of cloud base above local terrain (7777=unlimited), meter present_weather_observation Indicator for remaining fields: If 0, then the observed weather codes are taken from the following field. If 9, then missing weather is assumed. present_weather_codes Present weather code, see [1], chapter 2.9.1.28 - precipitable_water Total precipitable water contained in a column of unit cross section from earth to top of atmosphere, mm + precipitable_water Total precipitable water contained in a column of unit cross section from earth to top of atmosphere, cm aerosol_optical_depth The broadband aerosol optical depth per unit of air mass due to extinction by aerosol component of atmosphere, unitless snow_depth Snow depth in centimeters on the day indicated, (999 = missing data) days_since_last_snowfall Number of days since last snowfall (maximum value of 88, where 88 = 88 or greater days; 99 = missing data) @@ -160,12 +160,6 @@ def read_epw(filename, coerce_year=None): if coerce_year is not None: data["year"] = coerce_year -# # Convert precipitable water data from mm to cm if data comes from TMY3 -# # https://energyplus.net/AuxiliaryPrograms.pdf -> p.14 -# if filename.split(".")[-2].lower() is 'tmy3': - if 'tmy3' in filename.lower(): - data['precipitable_water'] = data['precipitable_water']/10 - # create index that supplies correct date and time zone information dts = data[['month', 'day']].astype(str).apply(lambda x: x.str.zfill(2)) hrs = (data['hour'] - 1).astype(str).str.zfill(2) diff --git a/pvlib/location.py b/pvlib/location.py index 7cad58fe09..68b2062907 100644 --- a/pvlib/location.py +++ b/pvlib/location.py @@ -128,43 +128,6 @@ def from_tmy(cls, tmy_metadata, tmy_data=None, **kwargs): return new_object - @classmethod - def from_epw(cls, epw_metadata, epw_data=None, **kwargs): - """ - coded by Tanguy - - Create a Location object based on a metadata - dictionary from epw2 or epw3 data readers. - - Parameters - ---------- - epw_metadata : dict - Returned from epw.read_epw - epw_data : None or DataFrame, default None - Optionally attach the epw data to this object. - - Returns - ------- - Location object (or the child class of Location that you - called this method from). - """ - # not complete, but hopefully you get the idea. - # might need code to handle the difference between all data type, - # even formatted in epw - - latitude = epw_metadata['latitude'] - longitude = epw_metadata['longitude'] - - name = epw_metadata['city'] - - tz = epw_metadata['TZ'] - altitude = epw_metadata['altitude'] - - new_object = cls(latitude, longitude, tz=tz, altitude=altitude, - name=name, **kwargs) - - return new_object - def get_solarposition(self, times, pressure=None, temperature=12, **kwargs): """ diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index eaf9230bf7..6aad00c1dd 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -8,7 +8,6 @@ from functools import partial import warnings -import pandas as pd from pvlib import (atmosphere, clearsky, pvsystem, solarposition, temperature, tools) @@ -453,7 +452,8 @@ def _singlediode(self, calcparams_model_function): self.cell_temperature)) self.diode_params = (photocurrent, saturation_current, - resistance_series, resistance_shunt, nNsVth) + resistance_series, + resistance_shunt, nNsVth) self.dc = self.system.singlediode( photocurrent, saturation_current, resistance_series, diff --git a/pvlib/singlediode.py b/pvlib/singlediode.py index 219ce1aa4f..1f616a245f 100644 --- a/pvlib/singlediode.py +++ b/pvlib/singlediode.py @@ -12,11 +12,11 @@ # ImportError when 'brentq' method is specified for those methods. try: from scipy.optimize import brentq - from scipy.optimize import root + except ImportError: def brentq(*a, **kw): raise ImportError( - "brentq or root couldn't be imported. Is SciPy installed?") + "brentq couldn't be imported. Is SciPy installed?") # FIXME: change this to newton when scipy-1.2 is released try: diff --git a/pvlib/test/test_singlediode.py b/pvlib/test/test_singlediode.py index 1c21436ec3..2d5074c884 100644 --- a/pvlib/test/test_singlediode.py +++ b/pvlib/test/test_singlediode.py @@ -3,7 +3,6 @@ """ import numpy as np -import pvlib from pvlib import pvsystem from pvlib.singlediode import (bishop88_mpp, estimate_voc, VOLTAGE_BUILTIN, bishop88, bishop88_i_from_v, bishop88_v_from_i) @@ -14,27 +13,6 @@ TCELL = 55 -# run this test solely: -# pytest.main(['test_singlediode.py::test_getparams_from_specs_desoto']) -def test_getparams_from_specs_desoto(): - """test singlediode.getparams_from_specs with specs of the - Sunpower SPR-E20-327 module. - """ - params = pvlib.singlediode.getparams_from_specs_desoto( - I_sc=6.46, V_oc=64.9, I_mp=5.98, V_mp=54.7, alpha_sc=0.05, - beta_oc=-0.270, N_s=96 - ) - params_expected = {'I_L_ref': 6.47200, - 'I_o_ref': 6.05276e-12, - 'a_ref': 2.34662, - 'R_sh_ref': 248.450, - 'R_s': 0.461519, - 'alpha_sc': 0.003230} - np.testing.assert_allclose(list(params.values()), - list(params_expected.values()), - rtol=1e-4) - - @requires_scipy @pytest.mark.parametrize('method', ['brentq', 'newton']) def test_method_spr_e20_327(method, cec_module_spr_e20_327): From ae5c8becf2784bf6b595f397a1408d255de1c336 Mon Sep 17 00:00:00 2001 From: Lunel Date: Wed, 2 Oct 2019 19:19:08 -0400 Subject: [PATCH 18/46] - other differences cleaned --- pvlib/pvsystem.py | 2 +- pvlib/singlediode.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index bfb71192b1..bebe2f1263 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -1858,7 +1858,7 @@ def retrieve_sam(name=None, path=None): def _parse_raw_sam_df(csvdata): - df = pd.read_csv(csvdata, index_col=0, skiprows=[1, 2], engine='python') + df = pd.read_csv(csvdata, index_col=0, skiprows=[1, 2]) colnames = df.columns.values.tolist() parsedcolnames = [] for cn in colnames: diff --git a/pvlib/singlediode.py b/pvlib/singlediode.py index 1f616a245f..d466c778ce 100644 --- a/pvlib/singlediode.py +++ b/pvlib/singlediode.py @@ -12,7 +12,6 @@ # ImportError when 'brentq' method is specified for those methods. try: from scipy.optimize import brentq - except ImportError: def brentq(*a, **kw): raise ImportError( From 2d4f8f310c061a5379573be79e531edb3b46cd78 Mon Sep 17 00:00:00 2001 From: Lunel Date: Wed, 2 Oct 2019 19:50:10 -0400 Subject: [PATCH 19/46] - renaming of one variable and minor documentation modifications --- pvlib/ivtools.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index 95c633ac42..9cdf42594c 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -271,13 +271,12 @@ def fit_sde_sandia(voltage, current, v_oc=None, i_sc=None, v_mp_i_mp=None, def fit_sdm_desoto(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, cells_in_series=60, temp_ref=25): """ - Calculates the five parameters for the single diode equation at - standard irradiance and standard cell temperature using the De Soto et al. - procedure described in [1]. This procedure has the advantage of using - common specifications given by manufacturers in the datasheets of - PV modules. - The six values returned by getparams_desoto - can be used by calcparams_desoto to calculate the values at different + Calculates the five parameters for the single diode equation using + the De Soto et al. procedure described in [1]. This procedure has the + advantage of using common specifications given by manufacturers in the + datasheets of PV modules. + The six values returned by this function can be used by + pvsystem.calcparams_desoto to calculate the values at different irradiance and cell temperature. Parameters @@ -326,7 +325,7 @@ def fit_sdm_desoto(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, * 'R_sh_ref': numeric Shunt resistance in ohms at std conditions. - * 'nNsVth_ref' : numeric + * 'a_ref' : numeric Modified ideality factor at std conditions. The product of the usual diode ideality factor (n, unitless), number of cells in series (Ns), and cell thermal voltage at @@ -430,7 +429,7 @@ def pv_fct(params, specs): # results return {'I_L_ref': sol[0], 'I_o_ref': sol[1], - 'nNsVth_ref': sol[2], + 'a_ref': sol[2], 'R_sh_ref': sol[3], 'R_s': sol[4], 'alpha_sc': alpha_sc} From 485dae6e92d0a3422c07deba2574d8969c711476 Mon Sep 17 00:00:00 2001 From: Lunel Date: Fri, 4 Oct 2019 10:04:36 -0400 Subject: [PATCH 20/46] - Beginning of writting of test_fit_sdm_desoto. Coverage around 90-95% I think --- pvlib/test/test_ivtools.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pvlib/test/test_ivtools.py b/pvlib/test/test_ivtools.py index d4aca050c4..ff94265ed0 100644 --- a/pvlib/test/test_ivtools.py +++ b/pvlib/test/test_ivtools.py @@ -28,6 +28,14 @@ def get_cec_params_cansol_cs5p_220p(): 'R_sh_ref': 837.51, 'R_s': 1.004, 'Adjust': 2.3}} +@pytest.fixture +def get_test_specs_params(): + """Specifications of module Kyocera KU270-6MCA""" + return {'celltype': 'polysi', 'v_mp': 31.0, 'i_mp': 8.71, + 'v_oc': 38.3, 'i_sc': 9.43, 'alpha_sc': 0.06, 'beta_voc': -0.36, + 'cells_in_series': 60} + + @requires_scipy def test_fit_sde_sandia(get_test_iv_params, get_bad_iv_curves): test_params = get_test_iv_params @@ -102,6 +110,19 @@ def test_fit_sdm_cec_sam(get_cec_params_cansol_cs5p_220p): cells_in_series=1, temp_ref=25) +@requires_scipy +def test_fit_sdm_desoto(get_test_specs_params): + result = pvlib.ivtools.fit_sdm_desoto(**get_test_specs_params) + result_expected = {'I_L_ref': 9.452306515003592, + 'I_o_ref': 3.244967717785086e-10, + 'a_ref': 1.59170284124638, + 'R_sh_ref': 125.85822206469133, + 'R_s': 0.2977156014170881, + 'alpha_sc': 0.005658} + assert np.allclose(pd.Series(result), pd.Series(result_expected), + rtol=5e-2) + + @pytest.fixture def get_bad_iv_curves(): # v1, i1 produces a bad value for I0_voc From 77d88a2f5f274112a0b222b3fb9145b325022e10 Mon Sep 17 00:00:00 2001 From: Lunel Date: Fri, 4 Oct 2019 10:27:28 -0400 Subject: [PATCH 21/46] -minor format changes --- pvlib/ivtools.py | 5 +++-- pvlib/test/test_ivtools.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index 9cdf42594c..7a2181f7d2 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -269,7 +269,7 @@ def fit_sde_sandia(voltage, current, v_oc=None, i_sc=None, v_mp_i_mp=None, def fit_sdm_desoto(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, - cells_in_series=60, temp_ref=25): + cells_in_series=None, temp_ref=25): """ Calculates the five parameters for the single diode equation using the De Soto et al. procedure described in [1]. This procedure has the @@ -300,7 +300,7 @@ def fit_sdm_desoto(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, The open-circuit voltage (v_oc) temperature coefficient of the module in units of %/K. It is converted in V/K for the computing process. - cells_in_series : numeric, default 60 + cells_in_series : numeric, default None Number of cell in the module. Optional input, but helps to insure the convergence of the computing. temp_ref : numeric, default 25 @@ -354,6 +354,7 @@ def fit_sdm_desoto(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, if celltype.lower() in ['monosi', 'polysi', 'multisi']: C = 0.0002677 # valid for silicon Eg = 1.796e-19 # in J, valid for silicon + elif celltype.lower() in ['gaas']: C = 0.0003174 # valid for gallium arsenide Eg = 2.163e-19 # in J, valid for gallium arsenide diff --git a/pvlib/test/test_ivtools.py b/pvlib/test/test_ivtools.py index ff94265ed0..675cff7cce 100644 --- a/pvlib/test/test_ivtools.py +++ b/pvlib/test/test_ivtools.py @@ -112,7 +112,7 @@ def test_fit_sdm_cec_sam(get_cec_params_cansol_cs5p_220p): @requires_scipy def test_fit_sdm_desoto(get_test_specs_params): - result = pvlib.ivtools.fit_sdm_desoto(**get_test_specs_params) + result = ivtools.fit_sdm_desoto(**get_test_specs_params) result_expected = {'I_L_ref': 9.452306515003592, 'I_o_ref': 3.244967717785086e-10, 'a_ref': 1.59170284124638, From 6fcafc7dcfe19544df253f0a9e7ee7e933101619 Mon Sep 17 00:00:00 2001 From: Lunel Date: Fri, 4 Oct 2019 17:13:49 -0400 Subject: [PATCH 22/46] - changes made according to feedbacks of markcampanelli --- pvlib/ivtools.py | 87 ++++++++++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 37 deletions(-) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index 7a2181f7d2..66615d19c8 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -9,6 +9,7 @@ try: from scipy.optimize import root + from scipy import constants except ImportError: raise ImportError( "'scipy.optimize.root' couldn't be imported. Is SciPy installed?") @@ -269,7 +270,7 @@ def fit_sde_sandia(voltage, current, v_oc=None, i_sc=None, v_mp_i_mp=None, def fit_sdm_desoto(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, - cells_in_series=None, temp_ref=25): + cells_in_series, temp_ref=25, irrad_ref=1000): """ Calculates the five parameters for the single diode equation using the De Soto et al. procedure described in [1]. This procedure has the @@ -281,31 +282,32 @@ def fit_sdm_desoto(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, Parameters ---------- - celltype : str, case insensitive + celltype: str, case insensitive Value is one of 'monosi', 'multisi', 'polysi' or'gaas'. Others like 'cis', 'cigs', 'cdte', 'amorphous' are not implemented yet - v_mp : numeric + v_mp: numeric Module voltage at the maximum-power point at std conditions in V. - i_mp : numeric + i_mp: numeric Module current at the maximum-power point at std conditions in A. - v_oc : numeric + v_oc: numeric Open-circuit voltage at std conditions in V. - i_sc : numeric + i_sc: numeric Short-circuit current at std conditions in A. - alpha_sc : numeric + alpha_sc: numeric The short-circuit current (i_sc) temperature coefficient of the module in units of %/K. It is converted in A/K for the computing process. - beta_voc : numeric + beta_voc: numeric The open-circuit voltage (v_oc) temperature coefficient of the module in units of %/K. It is converted in V/K for the computing process. - cells_in_series : numeric, default None + cells_in_series: numeric Number of cell in the module. Optional input, but helps to insure the convergence of the computing. - temp_ref : numeric, default 25 + temp_ref: numeric, default 25 Reference temperature condition [C] - + irrad_ref: numeric, default 1000 + Reference irradiance condition [W/m2] Returns ------- @@ -313,28 +315,31 @@ def fit_sdm_desoto(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, * 'I_L_ref': numeric Light-generated current in amperes at std conditions. - * 'I_o_ref': numeric Diode saturation curent in amperes at std conditions - * 'R_s': numeric Series resistance in ohms. Note that '_ref' is not mentionned in the name because this resistance is not sensible to the conditions of test. - * 'R_sh_ref': numeric Shunt resistance in ohms at std conditions. - * 'a_ref' : numeric Modified ideality factor at std conditions. The product of the usual diode ideality factor (n, unitless), number of cells in series (Ns), and cell thermal voltage at specified effective irradiance and cell temperature. - * 'alpha_sc': numeric Caution!: Different from the input because of the unit. The short-circuit current (i_sc) temperature coefficient of the module in units of A/K. + * 'EgRef': numeric + Energy of bandgap of semi-conductor used (depending on celltype) [J] + * 'dEgdT': numeric + Variation of bandgap according to temperature [J/K] + * 'irrad_ref': numeric + Reference irradiance condition [W/m2] + * 'temp_ref': numeric + Reference temperature condition [C] References ---------- @@ -346,18 +351,17 @@ def fit_sdm_desoto(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, Processes", Wiley, 2013 """ # Constants - k = 1.381e-23 # Boltzmann constant, in J/K, or 8.617e-5 eV/K - q = 1.602e-19 # electron charge in J/V, or 1 eV + k = constants.Boltzmann # in J/K, or 8.617e-5 eV/K + q = constants.elementary_charge # in J/V, or 1 eV Tref = temp_ref + 273.15 # in K if celltype.lower() in ['monosi', 'polysi', 'multisi']: - C = 0.0002677 # valid for silicon - Eg = 1.796e-19 # in J, valid for silicon - + dEgdT = -0.0002677 # valid for silicon + EgRef = 1.796e-19 # in J, valid for silicon elif celltype.lower() in ['gaas']: - C = 0.0003174 # valid for gallium arsenide - Eg = 2.163e-19 # in J, valid for gallium arsenide + dEgdT = -0.0003174 # valid for gallium arsenide + EgRef = 2.163e-19 # in J, valid for gallium arsenide elif celltype.lower() in ['cis', 'cigs', 'cdte', 'amorphous']: raise NotImplementedError else: @@ -390,11 +394,11 @@ def pv_fct(params, specs): y = [0, 0, 0, 0, 0] # 1st equation - short-circuit - eq(3) in [1] - y[0] = Isc - IL + Io*(np.exp(Isc*Rs/a) - 1.0) + Isc*Rs/Rsh + y[0] = Isc - IL + Io*np.expm1(Isc*Rs/a) + Isc*Rs/Rsh # 2nd equation - open-circuit Tref - eq(4) in [1] - y[1] = -IL + Io*(np.exp(Voc/a) - 1.0) + Voc/Rsh + y[1] = -IL + Io*np.expm1(Voc/a) + Voc/Rsh # 3rd equation - Imp & Vmp - eq(5) in [1] - y[2] = Imp - IL + Io*(np.exp((Vmp+Imp*Rs)/a) - 1.0) + \ + y[2] = Imp - IL + Io*np.exp((Vmp+Imp*Rs)/a) + \ (Vmp+Imp*Rs)/Rsh # 4th equation - Pmp derivated=0 - # caution: eq(6) in [1] seems to be incorrect, take eq23.2.6 in [2] @@ -405,9 +409,9 @@ def pv_fct(params, specs): Voc2 = (T2 - Tref)*betaoc + Voc # eq (7) in [1] a2 = a*T2/Tref # eq (8) in [1] IL2 = IL + alphasc*(T2-Tref) # eq (11) in [1] - Eg2 = Eg*(1-C*(T2-Tref)) # eq (10) in [1] - Io2 = Io * (T2/Tref)**3 * np.exp(1/k * (Eg/Tref-Eg2/T2)) # eq (9) - y[4] = -IL2 + Io2*(np.exp(Voc2/a2) - 1.0) + Voc2/Rsh # eq (4) at T2 + Eg2 = EgRef*(1 + dEgdT*(T2-Tref)) # eq (10) in [1] + Io2 = Io * (T2/Tref)**3 * np.exp(1/k * (EgRef/Tref-Eg2/T2)) # eq (9) + y[4] = -IL2 + Io2*np.expm1(Voc2/a2) + Voc2/Rsh # eq (4) at T2 return y @@ -417,7 +421,7 @@ def pv_fct(params, specs): a_i = 1.5*k*Tref*cells_in_series/q IL_i = i_sc Io_i = i_sc * np.exp(-v_oc/a_i) - Rs_i = (a_i*np.log((IL_i-i_mp)/Io_i+1) - v_mp)/i_mp + Rs_i = (a_i*np.log1p((IL_i-i_mp)/Io_i) - v_mp)/i_mp # params_i : initial values vector params_i = np.array([IL_i, Io_i, a_i, Rsh_i, Rs_i]) @@ -425,15 +429,24 @@ def pv_fct(params, specs): specs = np.array([i_sc, v_oc, i_mp, v_mp, beta_voc, alpha_sc]) # computing - sol = root(pv_fct, x0=params_i, args=specs, method='lm').x + result = root(pv_fct, x0=params_i, args=specs, method='lm') + + if result.success: + sdm_params = result.x + else: + raise RuntimeError('Parameter estimation failed') # results - return {'I_L_ref': sol[0], - 'I_o_ref': sol[1], - 'a_ref': sol[2], - 'R_sh_ref': sol[3], - 'R_s': sol[4], - 'alpha_sc': alpha_sc} + return {'I_L_ref': sdm_params[0], + 'I_o_ref': sdm_params[1], + 'a_ref': sdm_params[2], + 'R_sh_ref': sdm_params[3], + 'R_s': sdm_params[4], + 'alpha_sc': alpha_sc, + 'EgRef': EgRef, + 'dEgdT': dEgdT, + 'irrad_ref': irrad_ref, + 'temp_ref': temp_ref} def _find_mp(voltage, current): From 2d53d5d72ab49389da06e479dd3a0438804033c7 Mon Sep 17 00:00:00 2001 From: Lunel Date: Fri, 4 Oct 2019 18:52:51 -0400 Subject: [PATCH 23/46] - some cleaning on fit_sdm_desoto to make it more readable - tests completed --- pvlib/ivtools.py | 28 ++++++++++++---------------- pvlib/test/test_ivtools.py | 35 ++++++++++++++++++++++++----------- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index 66615d19c8..222cd94350 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -356,13 +356,11 @@ def fit_sdm_desoto(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, Tref = temp_ref + 273.15 # in K - if celltype.lower() in ['monosi', 'polysi', 'multisi']: + if celltype.lower() in ['monosi', 'polysi', 'multisi', + 'mono-c-si', 'multi-c-si']: dEgdT = -0.0002677 # valid for silicon EgRef = 1.796e-19 # in J, valid for silicon - elif celltype.lower() in ['gaas']: - dEgdT = -0.0003174 # valid for gallium arsenide - EgRef = 2.163e-19 # in J, valid for gallium arsenide - elif celltype.lower() in ['cis', 'cigs', 'cdte', 'amorphous']: + elif celltype.lower() in ['cis', 'cigs', 'cdte', 'amorphous', 'thin film']: raise NotImplementedError else: raise ValueError('Unknown cell type.') @@ -378,18 +376,11 @@ def pv_fct(params, specs): function the '_' of the variables were removed. """ # six input known variables - Isc = specs[0] - Voc = specs[1] - Imp = specs[2] - Vmp = specs[3] - betaoc = specs[4] - alphasc = specs[5] + Isc, Voc, Imp, Vmp, betaoc, alphasc = specs + # five parameters vector to find - IL = params[0] - Io = params[1] - a = params[2] - Rsh = params[3] - Rs = params[4] + IL, Io, a, Rsh, Rs = params + # five equation vector y = [0, 0, 0, 0, 0] @@ -535,3 +526,8 @@ def _calculate_sde_parameters(beta0, beta1, beta3, beta4, v_mp, i_mp, v_oc): else: # I0_voc > 0 I0 = I0_voc return (IL, I0, Rsh, Rs, nNsVth) + +if __name__ == '__main__': + print(fit_sdm_desoto(celltype='polysi', v_mp= 31.0, i_mp= 8.71, + v_oc= 38.3, i_sc= 9.43, alpha_sc= 0.001, beta_voc= -0.36, + cells_in_series= 60)) \ No newline at end of file diff --git a/pvlib/test/test_ivtools.py b/pvlib/test/test_ivtools.py index 675cff7cce..641bd7fd06 100644 --- a/pvlib/test/test_ivtools.py +++ b/pvlib/test/test_ivtools.py @@ -31,9 +31,8 @@ def get_cec_params_cansol_cs5p_220p(): @pytest.fixture def get_test_specs_params(): """Specifications of module Kyocera KU270-6MCA""" - return {'celltype': 'polysi', 'v_mp': 31.0, 'i_mp': 8.71, - 'v_oc': 38.3, 'i_sc': 9.43, 'alpha_sc': 0.06, 'beta_voc': -0.36, - 'cells_in_series': 60} + return {'v_mp': 31.0, 'i_mp': 8.71, 'v_oc': 38.3, + 'i_sc': 9.43, 'alpha_sc': 0.06, 'beta_voc': -0.36} @requires_scipy @@ -112,15 +111,29 @@ def test_fit_sdm_cec_sam(get_cec_params_cansol_cs5p_220p): @requires_scipy def test_fit_sdm_desoto(get_test_specs_params): - result = ivtools.fit_sdm_desoto(**get_test_specs_params) - result_expected = {'I_L_ref': 9.452306515003592, - 'I_o_ref': 3.244967717785086e-10, - 'a_ref': 1.59170284124638, - 'R_sh_ref': 125.85822206469133, - 'R_s': 0.2977156014170881, - 'alpha_sc': 0.005658} + result = ivtools.fit_sdm_desoto(celltype='polysi', cells_in_series=60, + **get_test_specs_params) + result_expected = {'I_L_ref': 9.452323061192306, + 'I_o_ref': 3.2262431300525453e-10, + 'a_ref': 1.5913209607827592, + 'R_sh_ref': 125.80348604299812, + 'R_s': 0.29780686327583955, + 'alpha_sc': 0.005658, + 'EgRef': 1.796e-19, + 'dEgdT': -0.0002677, + 'irrad_ref': 1000, + 'temp_ref': 25} assert np.allclose(pd.Series(result), pd.Series(result_expected), - rtol=5e-2) + rtol=1e-4) + with pytest.raises(NotImplementedError): + ivtools.fit_sdm_desoto(celltype='CDTE', cells_in_series=60, + **get_test_specs_params) + with pytest.raises(ValueError): + ivtools.fit_sdm_desoto(celltype='apple', cells_in_series=60, + **get_test_specs_params) + with pytest.raises(RuntimeError): + ivtools.fit_sdm_desoto(celltype='polysi', cells_in_series=1, + **get_test_specs_params) @pytest.fixture From fdbf5ec5b321bbde5ca52fe45d5af7585ae55664 Mon Sep 17 00:00:00 2001 From: Lunel Date: Fri, 4 Oct 2019 18:54:11 -0400 Subject: [PATCH 24/46] - minor code cleaning --- pvlib/ivtools.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index 222cd94350..51da55b838 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -526,8 +526,3 @@ def _calculate_sde_parameters(beta0, beta1, beta3, beta4, v_mp, i_mp, v_oc): else: # I0_voc > 0 I0 = I0_voc return (IL, I0, Rsh, Rs, nNsVth) - -if __name__ == '__main__': - print(fit_sdm_desoto(celltype='polysi', v_mp= 31.0, i_mp= 8.71, - v_oc= 38.3, i_sc= 9.43, alpha_sc= 0.001, beta_voc= -0.36, - cells_in_series= 60)) \ No newline at end of file From 0e1b4b397c82f3edf4cb1600573362745b985937 Mon Sep 17 00:00:00 2001 From: Lunel Date: Mon, 7 Oct 2019 15:46:53 -0400 Subject: [PATCH 25/46] - check on importation of scipy removed --- pvlib/ivtools.py | 9 ++------- pvlib/test/test_ivtools.py | 3 +++ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index 51da55b838..a3331327af 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -6,13 +6,8 @@ """ import numpy as np - -try: - from scipy.optimize import root - from scipy import constants -except ImportError: - raise ImportError( - "'scipy.optimize.root' couldn't be imported. Is SciPy installed?") +from scipy.optimize import root +from scipy import constants def fit_sdm_cec_sam(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, diff --git a/pvlib/test/test_ivtools.py b/pvlib/test/test_ivtools.py index 641bd7fd06..2730b19328 100644 --- a/pvlib/test/test_ivtools.py +++ b/pvlib/test/test_ivtools.py @@ -274,3 +274,6 @@ def get_bad_iv_curves(): 2.02963773590165, 1.49291145092070, 0.818343889647352, 0]) return v1, i1, v2, i2 + +if __name__ == '__main__': + pytest.main(['test_ivtools.py::test_fit_sdm_desoto']) From d2d8c45d85399188143accfb1c897437a3cc24dc Mon Sep 17 00:00:00 2001 From: Lunel Date: Mon, 7 Oct 2019 15:49:23 -0400 Subject: [PATCH 26/46] - minor cleaning --- pvlib/test/test_ivtools.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pvlib/test/test_ivtools.py b/pvlib/test/test_ivtools.py index 2730b19328..641bd7fd06 100644 --- a/pvlib/test/test_ivtools.py +++ b/pvlib/test/test_ivtools.py @@ -274,6 +274,3 @@ def get_bad_iv_curves(): 2.02963773590165, 1.49291145092070, 0.818343889647352, 0]) return v1, i1, v2, i2 - -if __name__ == '__main__': - pytest.main(['test_ivtools.py::test_fit_sdm_desoto']) From a70debbf57280e6ab5e7b11183f4a6684e24bfb9 Mon Sep 17 00:00:00 2001 From: Lunel Date: Tue, 8 Oct 2019 10:21:16 -0400 Subject: [PATCH 27/46] - attempt to reach 100% coverage --- pvlib/test/test_ivtools.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pvlib/test/test_ivtools.py b/pvlib/test/test_ivtools.py index 641bd7fd06..fbde353bb3 100644 --- a/pvlib/test/test_ivtools.py +++ b/pvlib/test/test_ivtools.py @@ -132,8 +132,10 @@ def test_fit_sdm_desoto(get_test_specs_params): ivtools.fit_sdm_desoto(celltype='apple', cells_in_series=60, **get_test_specs_params) with pytest.raises(RuntimeError): - ivtools.fit_sdm_desoto(celltype='polysi', cells_in_series=1, - **get_test_specs_params) + ivtools.fit_sdm_desoto(celltype='polysi', cells_in_series=10, + v_mp=31.0, i_mp=8.71, v_oc=38.3, + i_sc=9.43, alpha_sc=0.06, + beta_voc=-0.36) @pytest.fixture From e01f26286a51c6b5d5449f63093ea8ac5f2bddc8 Mon Sep 17 00:00:00 2001 From: Lunel Date: Tue, 8 Oct 2019 11:45:24 -0400 Subject: [PATCH 28/46] - description added in docs/sphinx/source/whatsnew/v0.7.0.rst --- docs/sphinx/source/whatsnew/v0.7.0.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index d358c88249..6b40c3c274 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -1,4 +1,4 @@ -.. _whatsnew_0700: +.. _whatsnew_0700: v0.7.0 (MONTH DAY, YEAR) ------------------------ @@ -96,6 +96,8 @@ Enhancements the single diode equation to an IV curve. * Add :py:func:`~pvlib.ivtools.fit_sdm_cec_sam`, a wrapper for the CEC single diode model fitting function '6parsolve' from NREL's System Advisor Model. +* Add :py:func:`~pvlib.ivtools.fit_sdm_desoto`, a method to fit the single + diode equation to the typical specifications given in manufacturers datasheets. Bug fixes ~~~~~~~~~ From 17f86176febdbffb8b5e908a1814ada523ca0097 Mon Sep 17 00:00:00 2001 From: Lunel Date: Thu, 10 Oct 2019 18:21:51 -0400 Subject: [PATCH 29/46] - changes made according to cwhanse review. Except alpha_sc and beta_voc still in %/K --- pvlib/ivtools.py | 115 ++++++++++++++++++++++++----------------------- 1 file changed, 60 insertions(+), 55 deletions(-) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index a3331327af..424bd28856 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -265,75 +265,81 @@ def fit_sde_sandia(voltage, current, v_oc=None, i_sc=None, v_mp_i_mp=None, def fit_sdm_desoto(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, - cells_in_series, temp_ref=25, irrad_ref=1000): + cells_in_series, temp_ref=25, irrad_ref=1000, + solver_method='lm'): """ - Calculates the five parameters for the single diode equation using - the De Soto et al. procedure described in [1]. This procedure has the + Calculates the parameters for the De Soto single diode model using the + procedure described in [1]. This procedure has the advantage of using common specifications given by manufacturers in the datasheets of PV modules. - The six values returned by this function can be used by + The parameters returned by this function can be used by pvsystem.calcparams_desoto to calculate the values at different irradiance and cell temperature. Parameters ---------- celltype: str, case insensitive - Value is one of 'monosi', 'multisi', 'polysi' or'gaas'. - Others like 'cis', 'cigs', 'cdte', 'amorphous' are not implemented yet - v_mp: numeric - Module voltage at the maximum-power point at std conditions in V. - i_mp: numeric - Module current at the maximum-power point at std conditions in A. - v_oc: numeric - Open-circuit voltage at std conditions in V. - i_sc: numeric - Short-circuit current at std conditions in A. - alpha_sc: numeric + Value is one of 'monosi', 'multisi', 'polysi', 'mono-c-si', + 'multi-c-si'. + Others like 'cis', 'cigs', 'cdte', 'amorphous', 'thin film', 'gaas' + are not implemented yet. + v_mp: float + Module voltage at the maximum-power point at reference conditions [V]. + i_mp: float + Module current at the maximum-power point at reference conditions [A]. + v_oc: float + Open-circuit voltage at reference conditions [V]. + i_sc: float + Short-circuit current at reference conditions [A]. + alpha_sc: float The short-circuit current (i_sc) temperature coefficient of the - module in units of %/K. It is converted in A/K for the computing + module [%/K]. + It is converted in A/K for the computing process. - beta_voc: numeric + beta_voc: float The open-circuit voltage (v_oc) temperature coefficient of the - module in units of %/K. It is converted in V/K for the computing + module [%/K]. + It is converted in V/K for the computing process. - cells_in_series: numeric + cells_in_series: float Number of cell in the module. - Optional input, but helps to insure the convergence of the computing. - temp_ref: numeric, default 25 + temp_ref: float, default 25 Reference temperature condition [C] - irrad_ref: numeric, default 1000 + irrad_ref: float, default 1000 Reference irradiance condition [W/m2] + solver_method: str, default 'lm' + See help of scipy.optimize.root() for more details. Returns ------- - Dictionnary with the following elements: - - * 'I_L_ref': numeric - Light-generated current in amperes at std conditions. - * 'I_o_ref': numeric - Diode saturation curent in amperes at std conditions - * 'R_s': numeric - Series resistance in ohms. Note that '_ref' is not mentionned - in the name because this resistance is not sensible to the - conditions of test. - * 'R_sh_ref': numeric - Shunt resistance in ohms at std conditions. - * 'a_ref' : numeric - Modified ideality factor at std conditions. + Dictionary with the following elements: + + * I_L_ref: float + Light-generated current at reference conditions [A] + * I_o_ref: float + Diode saturation current at reference conditions [A] + * R_s: float + Series resistance [ohms] + Note that '_ref' is not mentionned in the name because + this resistance is not sensible to the conditions of test. + * R_sh_ref: float + Shunt resistance at reference conditions [ohms]. + * a_ref: float + Modified ideality factor at reference conditions. The product of the usual diode ideality factor (n, unitless), number of cells in series (Ns), and cell thermal voltage at specified effective irradiance and cell temperature. - * 'alpha_sc': numeric + * alpha_sc: float Caution!: Different from the input because of the unit. The short-circuit current (i_sc) temperature coefficient of the - module in units of A/K. - * 'EgRef': numeric - Energy of bandgap of semi-conductor used (depending on celltype) [J] - * 'dEgdT': numeric - Variation of bandgap according to temperature [J/K] - * 'irrad_ref': numeric + module [A/K]. + * EgRef: float + Energy of bandgap of semi-conductor used (depending on celltype) [eV] + * dEgdT: float + Variation of bandgap according to temperature [eV/K] + * irrad_ref: float Reference irradiance condition [W/m2] - * 'temp_ref': numeric + * temp_ref: float Reference temperature condition [C] References @@ -346,15 +352,14 @@ def fit_sdm_desoto(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, Processes", Wiley, 2013 """ # Constants - k = constants.Boltzmann # in J/K, or 8.617e-5 eV/K - q = constants.elementary_charge # in J/V, or 1 eV + k = constants.constants.value('Boltzmann constant in eV/K') - Tref = temp_ref + 273.15 # in K + Tref = temp_ref + 273.15 # [K] if celltype.lower() in ['monosi', 'polysi', 'multisi', 'mono-c-si', 'multi-c-si']: - dEgdT = -0.0002677 # valid for silicon - EgRef = 1.796e-19 # in J, valid for silicon + dEgdT = -0.0002677 # [eV/K] + EgRef = 1.121 # [eV] elif celltype.lower() in ['cis', 'cigs', 'cdte', 'amorphous', 'thin film']: raise NotImplementedError else: @@ -365,8 +370,8 @@ def fit_sdm_desoto(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, beta_voc = beta_voc*v_oc/100 def pv_fct(params, specs): - """Returns the system of equations used for computing the - single-diode 5 parameters. + """Evaluates the systems of equations used to solve for the single + diode equation parameters. To avoid the confusion in names with variables of container function the '_' of the variables were removed. """ @@ -386,8 +391,8 @@ def pv_fct(params, specs): # 3rd equation - Imp & Vmp - eq(5) in [1] y[2] = Imp - IL + Io*np.exp((Vmp+Imp*Rs)/a) + \ (Vmp+Imp*Rs)/Rsh - # 4th equation - Pmp derivated=0 - - # caution: eq(6) in [1] seems to be incorrect, take eq23.2.6 in [2] + # 4th equation - Pmp derivated=0 - eq23.2.6 in [2] + # caution: eq(6) in [1] has a sign error y[3] = Imp - Vmp * ((Io/a)*np.exp((Vmp+Imp*Rs)/a) + 1.0/Rsh) / \ (1.0 + (Io*Rs/a)*np.exp((Vmp+Imp*Rs)/a) + Rs/Rsh) # 5th equation - open-circuit T2 - eq (4) at temperature T2 in [1] @@ -404,7 +409,7 @@ def pv_fct(params, specs): # initial guesses of variables for computing convergence: # Values are taken from [2], p753 Rsh_i = 100.0 - a_i = 1.5*k*Tref*cells_in_series/q + a_i = 1.5*k*Tref*cells_in_series IL_i = i_sc Io_i = i_sc * np.exp(-v_oc/a_i) Rs_i = (a_i*np.log1p((IL_i-i_mp)/Io_i) - v_mp)/i_mp @@ -415,7 +420,7 @@ def pv_fct(params, specs): specs = np.array([i_sc, v_oc, i_mp, v_mp, beta_voc, alpha_sc]) # computing - result = root(pv_fct, x0=params_i, args=specs, method='lm') + result = root(pv_fct, x0=params_i, args=specs, method=solver_method) if result.success: sdm_params = result.x From e14ad40613b5c6e553ce90b5e0e7651ef1fdd034 Mon Sep 17 00:00:00 2001 From: Lunel Date: Thu, 10 Oct 2019 18:29:59 -0400 Subject: [PATCH 30/46] - minor correction and adaptation of test --- pvlib/ivtools.py | 2 +- pvlib/test/test_ivtools.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index 424bd28856..52ec20168a 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -352,7 +352,7 @@ def fit_sdm_desoto(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, Processes", Wiley, 2013 """ # Constants - k = constants.constants.value('Boltzmann constant in eV/K') + k = constants.value('Boltzmann constant in eV/K') Tref = temp_ref + 273.15 # [K] diff --git a/pvlib/test/test_ivtools.py b/pvlib/test/test_ivtools.py index fbde353bb3..7cb1217b94 100644 --- a/pvlib/test/test_ivtools.py +++ b/pvlib/test/test_ivtools.py @@ -113,13 +113,13 @@ def test_fit_sdm_cec_sam(get_cec_params_cansol_cs5p_220p): def test_fit_sdm_desoto(get_test_specs_params): result = ivtools.fit_sdm_desoto(celltype='polysi', cells_in_series=60, **get_test_specs_params) - result_expected = {'I_L_ref': 9.452323061192306, - 'I_o_ref': 3.2262431300525453e-10, - 'a_ref': 1.5913209607827592, - 'R_sh_ref': 125.80348604299812, - 'R_s': 0.29780686327583955, + result_expected = {'I_L_ref': 9.452324509050774, + 'I_o_ref': 3.2246097466679494e-10, + 'a_ref': 1.5912875522463978, + 'R_sh_ref': 125.79869968976132, + 'R_s': 0.2978148476600744, 'alpha_sc': 0.005658, - 'EgRef': 1.796e-19, + 'EgRef': 1.121, 'dEgdT': -0.0002677, 'irrad_ref': 1000, 'temp_ref': 25} From 32049b08502b354855e6bb5c2b46e501e024de01 Mon Sep 17 00:00:00 2001 From: Lunel Date: Fri, 11 Oct 2019 11:52:44 -0400 Subject: [PATCH 31/46] - sign correction on 3rd equation --- pvlib/ivtools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index 52ec20168a..a1f82f8087 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -389,7 +389,7 @@ def pv_fct(params, specs): # 2nd equation - open-circuit Tref - eq(4) in [1] y[1] = -IL + Io*np.expm1(Voc/a) + Voc/Rsh # 3rd equation - Imp & Vmp - eq(5) in [1] - y[2] = Imp - IL + Io*np.exp((Vmp+Imp*Rs)/a) + \ + y[2] = Imp - IL + Io*np.expm1((Vmp+Imp*Rs)/a) + \ (Vmp+Imp*Rs)/Rsh # 4th equation - Pmp derivated=0 - eq23.2.6 in [2] # caution: eq(6) in [1] has a sign error From bfe3994343423a9e41ce5715441a29ad41a9b4aa Mon Sep 17 00:00:00 2001 From: Lunel Date: Wed, 16 Oct 2019 16:44:53 -0400 Subject: [PATCH 32/46] - changes on units of alpha_sc and beta_voc inputs. Now in A/K and V/K rather than %/K. - 'celltype' input replaced by EgRef and dEgdT, with values of Si as default --- pvlib/ivtools.py | 41 ++++++++++---------------------------- pvlib/test/test_ivtools.py | 15 ++------------ 2 files changed, 13 insertions(+), 43 deletions(-) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index a1f82f8087..70ce8dac18 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -264,9 +264,9 @@ def fit_sde_sandia(voltage, current, v_oc=None, i_sc=None, v_mp_i_mp=None, v_oc) -def fit_sdm_desoto(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, - cells_in_series, temp_ref=25, irrad_ref=1000, - solver_method='lm'): +def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, + cells_in_series, EgRef=1.121, dEgdT=-0.0002677, + temp_ref=25, irrad_ref=1000, solver_method='lm'): """ Calculates the parameters for the De Soto single diode model using the procedure described in [1]. This procedure has the @@ -278,12 +278,7 @@ def fit_sdm_desoto(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, Parameters ---------- - celltype: str, case insensitive - Value is one of 'monosi', 'multisi', 'polysi', 'mono-c-si', - 'multi-c-si'. - Others like 'cis', 'cigs', 'cdte', 'amorphous', 'thin film', 'gaas' - are not implemented yet. - v_mp: float + v_mp: float Module voltage at the maximum-power point at reference conditions [V]. i_mp: float Module current at the maximum-power point at reference conditions [A]. @@ -293,16 +288,16 @@ def fit_sdm_desoto(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, Short-circuit current at reference conditions [A]. alpha_sc: float The short-circuit current (i_sc) temperature coefficient of the - module [%/K]. - It is converted in A/K for the computing - process. + module [A/K]. beta_voc: float The open-circuit voltage (v_oc) temperature coefficient of the - module [%/K]. - It is converted in V/K for the computing - process. + module [V/K]. cells_in_series: float Number of cell in the module. + EgRef: float, default 1.121 eV - value for silicon + Energy of bandgap of semi-conductor used [eV] + dEgdT: float, default -0.0002677 - value for silicon + Variation of bandgap according to temperature [eV/K] temp_ref: float, default 25 Reference temperature condition [C] irrad_ref: float, default 1000 @@ -334,7 +329,7 @@ def fit_sdm_desoto(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, The short-circuit current (i_sc) temperature coefficient of the module [A/K]. * EgRef: float - Energy of bandgap of semi-conductor used (depending on celltype) [eV] + Energy of bandgap of semi-conductor used [eV] * dEgdT: float Variation of bandgap according to temperature [eV/K] * irrad_ref: float @@ -353,22 +348,8 @@ def fit_sdm_desoto(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, """ # Constants k = constants.value('Boltzmann constant in eV/K') - Tref = temp_ref + 273.15 # [K] - if celltype.lower() in ['monosi', 'polysi', 'multisi', - 'mono-c-si', 'multi-c-si']: - dEgdT = -0.0002677 # [eV/K] - EgRef = 1.121 # [eV] - elif celltype.lower() in ['cis', 'cigs', 'cdte', 'amorphous', 'thin film']: - raise NotImplementedError - else: - raise ValueError('Unknown cell type.') - - # Conversion from %/K to A/K & V/K - alpha_sc = alpha_sc*i_sc/100 - beta_voc = beta_voc*v_oc/100 - def pv_fct(params, specs): """Evaluates the systems of equations used to solve for the single diode equation parameters. diff --git a/pvlib/test/test_ivtools.py b/pvlib/test/test_ivtools.py index 7cb1217b94..4f1ce17ff1 100644 --- a/pvlib/test/test_ivtools.py +++ b/pvlib/test/test_ivtools.py @@ -32,7 +32,7 @@ def get_cec_params_cansol_cs5p_220p(): def get_test_specs_params(): """Specifications of module Kyocera KU270-6MCA""" return {'v_mp': 31.0, 'i_mp': 8.71, 'v_oc': 38.3, - 'i_sc': 9.43, 'alpha_sc': 0.06, 'beta_voc': -0.36} + 'i_sc': 9.43, 'alpha_sc': 0.005658, 'beta_voc': -0.13788} @requires_scipy @@ -111,7 +111,7 @@ def test_fit_sdm_cec_sam(get_cec_params_cansol_cs5p_220p): @requires_scipy def test_fit_sdm_desoto(get_test_specs_params): - result = ivtools.fit_sdm_desoto(celltype='polysi', cells_in_series=60, + result = ivtools.fit_sdm_desoto(cells_in_series=60, **get_test_specs_params) result_expected = {'I_L_ref': 9.452324509050774, 'I_o_ref': 3.2246097466679494e-10, @@ -125,17 +125,6 @@ def test_fit_sdm_desoto(get_test_specs_params): 'temp_ref': 25} assert np.allclose(pd.Series(result), pd.Series(result_expected), rtol=1e-4) - with pytest.raises(NotImplementedError): - ivtools.fit_sdm_desoto(celltype='CDTE', cells_in_series=60, - **get_test_specs_params) - with pytest.raises(ValueError): - ivtools.fit_sdm_desoto(celltype='apple', cells_in_series=60, - **get_test_specs_params) - with pytest.raises(RuntimeError): - ivtools.fit_sdm_desoto(celltype='polysi', cells_in_series=10, - v_mp=31.0, i_mp=8.71, v_oc=38.3, - i_sc=9.43, alpha_sc=0.06, - beta_voc=-0.36) @pytest.fixture From 7ff8a9601aba79fd480d68c44d4a40093c25eaef Mon Sep 17 00:00:00 2001 From: Lunel Date: Wed, 16 Oct 2019 17:10:31 -0400 Subject: [PATCH 33/46] - other line of test added for better coverage --- pvlib/test/test_ivtools.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pvlib/test/test_ivtools.py b/pvlib/test/test_ivtools.py index 4f1ce17ff1..c04a2197ab 100644 --- a/pvlib/test/test_ivtools.py +++ b/pvlib/test/test_ivtools.py @@ -125,6 +125,11 @@ def test_fit_sdm_desoto(get_test_specs_params): 'temp_ref': 25} assert np.allclose(pd.Series(result), pd.Series(result_expected), rtol=1e-4) + with pytest.raises(RuntimeError): + ivtools.fit_sdm_desoto(cells_in_series=10, + v_mp=31.0, i_mp=8.71, v_oc=38.3, + i_sc=9.43, alpha_sc=0.06, + beta_voc=-0.36) @pytest.fixture From 7f08d80989650757550d5ce2f9c2d302990ce3ef Mon Sep 17 00:00:00 2001 From: Lunel Date: Thu, 17 Oct 2019 18:27:04 -0400 Subject: [PATCH 34/46] - some changes to try feedbacks of cwhanse and markcampanelli, not finished --- pvlib/ivtools.py | 9 +++++++-- pvlib/test/test_ivtools.py | 30 ++++++++++++++++-------------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index 70ce8dac18..dc6ec4fd78 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -6,8 +6,6 @@ """ import numpy as np -from scipy.optimize import root -from scipy import constants def fit_sdm_cec_sam(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, @@ -346,6 +344,13 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, [2] John A Duffie ,William A Beckman, "Solar Engineering of Thermal Processes", Wiley, 2013 """ + + try: + from scipy.optimize import root + from scipy import constants + except ImportError: + raise ImportError("The fit_sdm_desoto function requires scipy.") + # Constants k = constants.value('Boltzmann constant in eV/K') Tref = temp_ref + 273.15 # [K] diff --git a/pvlib/test/test_ivtools.py b/pvlib/test/test_ivtools.py index c04a2197ab..5d3b2099dd 100644 --- a/pvlib/test/test_ivtools.py +++ b/pvlib/test/test_ivtools.py @@ -111,20 +111,22 @@ def test_fit_sdm_cec_sam(get_cec_params_cansol_cs5p_220p): @requires_scipy def test_fit_sdm_desoto(get_test_specs_params): - result = ivtools.fit_sdm_desoto(cells_in_series=60, - **get_test_specs_params) - result_expected = {'I_L_ref': 9.452324509050774, - 'I_o_ref': 3.2246097466679494e-10, - 'a_ref': 1.5912875522463978, - 'R_sh_ref': 125.79869968976132, - 'R_s': 0.2978148476600744, - 'alpha_sc': 0.005658, - 'EgRef': 1.121, - 'dEgdT': -0.0002677, - 'irrad_ref': 1000, - 'temp_ref': 25} - assert np.allclose(pd.Series(result), pd.Series(result_expected), - rtol=1e-4) + for solver_method in ('hybr', 'lm'): + result = ivtools.fit_sdm_desoto(cells_in_series=60, + **get_test_specs_params, + solver_method=solver_method) + result_expected = {'I_L_ref': 9.452324509050774, + 'I_o_ref': 3.2246097466679494e-10, + 'a_ref': 1.5912875522463978, + 'R_sh_ref': 125.79869968976132, + 'R_s': 0.2978148476600744, + 'alpha_sc': 0.005658, + 'EgRef': 1.121, + 'dEgdT': -0.0002677, + 'irrad_ref': 1000, + 'temp_ref': 25} + assert np.allclose(pd.Series(result), pd.Series(result_expected), + rtol=1e-4) with pytest.raises(RuntimeError): ivtools.fit_sdm_desoto(cells_in_series=10, v_mp=31.0, i_mp=8.71, v_oc=38.3, From 27ab96186c68bb87af0234a0f9a7bad82a7583dc Mon Sep 17 00:00:00 2001 From: Lunel Date: Fri, 18 Oct 2019 15:24:02 -0400 Subject: [PATCH 35/46] -OptimizeResult added in output - solver_method removed - docstring modified - result.message included in message raised by RuntimeError --- pvlib/ivtools.py | 23 +++++++++++++---------- pvlib/test/test_ivtools.py | 34 +++++++++++++++++----------------- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index dc6ec4fd78..4ed99f5d8c 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -264,12 +264,14 @@ def fit_sde_sandia(voltage, current, v_oc=None, i_sc=None, v_mp_i_mp=None, def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, cells_in_series, EgRef=1.121, dEgdT=-0.0002677, - temp_ref=25, irrad_ref=1000, solver_method='lm'): + temp_ref=25, irrad_ref=1000): """ Calculates the parameters for the De Soto single diode model using the - procedure described in [1]. This procedure has the - advantage of using common specifications given by manufacturers in the - datasheets of PV modules. + procedure described in [1]. This procedure has the advantage of + using common specifications given by manufacturers in the + datasheets of PV modules. The solution is found using the + scipy.optimize.root() function, with the correpsonding default solver + method 'hybr'. The parameters returned by this function can be used by pvsystem.calcparams_desoto to calculate the values at different irradiance and cell temperature. @@ -300,8 +302,6 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, Reference temperature condition [C] irrad_ref: float, default 1000 Reference irradiance condition [W/m2] - solver_method: str, default 'lm' - See help of scipy.optimize.root() for more details. Returns ------- @@ -323,7 +323,6 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, number of cells in series (Ns), and cell thermal voltage at specified effective irradiance and cell temperature. * alpha_sc: float - Caution!: Different from the input because of the unit. The short-circuit current (i_sc) temperature coefficient of the module [A/K]. * EgRef: float @@ -334,6 +333,9 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, Reference irradiance condition [W/m2] * temp_ref: float Reference temperature condition [C] + * result: scipy.optimize.OptimizeResult + Optimization result of scipy.optimize.root(). + See scipy.optimize.OptimizeResult for more details. References ---------- @@ -406,12 +408,12 @@ def pv_fct(params, specs): specs = np.array([i_sc, v_oc, i_mp, v_mp, beta_voc, alpha_sc]) # computing - result = root(pv_fct, x0=params_i, args=specs, method=solver_method) + result = root(pv_fct, x0=params_i, args=specs) if result.success: sdm_params = result.x else: - raise RuntimeError('Parameter estimation failed') + raise RuntimeError('Parameter estimation failed:\n' + result.message) # results return {'I_L_ref': sdm_params[0], @@ -423,7 +425,8 @@ def pv_fct(params, specs): 'EgRef': EgRef, 'dEgdT': dEgdT, 'irrad_ref': irrad_ref, - 'temp_ref': temp_ref} + 'temp_ref': temp_ref, + 'optimize_result': result} def _find_mp(voltage, current): diff --git a/pvlib/test/test_ivtools.py b/pvlib/test/test_ivtools.py index 5d3b2099dd..6d47144848 100644 --- a/pvlib/test/test_ivtools.py +++ b/pvlib/test/test_ivtools.py @@ -111,27 +111,27 @@ def test_fit_sdm_cec_sam(get_cec_params_cansol_cs5p_220p): @requires_scipy def test_fit_sdm_desoto(get_test_specs_params): - for solver_method in ('hybr', 'lm'): - result = ivtools.fit_sdm_desoto(cells_in_series=60, - **get_test_specs_params, - solver_method=solver_method) - result_expected = {'I_L_ref': 9.452324509050774, - 'I_o_ref': 3.2246097466679494e-10, - 'a_ref': 1.5912875522463978, - 'R_sh_ref': 125.79869968976132, - 'R_s': 0.2978148476600744, - 'alpha_sc': 0.005658, - 'EgRef': 1.121, - 'dEgdT': -0.0002677, - 'irrad_ref': 1000, - 'temp_ref': 25} - assert np.allclose(pd.Series(result), pd.Series(result_expected), - rtol=1e-4) - with pytest.raises(RuntimeError): + result = ivtools.fit_sdm_desoto(cells_in_series=60, + **get_test_specs_params) + result_expected = {'I_L_ref': 9.452324509050774, + 'I_o_ref': 3.2246097466679494e-10, + 'a_ref': 1.5912875522463978, + 'R_sh_ref': 125.79869968976132, + 'R_s': 0.2978148476600744, + 'alpha_sc': 0.005658, + 'EgRef': 1.121, + 'dEgdT': -0.0002677, + 'irrad_ref': 1000, + 'temp_ref': 25} + del result['optimize_result'] + assert np.allclose(pd.Series(result), pd.Series(result_expected), + rtol=1e-4) + with pytest.raises(RuntimeError) as exc: ivtools.fit_sdm_desoto(cells_in_series=10, v_mp=31.0, i_mp=8.71, v_oc=38.3, i_sc=9.43, alpha_sc=0.06, beta_voc=-0.36) + assert ('Parameter estimation failed') in str(exc.value) @pytest.fixture From 79ef920d340ca39027220036108d05192bb03fb5 Mon Sep 17 00:00:00 2001 From: Lunel Date: Mon, 21 Oct 2019 19:50:02 -0400 Subject: [PATCH 36/46] - includes all feedbacks made on the 21/10, except moving of pv_fct() to the module level --- docs/sphinx/source/api.rst | 1 + docs/sphinx/source/whatsnew/v0.7.0.rst | 4 +- pvlib/ivtools.py | 110 +++++++++++++------------ pvlib/test/test_ivtools.py | 30 +++---- 4 files changed, 74 insertions(+), 71 deletions(-) diff --git a/docs/sphinx/source/api.rst b/docs/sphinx/source/api.rst index 6e93dc5807..d363a40e4e 100644 --- a/docs/sphinx/source/api.rst +++ b/docs/sphinx/source/api.rst @@ -301,6 +301,7 @@ Functions for fitting PV models ivtools.fit_sde_sandia ivtools.fit_sdm_cec_sam + ivtools.fit_sdm_desoto Other ----- diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index 6b40c3c274..910b3ff78c 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -96,8 +96,8 @@ Enhancements the single diode equation to an IV curve. * Add :py:func:`~pvlib.ivtools.fit_sdm_cec_sam`, a wrapper for the CEC single diode model fitting function '6parsolve' from NREL's System Advisor Model. -* Add :py:func:`~pvlib.ivtools.fit_sdm_desoto`, a method to fit the single - diode equation to the typical specifications given in manufacturers datasheets. +* Add :py:func:`~pvlib.ivtools.fit_sdm_desoto`, a method to fit the De Soto single + diode model to the typical specifications given in manufacturers datasheets. Bug fixes ~~~~~~~~~ diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index 4ed99f5d8c..ae9b2eee07 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -269,16 +269,20 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, Calculates the parameters for the De Soto single diode model using the procedure described in [1]. This procedure has the advantage of using common specifications given by manufacturers in the - datasheets of PV modules. The solution is found using the - scipy.optimize.root() function, with the correpsonding default solver - method 'hybr'. + datasheets of PV modules. + The solution is found using the scipy.optimize.root() function, + with the corresponding default solver method 'hybr'. + No restriction is put on the fit variables, i.e. series + or shunt resistance could go negative. Nevertheless, if it happens, + check carefully the inputs and their units, alpha_sc and beta_voc are + often given in %/K in manufacturers datasheets. The parameters returned by this function can be used by pvsystem.calcparams_desoto to calculate the values at different irradiance and cell temperature. Parameters ---------- - v_mp: float + v_mp: float Module voltage at the maximum-power point at reference conditions [V]. i_mp: float Module current at the maximum-power point at reference conditions [A]. @@ -305,37 +309,36 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, Returns ------- - Dictionary with the following elements: - - * I_L_ref: float - Light-generated current at reference conditions [A] - * I_o_ref: float - Diode saturation current at reference conditions [A] - * R_s: float - Series resistance [ohms] - Note that '_ref' is not mentionned in the name because - this resistance is not sensible to the conditions of test. - * R_sh_ref: float - Shunt resistance at reference conditions [ohms]. - * a_ref: float - Modified ideality factor at reference conditions. - The product of the usual diode ideality factor (n, unitless), - number of cells in series (Ns), and cell thermal voltage at - specified effective irradiance and cell temperature. - * alpha_sc: float - The short-circuit current (i_sc) temperature coefficient of the - module [A/K]. - * EgRef: float - Energy of bandgap of semi-conductor used [eV] - * dEgdT: float - Variation of bandgap according to temperature [eV/K] - * irrad_ref: float - Reference irradiance condition [W/m2] - * temp_ref: float - Reference temperature condition [C] - * result: scipy.optimize.OptimizeResult - Optimization result of scipy.optimize.root(). - See scipy.optimize.OptimizeResult for more details. + Tuple of the following elements: + + * Dictionary with the following elements: + I_L_ref: float + Light-generated current at reference conditions [A] + I_o_ref: float + Diode saturation current at reference conditions [A] + R_s: float + Series resistance [ohms] + R_sh_ref: float + Shunt resistance at reference conditions [ohms]. + a_ref: float + Modified ideality factor at reference conditions. + The product of the usual diode ideality factor (n, unitless), + number of cells in series (Ns), and cell thermal voltage at + specified effective irradiance and cell temperature. + alpha_sc: float + The short-circuit current (i_sc) temperature coefficient of the + module [A/K]. + EgRef: float + Energy of bandgap of semi-conductor used [eV] + dEgdT: float + Variation of bandgap according to temperature [eV/K] + irrad_ref: float + Reference irradiance condition [W/m2] + temp_ref: float + Reference temperature condition [C] + * scipy.optimize.OptimizeResult + Optimization result of scipy.optimize.root(). + See scipy.optimize.OptimizeResult for more details. References ---------- @@ -343,7 +346,7 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, photovoltaic array performance", Solar Energy, vol 80, pp. 78-88, 2006. - [2] John A Duffie ,William A Beckman, "Solar Engineering of Thermal + [2] John A Duffie, William A Beckman, "Solar Engineering of Thermal Processes", Wiley, 2013 """ @@ -360,8 +363,6 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, def pv_fct(params, specs): """Evaluates the systems of equations used to solve for the single diode equation parameters. - To avoid the confusion in names with variables of container - function the '_' of the variables were removed. """ # six input known variables Isc, Voc, Imp, Vmp, betaoc, alphasc = specs @@ -405,28 +406,29 @@ def pv_fct(params, specs): params_i = np.array([IL_i, Io_i, a_i, Rsh_i, Rs_i]) # specs of module - specs = np.array([i_sc, v_oc, i_mp, v_mp, beta_voc, alpha_sc]) + specs = ((i_sc, v_oc, i_mp, v_mp, beta_voc, alpha_sc),) # computing - result = root(pv_fct, x0=params_i, args=specs) + optimize_result = root(pv_fct, x0=params_i, args=specs) - if result.success: - sdm_params = result.x + if optimize_result.success: + sdm_params = optimize_result.x else: - raise RuntimeError('Parameter estimation failed:\n' + result.message) + raise RuntimeError('Parameter estimation failed:\n' + + optimize_result.message) # results - return {'I_L_ref': sdm_params[0], - 'I_o_ref': sdm_params[1], - 'a_ref': sdm_params[2], - 'R_sh_ref': sdm_params[3], - 'R_s': sdm_params[4], - 'alpha_sc': alpha_sc, - 'EgRef': EgRef, - 'dEgdT': dEgdT, - 'irrad_ref': irrad_ref, - 'temp_ref': temp_ref, - 'optimize_result': result} + return ({'I_L_ref': sdm_params[0], + 'I_o_ref': sdm_params[1], + 'a_ref': sdm_params[2], + 'R_sh_ref': sdm_params[3], + 'R_s': sdm_params[4], + 'alpha_sc': alpha_sc, + 'EgRef': EgRef, + 'dEgdT': dEgdT, + 'irrad_ref': irrad_ref, + 'temp_ref': temp_ref}, + optimize_result) def _find_mp(voltage, current): diff --git a/pvlib/test/test_ivtools.py b/pvlib/test/test_ivtools.py index 6d47144848..d9dd8fb757 100644 --- a/pvlib/test/test_ivtools.py +++ b/pvlib/test/test_ivtools.py @@ -28,13 +28,6 @@ def get_cec_params_cansol_cs5p_220p(): 'R_sh_ref': 837.51, 'R_s': 1.004, 'Adjust': 2.3}} -@pytest.fixture -def get_test_specs_params(): - """Specifications of module Kyocera KU270-6MCA""" - return {'v_mp': 31.0, 'i_mp': 8.71, 'v_oc': 38.3, - 'i_sc': 9.43, 'alpha_sc': 0.005658, 'beta_voc': -0.13788} - - @requires_scipy def test_fit_sde_sandia(get_test_iv_params, get_bad_iv_curves): test_params = get_test_iv_params @@ -110,9 +103,11 @@ def test_fit_sdm_cec_sam(get_cec_params_cansol_cs5p_220p): @requires_scipy -def test_fit_sdm_desoto(get_test_specs_params): - result = ivtools.fit_sdm_desoto(cells_in_series=60, - **get_test_specs_params) +def test_fit_sdm_desoto(): + result, _ = ivtools.fit_sdm_desoto(v_mp=31.0, i_mp=8.71, v_oc=38.3, + i_sc=9.43, alpha_sc=0.005658, + beta_voc=-0.13788, + cells_in_series=60) result_expected = {'I_L_ref': 9.452324509050774, 'I_o_ref': 3.2246097466679494e-10, 'a_ref': 1.5912875522463978, @@ -123,14 +118,15 @@ def test_fit_sdm_desoto(get_test_specs_params): 'dEgdT': -0.0002677, 'irrad_ref': 1000, 'temp_ref': 25} - del result['optimize_result'] assert np.allclose(pd.Series(result), pd.Series(result_expected), rtol=1e-4) + + +def test_fit_sdm_desoto_failure(): with pytest.raises(RuntimeError) as exc: - ivtools.fit_sdm_desoto(cells_in_series=10, - v_mp=31.0, i_mp=8.71, v_oc=38.3, - i_sc=9.43, alpha_sc=0.06, - beta_voc=-0.36) + ivtools.fit_sdm_desoto(v_mp=31.0, i_mp=8.71, v_oc=38.3, i_sc=9.43, + alpha_sc=0.005658, beta_voc=-0.13788, + cells_in_series=10) assert ('Parameter estimation failed') in str(exc.value) @@ -272,3 +268,7 @@ def get_bad_iv_curves(): 2.02963773590165, 1.49291145092070, 0.818343889647352, 0]) return v1, i1, v2, i2 + +if __name__ == '__main__': + pytest.main(['test_ivtools.py::test_fit_sdm_desoto']) + pytest.main(['test_ivtools.py::test_fit_sdm_desoto_failure']) \ No newline at end of file From 5e9cf8d00f50145f7e8f203bdb8d701c123e46fb Mon Sep 17 00:00:00 2001 From: Lunel Date: Mon, 21 Oct 2019 19:57:20 -0400 Subject: [PATCH 37/46] - pv_fct moved out of the fit_sdm_desoto function and renamed in _system_of_equations --- pvlib/ivtools.py | 99 ++++++++++++++++++++++++-------------- pvlib/test/test_ivtools.py | 4 -- 2 files changed, 62 insertions(+), 41 deletions(-) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index ae9b2eee07..a9164642f1 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -360,41 +360,6 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, k = constants.value('Boltzmann constant in eV/K') Tref = temp_ref + 273.15 # [K] - def pv_fct(params, specs): - """Evaluates the systems of equations used to solve for the single - diode equation parameters. - """ - # six input known variables - Isc, Voc, Imp, Vmp, betaoc, alphasc = specs - - # five parameters vector to find - IL, Io, a, Rsh, Rs = params - - # five equation vector - y = [0, 0, 0, 0, 0] - - # 1st equation - short-circuit - eq(3) in [1] - y[0] = Isc - IL + Io*np.expm1(Isc*Rs/a) + Isc*Rs/Rsh - # 2nd equation - open-circuit Tref - eq(4) in [1] - y[1] = -IL + Io*np.expm1(Voc/a) + Voc/Rsh - # 3rd equation - Imp & Vmp - eq(5) in [1] - y[2] = Imp - IL + Io*np.expm1((Vmp+Imp*Rs)/a) + \ - (Vmp+Imp*Rs)/Rsh - # 4th equation - Pmp derivated=0 - eq23.2.6 in [2] - # caution: eq(6) in [1] has a sign error - y[3] = Imp - Vmp * ((Io/a)*np.exp((Vmp+Imp*Rs)/a) + 1.0/Rsh) / \ - (1.0 + (Io*Rs/a)*np.exp((Vmp+Imp*Rs)/a) + Rs/Rsh) - # 5th equation - open-circuit T2 - eq (4) at temperature T2 in [1] - T2 = Tref + 2 - Voc2 = (T2 - Tref)*betaoc + Voc # eq (7) in [1] - a2 = a*T2/Tref # eq (8) in [1] - IL2 = IL + alphasc*(T2-Tref) # eq (11) in [1] - Eg2 = EgRef*(1 + dEgdT*(T2-Tref)) # eq (10) in [1] - Io2 = Io * (T2/Tref)**3 * np.exp(1/k * (EgRef/Tref-Eg2/T2)) # eq (9) - y[4] = -IL2 + Io2*np.expm1(Voc2/a2) + Voc2/Rsh # eq (4) at T2 - - return y - # initial guesses of variables for computing convergence: # Values are taken from [2], p753 Rsh_i = 100.0 @@ -406,10 +371,11 @@ def pv_fct(params, specs): params_i = np.array([IL_i, Io_i, a_i, Rsh_i, Rs_i]) # specs of module - specs = ((i_sc, v_oc, i_mp, v_mp, beta_voc, alpha_sc),) + specs = ((i_sc, v_oc, i_mp, v_mp, beta_voc, alpha_sc, EgRef, dEgdT, + Tref),) # computing - optimize_result = root(pv_fct, x0=params_i, args=specs) + optimize_result = root(_system_of_equations, x0=params_i, args=specs) if optimize_result.success: sdm_params = optimize_result.x @@ -517,3 +483,62 @@ def _calculate_sde_parameters(beta0, beta1, beta3, beta4, v_mp, i_mp, v_oc): else: # I0_voc > 0 I0 = I0_voc return (IL, I0, Rsh, Rs, nNsVth) + + +def _system_of_equations(params, specs): + """Evaluates the systems of equations used to solve for the single + diode equation parameters. Function designed to be used by + scipy.optimize.root() in fit_sdm_desoto(). + + Parameters + ---------- + params: ndarray + Array with parameters of the De Soto single diode model. Must be + given in the following order: IL, Io, a, Rsh, Rs + specs: tuple + Specifications of pv module given by manufacturer. Must be given + in the following order: Isc, Voc, Imp, Vmp, betaoc, alphasc + + Returns + ------- + system of equations to solve with scipy.optimize.root(). + """ + try: + from scipy import constants + except ImportError: + raise ImportError("The fit_sdm_desoto function requires scipy.") + + # Constants + k = constants.value('Boltzmann constant in eV/K') + + # six input known variables + Isc, Voc, Imp, Vmp, betaoc, alphasc, EgRef, dEgdT, Tref = specs + + # five parameters vector to find + IL, Io, a, Rsh, Rs = params + + # five equation vector + y = [0, 0, 0, 0, 0] + + # 1st equation - short-circuit - eq(3) in [1] + y[0] = Isc - IL + Io*np.expm1(Isc*Rs/a) + Isc*Rs/Rsh + # 2nd equation - open-circuit Tref - eq(4) in [1] + y[1] = -IL + Io*np.expm1(Voc/a) + Voc/Rsh + # 3rd equation - Imp & Vmp - eq(5) in [1] + y[2] = Imp - IL + Io*np.expm1((Vmp+Imp*Rs)/a) + \ + (Vmp+Imp*Rs)/Rsh + # 4th equation - Pmp derivated=0 - eq23.2.6 in [2] + # caution: eq(6) in [1] has a sign error + y[3] = Imp - Vmp * ((Io/a)*np.exp((Vmp+Imp*Rs)/a) + 1.0/Rsh) / \ + (1.0 + (Io*Rs/a)*np.exp((Vmp+Imp*Rs)/a) + Rs/Rsh) + # 5th equation - open-circuit T2 - eq (4) at temperature T2 in [1] + T2 = Tref + 2 + Voc2 = (T2 - Tref)*betaoc + Voc # eq (7) in [1] + a2 = a*T2/Tref # eq (8) in [1] + IL2 = IL + alphasc*(T2-Tref) # eq (11) in [1] + Eg2 = EgRef*(1 + dEgdT*(T2-Tref)) # eq (10) in [1] + Io2 = Io * (T2/Tref)**3 * np.exp(1/k * (EgRef/Tref-Eg2/T2)) # eq (9) + y[4] = -IL2 + Io2*np.expm1(Voc2/a2) + Voc2/Rsh # eq (4) at T2 + + return y + diff --git a/pvlib/test/test_ivtools.py b/pvlib/test/test_ivtools.py index d9dd8fb757..9096f060e9 100644 --- a/pvlib/test/test_ivtools.py +++ b/pvlib/test/test_ivtools.py @@ -268,7 +268,3 @@ def get_bad_iv_curves(): 2.02963773590165, 1.49291145092070, 0.818343889647352, 0]) return v1, i1, v2, i2 - -if __name__ == '__main__': - pytest.main(['test_ivtools.py::test_fit_sdm_desoto']) - pytest.main(['test_ivtools.py::test_fit_sdm_desoto_failure']) \ No newline at end of file From c02e74f30ed4b236cea3acaad5c2bd74e2641387 Mon Sep 17 00:00:00 2001 From: Lunel Date: Mon, 21 Oct 2019 20:00:43 -0400 Subject: [PATCH 38/46] - minor modification: Boltzmann k given in specs to avoid import of scipy in _system_of_equations --- pvlib/ivtools.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index a9164642f1..78cfe43e98 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -372,7 +372,7 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, # specs of module specs = ((i_sc, v_oc, i_mp, v_mp, beta_voc, alpha_sc, EgRef, dEgdT, - Tref),) + Tref, k),) # computing optimize_result = root(_system_of_equations, x0=params_i, args=specs) @@ -503,16 +503,9 @@ def _system_of_equations(params, specs): ------- system of equations to solve with scipy.optimize.root(). """ - try: - from scipy import constants - except ImportError: - raise ImportError("The fit_sdm_desoto function requires scipy.") - - # Constants - k = constants.value('Boltzmann constant in eV/K') # six input known variables - Isc, Voc, Imp, Vmp, betaoc, alphasc, EgRef, dEgdT, Tref = specs + Isc, Voc, Imp, Vmp, betaoc, alphasc, EgRef, dEgdT, Tref, k = specs # five parameters vector to find IL, Io, a, Rsh, Rs = params From fa41eb8085ee7a1d587daf5e2265088867f595f3 Mon Sep 17 00:00:00 2001 From: Lunel Date: Mon, 21 Oct 2019 20:06:14 -0400 Subject: [PATCH 39/46] - cleaning and minor modifications to docstring --- pvlib/ivtools.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index 78cfe43e98..9fc549de69 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -274,8 +274,9 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, with the corresponding default solver method 'hybr'. No restriction is put on the fit variables, i.e. series or shunt resistance could go negative. Nevertheless, if it happens, - check carefully the inputs and their units, alpha_sc and beta_voc are - often given in %/K in manufacturers datasheets. + check carefully the inputs and their units; alpha_sc and beta_voc are + often given in %/K in manufacturers datasheets and should be given + in A/K and V/K here. The parameters returned by this function can be used by pvsystem.calcparams_desoto to calculate the values at different irradiance and cell temperature. @@ -380,8 +381,8 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, if optimize_result.success: sdm_params = optimize_result.x else: - raise RuntimeError('Parameter estimation failed:\n' + - optimize_result.message) + raise RuntimeError('Parameter estimation failed:\n' + + optimize_result.message) # results return ({'I_L_ref': sdm_params[0], @@ -534,4 +535,3 @@ def _system_of_equations(params, specs): y[4] = -IL2 + Io2*np.expm1(Voc2/a2) + Voc2/Rsh # eq (4) at T2 return y - From 753d3123bd3baec55710f3a356497823836814db Mon Sep 17 00:00:00 2001 From: Lunel Date: Tue, 22 Oct 2019 10:12:34 -0400 Subject: [PATCH 40/46] - references added to docstring in _system_of_equations --- pvlib/ivtools.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index 9fc549de69..ab14259a17 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -375,7 +375,7 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, specs = ((i_sc, v_oc, i_mp, v_mp, beta_voc, alpha_sc, EgRef, dEgdT, Tref, k),) - # computing + # computing with system of equations described in [1] optimize_result = root(_system_of_equations, x0=params_i, args=specs) if optimize_result.success: @@ -503,6 +503,16 @@ def _system_of_equations(params, specs): Returns ------- system of equations to solve with scipy.optimize.root(). + + + References + ---------- + [1] W. De Soto et al., "Improvement and validation of a model for + photovoltaic array performance", Solar Energy, vol 80, pp. 78-88, + 2006. + + [2] John A Duffie, William A Beckman, "Solar Engineering of Thermal + Processes", Wiley, 2013 """ # six input known variables From b1b405ce94070fb3f7572562bb7575c37fcaff0e Mon Sep 17 00:00:00 2001 From: Lunel Date: Tue, 22 Oct 2019 15:03:45 -0400 Subject: [PATCH 41/46] - modification for removing last lint error --- pvlib/ivtools.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index ab14259a17..8d50b352bf 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -270,6 +270,7 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, procedure described in [1]. This procedure has the advantage of using common specifications given by manufacturers in the datasheets of PV modules. + The solution is found using the scipy.optimize.root() function, with the corresponding default solver method 'hybr'. No restriction is put on the fit variables, i.e. series @@ -277,6 +278,7 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, check carefully the inputs and their units; alpha_sc and beta_voc are often given in %/K in manufacturers datasheets and should be given in A/K and V/K here. + The parameters returned by this function can be used by pvsystem.calcparams_desoto to calculate the values at different irradiance and cell temperature. @@ -381,8 +383,8 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, if optimize_result.success: sdm_params = optimize_result.x else: - raise RuntimeError('Parameter estimation failed:\n' - + optimize_result.message) + raise RuntimeError( + 'Parameter estimation failed:\n' + optimize_result.message) # results return ({'I_L_ref': sdm_params[0], From 22d613cfc9b00efd6d3bb8cc22bc51f068d658a0 Mon Sep 17 00:00:00 2001 From: Lunel Date: Tue, 22 Oct 2019 15:03:45 -0400 Subject: [PATCH 42/46] - modification for removing last lint error --- pvlib/ivtools.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index ab14259a17..8d50b352bf 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -270,6 +270,7 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, procedure described in [1]. This procedure has the advantage of using common specifications given by manufacturers in the datasheets of PV modules. + The solution is found using the scipy.optimize.root() function, with the corresponding default solver method 'hybr'. No restriction is put on the fit variables, i.e. series @@ -277,6 +278,7 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, check carefully the inputs and their units; alpha_sc and beta_voc are often given in %/K in manufacturers datasheets and should be given in A/K and V/K here. + The parameters returned by this function can be used by pvsystem.calcparams_desoto to calculate the values at different irradiance and cell temperature. @@ -381,8 +383,8 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, if optimize_result.success: sdm_params = optimize_result.x else: - raise RuntimeError('Parameter estimation failed:\n' - + optimize_result.message) + raise RuntimeError( + 'Parameter estimation failed:\n' + optimize_result.message) # results return ({'I_L_ref': sdm_params[0], From ae19a684542b8c480021137a651c3fb1cfd907ae Mon Sep 17 00:00:00 2001 From: Lunel Date: Tue, 22 Oct 2019 15:06:31 -0400 Subject: [PATCH 43/46] - modification for removing lint error --- pvlib/ivtools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index 8d50b352bf..591e7fe885 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -384,7 +384,7 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, sdm_params = optimize_result.x else: raise RuntimeError( - 'Parameter estimation failed:\n' + optimize_result.message) + 'Parameter estimation failed:\n' + optimize_result.message) # results return ({'I_L_ref': sdm_params[0], From 05051e1fbe89dd73f1563731837326ade00149be Mon Sep 17 00:00:00 2001 From: Lunel Date: Mon, 28 Oct 2019 18:49:17 -0400 Subject: [PATCH 44/46] - integration of adriesse suggestions --- pvlib/ivtools.py | 60 +++++++++++++++++++++----------------- pvlib/test/test_ivtools.py | 10 +++---- 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/pvlib/ivtools.py b/pvlib/ivtools.py index 591e7fe885..4806b2ed16 100644 --- a/pvlib/ivtools.py +++ b/pvlib/ivtools.py @@ -264,7 +264,7 @@ def fit_sde_sandia(voltage, current, v_oc=None, i_sc=None, v_mp_i_mp=None, def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, cells_in_series, EgRef=1.121, dEgdT=-0.0002677, - temp_ref=25, irrad_ref=1000): + temp_ref=25, irrad_ref=1000, root_kwargs={}): """ Calculates the parameters for the De Soto single diode model using the procedure described in [1]. This procedure has the advantage of @@ -299,7 +299,7 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, beta_voc: float The open-circuit voltage (v_oc) temperature coefficient of the module [V/K]. - cells_in_series: float + cells_in_series: integer Number of cell in the module. EgRef: float, default 1.121 eV - value for silicon Energy of bandgap of semi-conductor used [eV] @@ -309,6 +309,8 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, Reference temperature condition [C] irrad_ref: float, default 1000 Reference irradiance condition [W/m2] + root_kwargs: dictionary, default None + Dictionary of arguments to pass onto scipy.optimize.root() Returns ------- @@ -365,20 +367,21 @@ def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, # initial guesses of variables for computing convergence: # Values are taken from [2], p753 - Rsh_i = 100.0 - a_i = 1.5*k*Tref*cells_in_series - IL_i = i_sc - Io_i = i_sc * np.exp(-v_oc/a_i) - Rs_i = (a_i*np.log1p((IL_i-i_mp)/Io_i) - v_mp)/i_mp + Rsh_0 = 100.0 + a_0 = 1.5*k*Tref*cells_in_series + IL_0 = i_sc + Io_0 = i_sc * np.exp(-v_oc/a_0) + Rs_0 = (a_0*np.log1p((IL_0-i_mp)/Io_0) - v_mp)/i_mp # params_i : initial values vector - params_i = np.array([IL_i, Io_i, a_i, Rsh_i, Rs_i]) + params_i = np.array([IL_0, Io_0, a_0, Rsh_0, Rs_0]) # specs of module - specs = ((i_sc, v_oc, i_mp, v_mp, beta_voc, alpha_sc, EgRef, dEgdT, - Tref, k),) + specs = (i_sc, v_oc, i_mp, v_mp, beta_voc, alpha_sc, EgRef, dEgdT, + Tref, k) # computing with system of equations described in [1] - optimize_result = root(_system_of_equations, x0=params_i, args=specs) + optimize_result = root(_system_of_equations_desoto, x0=params_i, + args=(specs,), **root_kwargs) if optimize_result.success: sdm_params = optimize_result.x @@ -488,7 +491,7 @@ def _calculate_sde_parameters(beta0, beta1, beta3, beta4, v_mp, i_mp, v_oc): return (IL, I0, Rsh, Rs, nNsVth) -def _system_of_equations(params, specs): +def _system_of_equations_desoto(params, specs): """Evaluates the systems of equations used to solve for the single diode equation parameters. Function designed to be used by scipy.optimize.root() in fit_sdm_desoto(). @@ -500,7 +503,7 @@ def _system_of_equations(params, specs): given in the following order: IL, Io, a, Rsh, Rs specs: tuple Specifications of pv module given by manufacturer. Must be given - in the following order: Isc, Voc, Imp, Vmp, betaoc, alphasc + in the following order: Isc, Voc, Imp, Vmp, beta_oc, alpha_sc Returns ------- @@ -518,7 +521,7 @@ def _system_of_equations(params, specs): """ # six input known variables - Isc, Voc, Imp, Vmp, betaoc, alphasc, EgRef, dEgdT, Tref, k = specs + Isc, Voc, Imp, Vmp, beta_oc, alpha_sc, EgRef, dEgdT, Tref, k = specs # five parameters vector to find IL, Io, a, Rsh, Rs = params @@ -527,23 +530,28 @@ def _system_of_equations(params, specs): y = [0, 0, 0, 0, 0] # 1st equation - short-circuit - eq(3) in [1] - y[0] = Isc - IL + Io*np.expm1(Isc*Rs/a) + Isc*Rs/Rsh + y[0] = Isc - IL + Io * np.expm1(Isc * Rs / a) + Isc * Rs / Rsh + # 2nd equation - open-circuit Tref - eq(4) in [1] - y[1] = -IL + Io*np.expm1(Voc/a) + Voc/Rsh + y[1] = -IL + Io * np.expm1(Voc / a) + Voc / Rsh + # 3rd equation - Imp & Vmp - eq(5) in [1] - y[2] = Imp - IL + Io*np.expm1((Vmp+Imp*Rs)/a) + \ - (Vmp+Imp*Rs)/Rsh + y[2] = Imp - IL + Io * np.expm1((Vmp + Imp * Rs) / a) \ + + (Vmp + Imp * Rs) / Rsh + # 4th equation - Pmp derivated=0 - eq23.2.6 in [2] # caution: eq(6) in [1] has a sign error - y[3] = Imp - Vmp * ((Io/a)*np.exp((Vmp+Imp*Rs)/a) + 1.0/Rsh) / \ - (1.0 + (Io*Rs/a)*np.exp((Vmp+Imp*Rs)/a) + Rs/Rsh) + y[3] = Imp \ + - Vmp * ((Io / a) * np.exp((Vmp + Imp * Rs) / a) + 1.0 / Rsh) \ + / (1.0 + (Io * Rs / a) * np.exp((Vmp + Imp * Rs) / a) + Rs / Rsh) + # 5th equation - open-circuit T2 - eq (4) at temperature T2 in [1] T2 = Tref + 2 - Voc2 = (T2 - Tref)*betaoc + Voc # eq (7) in [1] - a2 = a*T2/Tref # eq (8) in [1] - IL2 = IL + alphasc*(T2-Tref) # eq (11) in [1] - Eg2 = EgRef*(1 + dEgdT*(T2-Tref)) # eq (10) in [1] - Io2 = Io * (T2/Tref)**3 * np.exp(1/k * (EgRef/Tref-Eg2/T2)) # eq (9) - y[4] = -IL2 + Io2*np.expm1(Voc2/a2) + Voc2/Rsh # eq (4) at T2 + Voc2 = (T2 - Tref) * beta_oc + Voc # eq (7) in [1] + a2 = a * T2 / Tref # eq (8) in [1] + IL2 = IL + alpha_sc * (T2 - Tref) # eq (11) in [1] + Eg2 = EgRef * (1 + dEgdT * (T2 - Tref)) # eq (10) in [1] + Io2 = Io * (T2 / Tref)**3 * np.exp(1 / k * (EgRef/Tref - Eg2/T2)) # eq (9) + y[4] = -IL2 + Io2 * np.expm1(Voc2 / a2) + Voc2 / Rsh # eq (4) at T2 return y diff --git a/pvlib/test/test_ivtools.py b/pvlib/test/test_ivtools.py index 9096f060e9..9cb4ca34bb 100644 --- a/pvlib/test/test_ivtools.py +++ b/pvlib/test/test_ivtools.py @@ -108,11 +108,11 @@ def test_fit_sdm_desoto(): i_sc=9.43, alpha_sc=0.005658, beta_voc=-0.13788, cells_in_series=60) - result_expected = {'I_L_ref': 9.452324509050774, - 'I_o_ref': 3.2246097466679494e-10, - 'a_ref': 1.5912875522463978, - 'R_sh_ref': 125.79869968976132, - 'R_s': 0.2978148476600744, + result_expected = {'I_L_ref': 9.45232, + 'I_o_ref': 3.22460e-10, + 'a_ref': 1.59128, + 'R_sh_ref': 125.798, + 'R_s': 0.297814, 'alpha_sc': 0.005658, 'EgRef': 1.121, 'dEgdT': -0.0002677, From d8214942d10d2d6a38b2417e15a5c45df0d28057 Mon Sep 17 00:00:00 2001 From: Lunel Date: Tue, 29 Oct 2019 10:45:31 -0400 Subject: [PATCH 45/46] - adding of tylunel to the list of contributors --- docs/sphinx/source/whatsnew/v0.7.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index 910b3ff78c..aa8e8e03fe 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -148,3 +148,4 @@ Contributors * Anton Driesse (:ghuser:`adriesse`) * Alexander Morgan (:ghuser:`alexandermorgan`) * Miguel Sánchez de León Peque (:ghuser:`Peque`) +* Tanguy Lunel (:ghuser:`tylunel`) From 5037da6ca17abf7769e2a1bee5f4b436c47cc920 Mon Sep 17 00:00:00 2001 From: Lunel Date: Tue, 29 Oct 2019 15:06:05 -0400 Subject: [PATCH 46/46] - adding of mark requires_scipy --- pvlib/test/test_ivtools.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pvlib/test/test_ivtools.py b/pvlib/test/test_ivtools.py index 9cb4ca34bb..6f9fdd6fec 100644 --- a/pvlib/test/test_ivtools.py +++ b/pvlib/test/test_ivtools.py @@ -122,6 +122,7 @@ def test_fit_sdm_desoto(): rtol=1e-4) +@requires_scipy def test_fit_sdm_desoto_failure(): with pytest.raises(RuntimeError) as exc: ivtools.fit_sdm_desoto(v_mp=31.0, i_mp=8.71, v_oc=38.3, i_sc=9.43,