Skip to content

Commit 891778c

Browse files
cwhanseRDaxinikandersolar
authored
Add optional initial guess to fit_desoto (#2291)
* add option to provide init guess * remove whitespace * whatsnew * Apply suggestions from code review Co-authored-by: RDaxini <[email protected]> * accomodate partial initial dict * no slash * fix array constructor * two k's * mocker test * fix mocker * change initial condition * forgot a_0 * finetune initial guess * cherry pick the parameter to check * kwarg * nope * try this * try splatting * add explicit args * use a tuple * aha * use explicit? * wrong Boltzmann * use pytest.approx * back to ANY * drop comment * without the tuple? * periods and superscript * Apply suggestions from code review Co-authored-by: Kevin Anderson <[email protected]> * ValueError and new test --------- Co-authored-by: RDaxini <[email protected]> Co-authored-by: Kevin Anderson <[email protected]>
1 parent afdcdd4 commit 891778c

File tree

3 files changed

+76
-39
lines changed

3 files changed

+76
-39
lines changed

docs/sphinx/source/whatsnew/v0.11.2.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Deprecations
1010

1111
Enhancements
1212
~~~~~~~~~~~~
13+
* :py:func:`~pvlib.ivtools.sdm.fit_desoto` now allows input of initial
14+
parameter guesses. (:issue:`1014`, :pull:`2291`)
1315

1416
Bug Fixes
1517
~~~~~~~~~

pvlib/ivtools/sdm.py

Lines changed: 54 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -120,107 +120,123 @@ def fit_cec_sam(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc,
120120

121121
def fit_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, cells_in_series,
122122
EgRef=1.121, dEgdT=-0.0002677, temp_ref=25, irrad_ref=1000,
123-
root_kwargs={}):
123+
init_guess={}, root_kwargs={}):
124124
"""
125125
Calculates the parameters for the De Soto single diode model.
126126
127-
This procedure (described in [1]_) has the advantage of
128-
using common specifications given by manufacturers in the
127+
This procedure (described in [1]_) fits the De Soto model [2]_ using
128+
common specifications given by manufacturers in the
129129
datasheets of PV modules.
130130
131-
The solution is found using the scipy.optimize.root() function,
132-
with the corresponding default solver method 'hybr'.
133-
No restriction is put on the fit variables, i.e. series
131+
The solution is found using :py:func:`scipy.optimize.root`,
132+
with the default solver method 'hybr'.
133+
No restriction is put on the fit variables, e.g. series
134134
or shunt resistance could go negative. Nevertheless, if it happens,
135-
check carefully the inputs and their units; alpha_sc and beta_voc are
136-
often given in %/K in manufacturers datasheets and should be given
137-
in A/K and V/K here.
135+
check carefully the inputs and their units. For example, ``alpha_sc`` and
136+
``beta_voc`` are often given in %/K in manufacturers datasheets but should
137+
be given in A/K and V/K here.
138138
139139
The parameters returned by this function can be used by
140-
:py:func:`pvlib.pvsystem.calcparams_desoto` to calculate the values at
141-
different irradiance and cell temperature.
140+
:py:func:`pvlib.pvsystem.calcparams_desoto` to calculate single diode
141+
equation parameters at different irradiance and cell temperature.
142142
143143
Parameters
144144
----------
145145
v_mp: float
146-
Module voltage at the maximum-power point at reference conditions [V].
146+
Module voltage at the maximum-power point at reference conditions. [V]
147147
i_mp: float
148-
Module current at the maximum-power point at reference conditions [A].
148+
Module current at the maximum-power point at reference conditions. [A]
149149
v_oc: float
150-
Open-circuit voltage at reference conditions [V].
150+
Open-circuit voltage at reference conditions. [V]
151151
i_sc: float
152-
Short-circuit current at reference conditions [A].
152+
Short-circuit current at reference conditions. [A]
153153
alpha_sc: float
154154
The short-circuit current (i_sc) temperature coefficient of the
155-
module [A/K].
155+
module. [A/K]
156156
beta_voc: float
157157
The open-circuit voltage (v_oc) temperature coefficient of the
158-
module [V/K].
158+
module. [V/K]
159159
cells_in_series: integer
160160
Number of cell in the module.
161161
EgRef: float, default 1.121 eV - value for silicon
162-
Energy of bandgap of semi-conductor used [eV]
162+
Energy of bandgap of semi-conductor used. [eV]
163163
dEgdT: float, default -0.0002677 - value for silicon
164-
Variation of bandgap according to temperature [eV/K]
164+
Variation of bandgap according to temperature. [eV/K]
165165
temp_ref: float, default 25
166-
Reference temperature condition [C]
166+
Reference temperature condition. [C]
167167
irrad_ref: float, default 1000
168-
Reference irradiance condition [W/m2]
168+
Reference irradiance condition. [Wm⁻²]
169+
init_guess: dict, optional
170+
Initial values for optimization. Keys can be `'Rsh_0'`, `'a_0'`,
171+
`'IL_0'`, `'Io_0'`, `'Rs_0'`.
169172
root_kwargs : dictionary, optional
170173
Dictionary of arguments to pass onto scipy.optimize.root()
171174
172175
Returns
173176
-------
174177
dict with the following elements:
175178
I_L_ref: float
176-
Light-generated current at reference conditions [A]
179+
Light-generated current at reference conditions. [A]
177180
I_o_ref: float
178-
Diode saturation current at reference conditions [A]
181+
Diode saturation current at reference conditions. [A]
179182
R_s: float
180-
Series resistance [ohm]
183+
Series resistance. [ohm]
181184
R_sh_ref: float
182-
Shunt resistance at reference conditions [ohm].
185+
Shunt resistance at reference conditions. [ohm].
183186
a_ref: float
184187
Modified ideality factor at reference conditions.
185188
The product of the usual diode ideality factor (n, unitless),
186189
number of cells in series (Ns), and cell thermal voltage at
187190
specified effective irradiance and cell temperature.
188191
alpha_sc: float
189192
The short-circuit current (i_sc) temperature coefficient of the
190-
module [A/K].
193+
module. [A/K]
191194
EgRef: float
192-
Energy of bandgap of semi-conductor used [eV]
195+
Energy of bandgap of semi-conductor used. [eV]
193196
dEgdT: float
194-
Variation of bandgap according to temperature [eV/K]
197+
Variation of bandgap according to temperature. [eV/K]
195198
irrad_ref: float
196-
Reference irradiance condition [W/m2]
199+
Reference irradiance condition. [Wm⁻²]
197200
temp_ref: float
198-
Reference temperature condition [C]
201+
Reference temperature condition. [C]
199202
200203
scipy.optimize.OptimizeResult
201204
Optimization result of scipy.optimize.root().
202205
See scipy.optimize.OptimizeResult for more details.
203206
204207
References
205208
----------
206-
.. [1] W. De Soto et al., "Improvement and validation of a model for
209+
.. [1] J. A Duffie, W. A Beckman, "Solar Engineering of Thermal Processes",
210+
4th ed., Wiley, 2013. :doi:`10.1002/9781118671603`
211+
.. [2] W. De Soto et al., "Improvement and validation of a model for
207212
photovoltaic array performance", Solar Energy, vol 80, pp. 78-88,
208213
2006. :doi:`10.1016/j.solener.2005.06.010`
214+
209215
"""
210216

