Skip to content

Commit ef87465

Browse files
committed
adapt to new organization
1 parent 30cda13 commit ef87465

2 files changed

Lines changed: 384 additions & 0 deletions

File tree

pvlib/ivtools/convert.py

Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
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+
}

tests/ivtools/test_convert.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import numpy as np
2+
from pvlib.ivtools import sdm
3+
4+
5+
def test_convert_cec_pvsyst():
6+
cells_in_series = 66
7+
trina660_cec = {'I_L_ref': 18.4759, 'I_o_ref': 5.31e-12,
8+
'EgRef': 1.121, 'dEgdT': -0.0002677,
9+
'R_s': 0.159916, 'R_sh_ref': 113.991, 'a_ref': 1.59068,
10+
'Adjust': 6.42247, 'alpha_sc': 0.00629}
11+
trina660_pvsyst_est = sdm.convert_cec_pvsyst(trina660_cec,
12+
cells_in_series)
13+
pvsyst_expected = {'alpha_sc': 0.007478218748188788,
14+
'I_L_ref': 18.227679597516214,
15+
'I_o_ref': 2.7418999402908e-11,
16+
'EgRef': 1.121,
17+
'R_s': 0.16331908293164496,
18+
'R_sh_ref': 5267.928954454954,
19+
'R_sh_0': 60171.206687871425,
20+
'R_sh_exp': 5.5,
21+
'gamma_ref': 1.0,
22+
'mu_gamma': -6.349173477135307e-05,
23+
'cells_in_series': 66}
24+
25+
assert np.all([np.isclose(trina660_pvsyst_est[k], pvsyst_expected[k],
26+
rtol=1e-3)
27+
for k in pvsyst_expected])
28+
29+
30+
def test_convert_pvsyst_cec():
31+
trina660_pvsyst = {'alpha_sc': 0.0074, 'I_o_ref': 3.3e-11, 'EgRef': 1.121,
32+
'R_s': 0.156, 'R_sh_ref': 200, 'R_sh_0': 800,
33+
'R_sh_exp': 5.5, 'gamma_ref': 1.002, 'mu_gamma': 1e-3,
34+
'cells_in_series': 66}
35+
trina660_cec_est = sdm.convert_pvsyst_cec(trina660_pvsyst)
36+
cec_expected = {'alpha_sc': 0.0074,
37+
'I_L_ref': 18.05154226834071,
38+
'I_o_ref': 2.6863417875143392e-14,
39+
'EgRef': 1.121,
40+
'dEgdT': -0.0002677,
41+
'R_s': 0.09436341848926795,
42+
'a_ref': 1.2954800250731866,
43+
'Adjust': 0.0011675969492410047,
44+
'cells_in_series': 66}
45+
46+
assert np.all([np.isclose(trina660_cec_est[k], cec_expected[k],
47+
rtol=1e-3)
48+
for k in cec_expected])

0 commit comments

Comments
 (0)