|
| 1 | +""" |
| 2 | +The ``sdm`` module contains functions to fit single diode models. |
| 3 | +
|
| 4 | +Function names should follow the pattern "fit_" + name of model + "_" + |
| 5 | + fitting method. |
| 6 | +
|
| 7 | +""" |
| 8 | + |
| 9 | +import numpy as np |
| 10 | +import pandas as pd |
| 11 | + |
| 12 | +from scipy import constants |
| 13 | +from scipy import optimize |
| 14 | + |
| 15 | +from pvlib.pvsystem import (calcparams_pvsyst, calcparams_cec, singlediode) |
| 16 | + |
| 17 | + |
| 18 | +CONSTANTS = {'E0': 1000.0, 'T0': 25.0, 'k': constants.k, 'q': constants.e} |
| 19 | + |
| 20 | + |
| 21 | +IEC61853 = pd.DataFrame( |
| 22 | + columns=['effective_irradiance', 'temp_cell'], |
| 23 | + data=np.array( |
| 24 | + [[100, 100, 100, 100, 200, 200, 200, 200, 400, 400, 400, 400, |
| 25 | + 600, 600, 600, 600, 800, 800, 800, 800, 1000, 1000, 1000, 1000, |
| 26 | + 1100, 1100, 1100, 1100], |
| 27 | + [15, 25, 50, 75, 15, 25, 50, 75, 15, 25, 50, 75, 15, 25, 50, 75, |
| 28 | + 15, 25, 50, 75, 15, 25, 50, 75, 15, 25, 50, 75]]).T, |
| 29 | + dtype=np.float64) |
| 30 | + |
| 31 | + |
| 32 | + |
| 33 | +def _pvsyst_objfun(pvs_mod, cec_ivs, ee, tc, cs): |
| 34 | + |
| 35 | + # translate the guess into named args that are used in the functions |
| 36 | + # order : [alpha_sc, gamma_ref, mu_gamma, I_L_ref, I_o_ref, |
| 37 | + # R_sh_mult, R_sh_ref, R_s] |
| 38 | + # cec_ivs : DataFrame with columns i_sc, v_oc, i_mp, v_mp, p_mp |
| 39 | + # ee : effective irradiance |
| 40 | + # tc : cell temperature |
| 41 | + # cs : cells in series |
| 42 | + alpha_sc = pvs_mod[0] |
| 43 | + gamma_ref = pvs_mod[1] |
| 44 | + mu_gamma = pvs_mod[2] |
| 45 | + I_L_ref = pvs_mod[3] |
| 46 | + I_o_ref = pvs_mod[4] |
| 47 | + R_sh_mult = pvs_mod[5] |
| 48 | + R_sh_ref = pvs_mod[6] |
| 49 | + R_s = pvs_mod[7] |
| 50 | + |
| 51 | + R_sh_0 = R_sh_ref * R_sh_mult |
| 52 | + |
| 53 | + pvs_params = calcparams_pvsyst( |
| 54 | + ee, tc, alpha_sc, gamma_ref, mu_gamma, I_L_ref, I_o_ref, R_sh_ref, |
| 55 | + R_sh_0, R_s, cs) |
| 56 | + |
| 57 | + pvsyst_ivs = singlediode(*pvs_params) |
| 58 | + |
| 59 | + isc_diff = np.abs((pvsyst_ivs['i_sc'] - cec_ivs['i_sc']) / |
| 60 | + cec_ivs['i_sc']).mean() |
| 61 | + imp_diff = np.abs((pvsyst_ivs['i_mp'] - cec_ivs['i_mp']) / |
| 62 | + cec_ivs['i_mp']).mean() |
| 63 | + voc_diff = np.abs((pvsyst_ivs['v_oc'] - cec_ivs['v_oc']) / |
| 64 | + cec_ivs['v_oc']).mean() |
| 65 | + vmp_diff = np.abs((pvsyst_ivs['v_mp'] - cec_ivs['v_mp']) / |
| 66 | + cec_ivs['v_mp']).mean() |
| 67 | + pmp_diff = np.abs((pvsyst_ivs['p_mp'] - cec_ivs['p_mp']) / |
| 68 | + cec_ivs['p_mp']).mean() |
| 69 | + |
| 70 | + mean_abs_diff = (isc_diff + imp_diff + voc_diff + vmp_diff + pmp_diff) / 5 |
| 71 | + |
| 72 | + return mean_abs_diff |
| 73 | + |
| 74 | + |
| 75 | +def convert_cec_pvsyst(cec_model, cells_in_series, method='Nelder-Mead', |
| 76 | + options=None): |
| 77 | + r""" |
| 78 | + Convert a set of CEC model parameters to an equivalent set of PVsyst model |
| 79 | + parameters. |
| 80 | +
|
| 81 | + Parameter conversion uses optimization as described in [1]_ to fit the |
| 82 | + PVsyst model to :math:`I_{sc}`, :math:`V_{oc}`, :math:`V_{mp}`, |
| 83 | + :math:`I_{mp}`, and :math:`P_{mp}`, calculated using the input CEC model |
| 84 | + at the IEC 61853-3 conditions [2]_. |
| 85 | +
|
| 86 | + Parameters |
| 87 | + ---------- |
| 88 | + cec_model : dict or DataFrame |
| 89 | + Must include keys: 'alpha_sc', 'a_ref', 'I_L_ref', 'I_o_ref', |
| 90 | + 'R_sh_ref', 'R_s', 'Adjust' |
| 91 | + cell_in_series : int |
| 92 | + Number of cells in series. |
| 93 | + method : str, default 'Nelder-Mead' |
| 94 | + Method for scipy.optimize.minimize. |
| 95 | + options : dict, optional |
| 96 | + Solver options passed to scipy.optimize.minimize. |
| 97 | +
|
| 98 | + Returns |
| 99 | + ------- |
| 100 | + dict with the following elements: |
| 101 | + alpha_sc : float |
| 102 | + Short-circuit current temperature coefficient [A/C] . |
| 103 | + I_L_ref : float |
| 104 | + The light-generated current (or photocurrent) at reference |
| 105 | + conditions [A]. |
| 106 | + I_o_ref : float |
| 107 | + The dark or diode reverse saturation current at reference |
| 108 | + conditions [A]. |
| 109 | + EgRef : float |
| 110 | + The energy bandgap at reference temperature [eV]. |
| 111 | + R_s : float |
| 112 | + The series resistance at reference conditions [ohm]. |
| 113 | + R_sh_ref : float |
| 114 | + The shunt resistance at reference conditions [ohm]. |
| 115 | + R_sh_0 : float |
| 116 | + Shunt resistance at zero irradiance [ohm]. |
| 117 | + R_sh_exp : float |
| 118 | + Exponential factor defining decrease in shunt resistance with |
| 119 | + increasing effective irradiance [unitless]. |
| 120 | + gamma_ref : float |
| 121 | + Diode (ideality) factor at reference conditions [unitless]. |
| 122 | + mu_gamma : float |
| 123 | + Temperature coefficient for diode (ideality) factor at reference |
| 124 | + conditions [1/K]. |
| 125 | + cells_in_series : int |
| 126 | + Number of cells in series. |
| 127 | +
|
| 128 | + Notes |
| 129 | + ----- |
| 130 | + Reference conditions are irradiance of 1000 W/m⁻² and cell temperature of |
| 131 | + 25 °C. |
| 132 | +
|
| 133 | + See Also |
| 134 | + -------- |
| 135 | + pvlib.ivtools.sdm.convert_pvsyst_cec |
| 136 | +
|
| 137 | + References |
| 138 | + ---------- |
| 139 | + .. [1] L. Deville et al., "Parameter Translation for Photovoltaic Single |
| 140 | + Diode Models", submitted. 2024 |
| 141 | +
|
| 142 | + .. [2] "IEC 61853-3 Photovoltaic (PV) module performance testing and energy |
| 143 | + rating - Part 3: Energy rating of PV modules". IEC, Geneva, 2018. |
| 144 | + """ |
| 145 | + if options is None: |
| 146 | + options = {'maxiter': 5000, 'maxfev': 5000, 'xatol': 0.001} |
| 147 | + |
| 148 | + # calculate target IV curve values |
| 149 | + cec_params = calcparams_cec( |
| 150 | + IEC61853['effective_irradiance'], |
| 151 | + IEC61853['temp_cell'], |
| 152 | + **cec_model) |
| 153 | + cec_ivs = singlediode(*cec_params) |
| 154 | + |
| 155 | + # initial guess at PVsyst parameters |
| 156 | + # Order in list is alpha_sc, gamma_ref, mu_gamma, I_L_ref, I_o_ref, |
| 157 | + # Rsh_mult = R_sh_0 / R_sh_ref, R_sh_ref, R_s |
| 158 | + initial = [0, 1.2, 0.001, cec_model['I_L_ref'], cec_model['I_o_ref'], |
| 159 | + 12, 1000, cec_model['R_s']] |
| 160 | + |
| 161 | + # bounds for PVsyst parameters |
| 162 | + b_alpha = (-1, 1) |
| 163 | + b_gamma = (1, 2) |
| 164 | + b_mu = (-1, 1) |
| 165 | + b_IL = (1e-12, 100) |
| 166 | + b_Io = (1e-24, 0.1) |
| 167 | + b_Rmult = (1, 20) |
| 168 | + b_Rsh = (100, 1e6) |
| 169 | + b_Rs = (1e-12, 10) |
| 170 | + bounds = [b_alpha, b_gamma, b_mu, b_IL, b_Io, b_Rmult, b_Rsh, b_Rs] |
| 171 | + |
| 172 | + # optimization to find PVsyst parameters |
| 173 | + result = optimize.minimize( |
| 174 | + _pvsyst_objfun, initial, |
| 175 | + args=(cec_ivs, IEC61853['effective_irradiance'], |
| 176 | + IEC61853['temp_cell'], cells_in_series), |
| 177 | + method='Nelder-Mead', |
| 178 | + bounds=bounds, |
| 179 | + options=options) |
| 180 | + |
| 181 | + alpha_sc, gamma, mu_gamma, I_L_ref, I_o_ref, Rsh_mult, R_sh_ref, R_s = \ |
| 182 | + result.x |
| 183 | + |
| 184 | + R_sh_0 = Rsh_mult * R_sh_ref |
| 185 | + R_sh_exp = 5.5 |
| 186 | + EgRef = 1.121 # default for all modules in the CEC model |
| 187 | + return {'alpha_sc': alpha_sc, |
| 188 | + 'I_L_ref': I_L_ref, 'I_o_ref': I_o_ref, 'EgRef': EgRef, 'R_s': R_s, |
| 189 | + 'R_sh_ref': R_sh_ref, 'R_sh_0': R_sh_0, 'R_sh_exp': R_sh_exp, |
| 190 | + 'gamma_ref': gamma, 'mu_gamma': mu_gamma, |
| 191 | + 'cells_in_series': cells_in_series, |
| 192 | + } |
| 193 | + |
| 194 | + |
| 195 | +def _cec_objfun(cec_mod, pvs_ivs, ee, tc, alpha_sc): |
| 196 | + # translate the guess into named args that are used in the functions |
| 197 | + # order : [I_L_ref, I_o_ref, a_ref, R_sh_ref, R_s, alpha_sc, Adjust] |
| 198 | + # pvs_ivs : DataFrame with columns i_sc, v_oc, i_mp, v_mp, p_mp |
| 199 | + # ee : effective irradiance |
| 200 | + # tc : cell temperature |
| 201 | + # alpha_sc : temperature coefficient for Isc |
| 202 | + I_L_ref = cec_mod[0] |
| 203 | + I_o_ref = cec_mod[1] |
| 204 | + a_ref = cec_mod[2] |
| 205 | + R_sh_ref = cec_mod[3] |
| 206 | + R_s = cec_mod[4] |
| 207 | + Adjust = cec_mod[5] |
| 208 | + alpha_sc = alpha_sc |
| 209 | + |
| 210 | + cec_params = calcparams_cec( |
| 211 | + ee, tc, alpha_sc, a_ref, I_L_ref, I_o_ref, R_sh_ref, R_s, Adjust) |
| 212 | + cec_ivs = singlediode(*cec_params) |
| 213 | + |
| 214 | + isc_rss = np.sqrt(sum((cec_ivs['i_sc'] - pvs_ivs['i_sc'])**2)) |
| 215 | + imp_rss = np.sqrt(sum((cec_ivs['i_mp'] - pvs_ivs['i_mp'])**2)) |
| 216 | + voc_rss = np.sqrt(sum((cec_ivs['v_oc'] - pvs_ivs['v_oc'])**2)) |
| 217 | + vmp_rss = np.sqrt(sum((cec_ivs['v_mp'] - pvs_ivs['v_mp'])**2)) |
| 218 | + pmp_rss = np.sqrt(sum((cec_ivs['p_mp'] - pvs_ivs['p_mp'])**2)) |
| 219 | + |
| 220 | + mean_diff = (isc_rss+imp_rss+voc_rss+vmp_rss+pmp_rss) / 5 |
| 221 | + |
| 222 | + return mean_diff |
| 223 | + |
| 224 | + |
| 225 | +def convert_pvsyst_cec(pvsyst_model, method='Nelder-Mead', options=None): |
| 226 | + r""" |
| 227 | + Convert a set of PVsyst model parameters to an equivalent set of CEC model |
| 228 | + parameters. |
| 229 | +
|
| 230 | + Parameter conversion uses optimization as described in [1]_ to fit the |
| 231 | + CEC model to :math:`I_{sc}`, :math:`V_{oc}`, :math:`V_{mp}`, |
| 232 | + :math:`I_{mp}`, and :math:`P_{mp}`, calculated using the input PVsyst model |
| 233 | + at the IEC 61853-3 conditions [2]_. |
| 234 | +
|
| 235 | + Parameters |
| 236 | + ---------- |
| 237 | + pvsyst_model : dict or DataFrame |
| 238 | + Must include keys: 'alpha_sc', 'I_L_ref', 'I_o_ref', 'EgRef', 'R_s', |
| 239 | + 'R_sh_ref', 'R_sh_0', 'R_sh_exp', 'gamma_ref', 'mu_gamma', |
| 240 | + 'cells_in_series' |
| 241 | + method : str, default 'Nelder-Mead' |
| 242 | + Method for scipy.optimize.minimize. |
| 243 | + options : dict, optional |
| 244 | + Solver options passed to scipy.optimize.minimize. |
| 245 | +
|
| 246 | + Returns |
| 247 | + ------- |
| 248 | + dict with the following elements: |
| 249 | + I_L_ref : float |
| 250 | + The light-generated current (or photocurrent) at reference |
| 251 | + conditions [A]. |
| 252 | + I_o_ref : float |
| 253 | + The dark or diode reverse saturation current at reference |
| 254 | + conditions [A]. |
| 255 | + R_s : float |
| 256 | + The series resistance at reference conditions [ohm]. |
| 257 | + R_sh_ref : float |
| 258 | + The shunt resistance at reference conditions [ohm]. |
| 259 | + a_ref : float |
| 260 | + The product of the usual diode ideality factor ``n`` (unitless), |
| 261 | + number of cells in series ``Ns``, and cell thermal voltage at |
| 262 | + reference conditions [V]. |
| 263 | + Adjust : float |
| 264 | + The adjustment to the temperature coefficient for short circuit |
| 265 | + current, in percent. |
| 266 | + EgRef : float |
| 267 | + The energy bandgap at reference temperature [eV]. |
| 268 | + dEgdT : float |
| 269 | + The temperature dependence of the energy bandgap at reference |
| 270 | + conditions [1/K]. |
| 271 | +
|
| 272 | + Notes |
| 273 | + ----- |
| 274 | + Reference conditions are irradiance of 1000 W/m⁻² and cell temperature of |
| 275 | + 25 °C. |
| 276 | +
|
| 277 | + See Also |
| 278 | + -------- |
| 279 | + pvlib.ivtools.sdm.convert_cec_pvsyst |
| 280 | +
|
| 281 | + References |
| 282 | + ---------- |
| 283 | + .. [1] L. Deville et al., "Parameter Translation for Photovoltaic Single |
| 284 | + Diode Models", submitted. 2024. |
| 285 | +
|
| 286 | + .. [2] "IEC 61853-3 Photovoltaic (PV) module performance testing and energy |
| 287 | + rating - Part 3: Energy rating of PV modules". IEC, Geneva, 2018. |
| 288 | + """ |
| 289 | + |
| 290 | + if options is None: |
| 291 | + options = {'maxiter': 5000, 'maxfev': 5000, 'xatol': 0.001} |
| 292 | + |
| 293 | + # calculate target IV curve values |
| 294 | + pvs_params = calcparams_pvsyst( |
| 295 | + IEC61853['effective_irradiance'], |
| 296 | + IEC61853['temp_cell'], |
| 297 | + **pvsyst_model) |
| 298 | + pvsyst_ivs = singlediode(*pvs_params) |
| 299 | + |
| 300 | + # set EgRef and dEgdT to CEC defaults |
| 301 | + EgRef = 1.121 |
| 302 | + dEgdT = -0.0002677 |
| 303 | + |
| 304 | + # initial guess |
| 305 | + # order must match _pvsyst_objfun |
| 306 | + # order : [I_L_ref, I_o_ref, a_ref, R_sh_ref, R_s, alpha_sc, Adjust] |
| 307 | + nNsVth = pvsyst_model['gamma_ref'] * pvsyst_model['cells_in_series'] \ |
| 308 | + * 0.025 |
| 309 | + initial = [pvsyst_model['I_L_ref'], pvsyst_model['I_o_ref'], |
| 310 | + nNsVth, pvsyst_model['R_sh_ref'], pvsyst_model['R_s'], |
| 311 | + 0] |
| 312 | + |
| 313 | + # bounds for PVsyst parameters |
| 314 | + b_IL = (1e-12, 100) |
| 315 | + b_Io = (1e-24, 0.1) |
| 316 | + b_aref = (1e-12, 1000) |
| 317 | + b_Rsh = (100, 1e6) |
| 318 | + b_Rs = (1e-12, 10) |
| 319 | + b_Adjust = (-100, 100) |
| 320 | + bounds = [b_IL, b_Io, b_aref, b_Rsh, b_Rs, b_Adjust] |
| 321 | + |
| 322 | + result = optimize.minimize( |
| 323 | + _cec_objfun, initial, |
| 324 | + args=(pvsyst_ivs, IEC61853['effective_irradiance'], |
| 325 | + IEC61853['temp_cell'], pvsyst_model['alpha_sc']), |
| 326 | + method='Nelder-Mead', |
| 327 | + bounds=bounds, |
| 328 | + options=options) |
| 329 | + |
| 330 | + I_L_ref, I_o_ref, a_ref, R_sh_ref, R_s, Adjust = result.x |
| 331 | + |
| 332 | + return {'alpha_sc': pvsyst_model['alpha_sc'], |
| 333 | + 'a_ref': a_ref, 'I_L_ref': I_L_ref, 'I_o_ref': I_o_ref, |
| 334 | + 'R_sh_ref': R_sh_ref, 'R_s': R_s, 'Adjust': Adjust, |
| 335 | + 'EgRef': EgRef, 'dEgdT': dEgdT |
| 336 | + } |
0 commit comments