211217
# Constants
212218
k = constants.value('Boltzmann constant in eV/K') # in eV/K
213219
Tref = temp_ref + 273.15 # [K]
214220

215221
# initial guesses of variables for computing convergence:
216-
# Values are taken from [2], p753
217-
Rsh_0 = 100.0
218-
a_0 = 1.5*k*Tref*cells_in_series
219-
IL_0 = i_sc
220-
Io_0 = i_sc * np.exp(-v_oc/a_0)
221-
Rs_0 = (a_0*np.log1p((IL_0-i_mp)/Io_0) - v_mp)/i_mp
222+
# Default values are taken from [1], p753
223+
init_guess_keys = ['IL_0', 'Io_0', 'Rs_0', 'Rsh_0', 'a_0'] # order matters
224+
init = {key: None for key in init_guess_keys}
225+
init['IL_0'] = i_sc
226+
init['a_0'] = 1.5*k*Tref*cells_in_series
227+
init['Io_0'] = i_sc * np.exp(-v_oc/init['a_0'])
228+
init['Rs_0'] = (init['a_0']*np.log1p((init['IL_0'] - i_mp)/init['Io_0'])
229+
- v_mp) / i_mp
230+
init['Rsh_0'] = 100.0
231+
# overwrite if optional init_guess is provided
232+
for key in init_guess:
233+
if key in init_guess_keys:
234+
init[key] = init_guess[key]
235+
else:
236+
raise ValueError(f"'{key}' is not a valid name;"
237+
f" allowed values are {init_guess_keys}")
222238
# params_i : initial values vector
223-
params_i = np.array([IL_0, Io_0, Rs_0, Rsh_0, a_0])
239+
params_i = np.array([init[k] for k in init_guess_keys])
224240

225241
# specs of module
226242
specs = (i_sc, v_oc, i_mp, v_mp, beta_voc, alpha_sc, EgRef, dEgdT,

pvlib/tests/ivtools/test_sdm.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import numpy as np
22
import pandas as pd
3+
from scipy import optimize
34

45
import pytest
56
from numpy.testing import assert_allclose
67

78
from pvlib.ivtools import sdm
89
from pvlib import pvsystem
9-
from pvlib._deprecation import pvlibDeprecationWarning
1010

1111
from pvlib.tests.conftest import requires_pysam, requires_statsmodels
1212

@@ -80,6 +80,25 @@ def test_fit_desoto():
8080
rtol=1e-4)
8181

8282

83+
def test_fit_desoto_init_guess(mocker):
84+
init_guess_array = np.array([9.4, 3.0e-10, 0.3, 125., 1.6])
85+
init_guess = {k: v for k, v in zip(
86+
['IL_0', 'Io_0', 'Rs_0', 'Rsh_0', 'a_0'], init_guess_array)}
87+
spy = mocker.spy(optimize, 'root')
88+
result, _ = sdm.fit_desoto(v_mp=31.0, i_mp=8.71, v_oc=38.3, i_sc=9.43,
89+
alpha_sc=0.005658, beta_voc=-0.13788,
90+
cells_in_series=60, init_guess=init_guess)
91+
np.testing.assert_array_equal(init_guess_array, spy.call_args[1]['x0'])
92+
93+
94+
def test_fit_desoto_init_bad_key():
95+
init_guess = {'IL_0': 6., 'bad_key': 0}
96+
with pytest.raises(ValueError, match='is not a valid name;'):
97+
result, _ = sdm.fit_desoto(v_mp=31.0, i_mp=8.71, v_oc=38.3, i_sc=9.43,
98+
alpha_sc=0.005658, beta_voc=-0.13788,
99+
cells_in_series=60, init_guess=init_guess)
100+
101+
83102
def test_fit_desoto_failure():
84103
with pytest.raises(RuntimeError) as exc:
85104
sdm.fit_desoto(v_mp=31.0, i_mp=8.71, v_oc=38.3, i_sc=9.43,

0 commit comments

Comments
 (0)