Skip to content

Commit 1f73af6

Browse files
authored
Fix numerical overflow in v_from_i for some conditions (#226)
* implement log lambertw calculation in v_from_i * add test * more robust * add note to whatsnew * add issue note to test * move the .real * remove scipy version in appveyor config
1 parent 85e53af commit 1f73af6

File tree

4 files changed

+36
-11
lines changed

4 files changed

+36
-11
lines changed

appveyor.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ install:
2727
- cmd: conda info -a
2828

2929
# install depenencies
30-
- cmd: conda create -n test_env --yes --quiet python=%PYTHON_VERSION% pip numpy scipy=0.16.0 pandas nose pytest pytz ephem numba
30+
- cmd: conda create -n test_env --yes --quiet python=%PYTHON_VERSION% pip numpy scipy pandas nose pytest pytz ephem numba
3131
- cmd: activate test_env
3232
- cmd: python --version
3333
- cmd: conda list

docs/sphinx/source/whatsnew/v0.4.0.txt

+2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ Bug fixes
6565
rather than requiring that the solution for all points be within the
6666
error tolerance. Results in test scenarios changed by 1-10%.
6767
(:issue:`221`)
68+
* Fixed a numerical overflow error in pvsystem.singlediode's v_oc
69+
determination for some combinations of parameters. (:issue:`225`)
6870
* dirint function yielded the wrong results for non-sea-level pressures.
6971
Fixed. (:issue:`212`)
7072
* Fixed a bug in the day angle calculation used by the 'spencer' and 'asce'

pvlib/pvsystem.py

+21-6
Original file line numberDiff line numberDiff line change
@@ -1827,14 +1827,29 @@ def v_from_i(resistance_shunt, resistance_series, nNsVth, current,
18271827
IL = photocurrent
18281828
I = current
18291829

1830-
argW = I0 * Rsh / nNsVth * np.exp(Rsh *(-I + IL + I0) / nNsVth)
1831-
lambertwterm = lambertw(argW)
1830+
argW = I0 * Rsh / nNsVth * np.exp(Rsh * (-I + IL + I0) / nNsVth)
1831+
lambertwterm = lambertw(argW).real
18321832

1833-
# Eqn. 3 in Jain and Kapoor, 2004
1833+
# Calculate using log(argW) in case argW is really big
1834+
logargW = (np.log(I0) + np.log(Rsh) - np.log(nNsVth) +
1835+
Rsh * (-I + IL + I0) / nNsVth)
1836+
1837+
# Three iterations of Newton-Raphson method to solve
1838+
# w+log(w)=logargW. The initial guess is w=logargW. Where direct
1839+
# evaluation (above) results in NaN from overflow, 3 iterations
1840+
# of Newton's method gives approximately 8 digits of precision.
1841+
w = logargW
1842+
for i in range(0, 3):
1843+
w = w * (1 - np.log(w) + logargW) / (1 + w)
1844+
lambertwterm_log = w
18341845

1846+
lambertwterm = np.where(np.isfinite(lambertwterm), lambertwterm,
1847+
lambertwterm_log)
1848+
1849+
# Eqn. 3 in Jain and Kapoor, 2004
18351850
V = -I*(Rs + Rsh) + IL*Rsh - nNsVth*lambertwterm + I0*Rsh
18361851

1837-
return V.real
1852+
return V
18381853

18391854

18401855
def i_from_v(resistance_shunt, resistance_series, nNsVth, voltage,
@@ -1898,12 +1913,12 @@ def i_from_v(resistance_shunt, resistance_series, nNsVth, voltage,
18981913
argW = (Rs*I0*Rsh *
18991914
np.exp(Rsh*(Rs*(IL+I0)+V) / (nNsVth*(Rs+Rsh))) /
19001915
(nNsVth*(Rs + Rsh)))
1901-
lambertwterm = lambertw(argW)
1916+
lambertwterm = lambertw(argW).real
19021917

19031918
# Eqn. 4 in Jain and Kapoor, 2004
19041919
I = -V/(Rs + Rsh) - (nNsVth/Rs)*lambertwterm + Rsh*(IL + I0)/(Rs + Rsh)
19051920

1906-
return I.real
1921+
return I
19071922

19081923

19091924
def snlinverter(v_dc, p_dc, inverter):

pvlib/test/test_pvsystem.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -355,26 +355,34 @@ def test_PVSystem_calcparams_desoto(cec_module_params):
355355
@requires_scipy
356356
def test_v_from_i():
357357
output = pvsystem.v_from_i(20, .1, .5, 3, 6e-7, 7)
358-
assert_allclose(7.5049875193450521, output, 5)
358+
assert_allclose(7.5049875193450521, output, atol=1e-5)
359359

360360

361361
@requires_scipy
362362
def test_v_from_i_big():
363363
output = pvsystem.v_from_i(500, 10, 4.06, 0, 6e-10, 1.2)
364-
assert_allclose(86.320000493521079, output, 5)
364+
assert_allclose(86.320000493521079, output, atol=1e-5)
365+
366+
367+
@requires_scipy
368+
def test_v_from_i_bigger():
369+
# 1000 W/m^2 on a Canadian Solar 220M with 20 C ambient temp
370+
# github issue 225
371+
output = pvsystem.v_from_i(190, 1.065, 2.89, 0, 7.05196029e-08, 10.491262)
372+
assert_allclose(54.303958833791455, output, atol=1e-5)
365373

366374

367375
@requires_scipy
368376
def test_i_from_v():
369377
output = pvsystem.i_from_v(20, .1, .5, 40, 6e-7, 7)
370-
assert_allclose(-299.746389916, output, 5)
378+
assert_allclose(-299.746389916, output, atol=1e-5)
371379

372380

373381
@requires_scipy
374382
def test_PVSystem_i_from_v():
375383
system = pvsystem.PVSystem()
376384
output = system.i_from_v(20, .1, .5, 40, 6e-7, 7)
377-
assert_allclose(-299.746389916, output, 5)
385+
assert_allclose(-299.746389916, output, atol=1e-5)
378386

379387

380388
@requires_scipy

0 commit comments

Comments
 (0)