Skip to content

Commit e733ebb

Browse files
mikofskichetan201
authored andcommitted
BUG: fix contrib.gen_coeffs Isat2 temperature dependence (#89)
* BUG: fix contrib.gen_coeffs Isat2 temperature dependence * in residual, assume "isat2" in x is at STC, ie "isat2_t0" and calculate isat2 using `diode.isat_t()` * also fix all documentation in gen_coeffs to render okay * make example a callable script, allow user to specify whether they want to fit just STC or all IEC61853, show generated coeffs, and save figure * futurize Bennet's module mismatch simulator for py36 * add contrib, gen_coeffs, and module mismatch simulator to docs in new api section called contrib * add example output figures * fix intersphinx, must use https, leave off trailing slash * don't ignore git conflict files, so they can be removed without hiding * ignore the new .pytest_cache folder, move python3 __pycache__ folder to #build * fix underline in README.rst, should be as long as the title * fix NoneType has no method upper() * scale STC values by cells in series and parallel in RMSE relative to STC * fix RMSE, show Isc, Voc on plots
1 parent b5b7e09 commit e733ebb

10 files changed

Lines changed: 198 additions & 95 deletions

File tree

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ pvmismatch.sublime-workspace
1010

1111
# files
1212
*.pyc
13-
*.orig
1413

1514
# build
1615
dist/
1716
pvmismatch.egg-info/
1817
build/
1918
Makefile
2019
version.py
20+
__pycache__/
2121

2222
# docs
2323
!docs/_templates/
@@ -28,7 +28,7 @@ testPV/
2828
benchmark_*/
2929
.coverage
3030
.cache/
31-
__pycache__/
31+
.pytest_cache/
3232

3333
# virtualenv
3434
venv/

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,6 @@ history is also on `GitHub <https://github.com/SunPower/releases/>`__.
3535
:target: https://travis-ci.org/SunPower/PVMismatch
3636

3737
Other Projects that use PVMismatch
38-
-----
38+
----------------------------------
3939
System level mismatch loss calculator using PVMismatch tool (STC and Annual energy loss)
4040
https://github.com/SunPower/MismatchLossStudy

pvmismatch/contrib/gen_coeffs/__init__.py

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,37 @@
1111
# IEC 61853 test matrix
1212
TC_C = [15.0, 25.0, 50.0, 75.0]
1313
IRR_W_M2 = [100.0, 200.0, 400.0, 600.0, 800.0, 1000.0, 1100.0]
14-
TEST_MAT = np.meshgrid(TC_C, IRR_W_M2)
14+
TEST_MAT = np.meshgrid(TC_C, IRR_W_M2) #: IEC61853 test matrix
1515

1616
def gen_iec_61853_from_sapm(pvmodule):
1717
"""
1818
Generate an IEC 61853 test from Sandia Array Performance Model (sapm).
1919
20-
:param pvmodule: PV module to be tested
21-
:type pvmodule: dict
20+
:param dict pvmodule: PV module to be tested
21+
:returns: a pandas dataframe with columns ``i_mp``, ``v_mp``, ``i_sc``, and
22+
``v_oc`` and rows corresponding to the IEC61853 test conditions
2223
23-
Module is a dictionary according to :def:`pvlib.pvsystem.sapm`.
24+
Module is a dictionary according to ``pvlib.pvsystem.sapm``.
2425
"""
2526
tc, irr = TEST_MAT
2627
return sapm(irr / 1000.0, tc, pvmodule)
2728

2829

2930
def gen_two_diode(isc, voc, imp, vmp, nseries, nparallel,
30-
tc, x0 = None, *args, **kwargs):
31+
tc, x0=None, *args, **kwargs):
3132
"""
32-
Generate two-diode model parameters for ``pvcell`` given
33+
Generate two-diode model parameters for ``pvcell`` given.
34+
35+
:param numeric isc: short circuit current [A]
36+
:param numeric voc: open circuit voltage [V]
37+
:param numeric imp: max power current [A]
38+
:param numeric vmp: max power voltage [V]
39+
:param int nseries: number of cells in series
40+
:param int nparallel: number of parallel substrings in PV module
41+
:param numeric tc: cell temperature [C]
42+
:param x0: optional list of initial guesses, default is ``None``
43+
:returns: tuple ``(isat1, isat2, rs, rsh)`` of generated coefficients and
44+
the solver output
3345
"""
3446
isc_cell = isc / nparallel
3547
voc_cell = voc / nseries
@@ -78,17 +90,17 @@ def gen_sapm(iec_61853):
7890
return isc0, alpha_isc
7991

8092

81-
8293
def residual_two_diode(x, isc, voc, imp, vmp, tc):
8394
"""
8495
Objective function to solve 2-diode model.
85-
:param x: parameters isat1, isat2, rs and rsh
86-
:param isc: short circuit current [A] at tc [C]
87-
:param voc: open circuit voltage [V] at tc [C]
88-
:param imp: max power current [A] at tc [C]
89-
:param vmp: max power voltage [V] at tc [C]
96+
97+
:param x: parameters ``isat1``, ``isat2``, ``rs``, and ``rsh``
98+
:param isc: short circuit current [A] at ``tc`` [C]
99+
:param voc: open circuit voltage [V] at ``tc`` [C]
100+
:param imp: max power current [A] at ``tc`` [C]
101+
:param vmp: max power voltage [V] at ``tc`` [C]
90102
:param tc: cell temperature [C]
91-
:return: norm of the residuals its sensitivity
103+
:returns: norm of the residuals and the Jacobian matrix
92104
"""
93105
# Constants
94106
q = diode.QE # [C/electron] elementary electric charge
@@ -99,11 +111,12 @@ def residual_two_diode(x, isc, voc, imp, vmp, tc):
99111
vt = kb * tck / q # [V] thermal voltage
100112
# Rescale Variables
101113
isat1_t0 = np.exp(x[0])
102-
isat2 = np.exp(x[1])
114+
isat2_t0 = np.exp(x[1])
103115
rs = x[2] ** 2.0
104116
rsh = x[3] ** 2.0
105117
# first diode saturation current
106118
isat1 = diode.isat_t(tc, isat1_t0)
119+
isat2 = diode.isat_t(tc, isat2_t0)
107120
# Short Circuit
108121
vd_isc, _ = diode.fvd(vc=0.0, ic=isc, rs=rs)
109122
id1_isc, _ = diode.fid(isat=isat1, vd=vd_isc, m=1.0, vt=vt)
Lines changed: 90 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1+
#! /usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
14
"""
25
Generate coefficients example.
36
"""
47

8+
from __future__ import (
9+
absolute_import, division, print_function, unicode_literals)
10+
import os
11+
import sys
512
from pvmismatch.contrib import gen_coeffs
613
from pvmismatch import *
714
from matplotlib import pyplot as plt
@@ -22,60 +29,89 @@
2229

2330
iec61853 = gen_coeffs.gen_iec_61853_from_sapm(gen_coeffs.PVMODULES[PROD_NAME])
2431
iec61853['i_mp'] = iec61853['p_mp'] / iec61853['v_mp']
25-
#isc0, alpha_isc = gen_coeffs.gen_sapm(iec61853)
26-
#x, sol = gen_coeffs.gen_two_diode(ISC0, VOC0, IMP0, VMP0, NS, NP, T0)
27-
x, sol = gen_coeffs.gen_two_diode(
28-
iec61853['i_sc'], iec61853['v_oc'], iec61853['i_mp'],
29-
iec61853['v_mp'], NS, NP, tc=TC, method='lm',
30-
x0=(2.25e-11, 1.5e-6, 0.004, 10.0)
31-
)
32-
isat1, isat2, rs, rsh = x
33-
34-
pvc = pvcell.PVcell(
35-
Rs=rs, Rsh=rsh, Isat1_T0=isat1, Isat2_T0=isat2,
36-
Isc0_T0=ISC0/NP, alpha_Isc=AISC
37-
)
38-
f1 = plt.figure()
32+
isc0, alpha_isc = gen_coeffs.gen_sapm(iec61853)
33+
assert np.isclose(isc0, ISC0)
34+
assert np.isclose(alpha_isc, AISC)
3935

40-
for m, _tc in enumerate(gen_coeffs.TC_C):
41-
pvc.Tcell = _tc + 273.15
42-
plt.subplot(2, 2, m+1)
43-
plt.xlim([0, 0.8])
44-
plt.ylim([0, 8])
45-
res_norm = 0
46-
for n, _irr in enumerate(gen_coeffs.IRR_W_M2):
47-
pvc.Ee = _irr / 1000.0
48-
plt.plot(pvc.Vcell, pvc.Icell, '-', pvc.Vcell, pvc.Pcell, ':')
49-
plt.plot(
50-
iec61853['v_mp'][n][m]/NS, iec61853['i_mp'][n][m]/NP, 'x',
51-
iec61853['v_oc'][n][m]/NS, 0.0, 'x',
52-
0.0, iec61853['i_sc'][n][m]/NP, 'x',
53-
iec61853['v_mp'][n][m]/NS, iec61853['p_mp'][n][m]/NS/NP, 'o',
36+
if __name__ == '__main__':
37+
test_cond = 'STC'
38+
if len(sys.argv) > 1:
39+
test_cond = sys.argv[1]
40+
if test_cond.upper() == 'STC':
41+
x, sol = gen_coeffs.gen_two_diode(ISC0, VOC0, IMP0, VMP0, NS, NP, T0)
42+
else:
43+
x, sol = gen_coeffs.gen_two_diode(
44+
iec61853['i_sc'], iec61853['v_oc'], iec61853['i_mp'],
45+
iec61853['v_mp'], NS, NP, tc=TC, method='lm',
46+
x0=(2.25e-11, 1.5e-6, 0.004, 10.0)
5447
)
55-
res_norm += (
56-
pvc.calcIcell(iec61853['v_mp'][n][m]/NS)
57-
- iec61853['i_mp'][n][m]/NP
58-
)**2 / (iec61853['i_mp'][n][m]/NP)**2
59-
res_norm += (
60-
pvc.calcVcell(iec61853['i_mp'][n][m]/NP)
61-
- iec61853['v_mp'][n][m]/NS
62-
)**2 / (iec61853['v_mp'][n][m]/NS)**2
63-
res_norm += (
64-
pvc.calcVcell(0.0) - iec61853['v_oc'][n][m]/NS
65-
)**2 / (iec61853['v_oc'][n][m]/NS)**2
66-
res_norm += (
67-
pvc.calcIcell(0.0) - iec61853['i_sc'][n][m]/NP
68-
)**2 / (iec61853['i_sc'][n][m]/NP)**2
69-
rel_diff = (pvc.Pcell.max()*NS*NP - iec61853['p_mp'][n][m]) / PMP0
70-
plt.annotate('$\Delta_{rel}$ = %.2g%%' % (rel_diff*100),
71-
(0.65, iec61853['p_mp'][n][m]/NS/NP))
72-
plt.annotate('norm of residuals = %g' % np.sqrt(res_norm / (7*4)),
73-
(0.5, 7.5))
74-
plt.grid(True)
75-
plt.title(
76-
'PVMismatch Generated Coefficients for %s at Tc = %g' % (PROD_NAME, _tc)
48+
isat1, isat2, rs, rsh = x
49+
50+
pvc = pvcell.PVcell(
51+
Rs=rs, Rsh=rsh, Isat1_T0=isat1, Isat2_T0=isat2,
52+
Isc0_T0=ISC0/NP, alpha_Isc=AISC,
53+
pvconst=pvconstants.PVconstants(npts=1001)
7754
)
78-
plt.xlabel('voltage')
79-
plt.ylabel('current [A]')
80-
plt.xlabel('voltage [V]')
81-
f1.show()
55+
f1 = plt.figure(figsize=(16, 10))
56+
57+
for m, _tc in enumerate(gen_coeffs.TC_C):
58+
pvc.Tcell = _tc + 273.15
59+
plt.subplot(2, 2, m+1)
60+
plt.xlim([0, 0.8])
61+
plt.ylim([0, 8])
62+
res_norm = 0
63+
for n, _irr in enumerate(gen_coeffs.IRR_W_M2):
64+
pvc.Ee = _irr / 1000.0
65+
plt.plot(pvc.Vcell, pvc.Icell, '-', pvc.Vcell, pvc.Pcell, ':')
66+
plt.plot(
67+
iec61853['v_mp'][n][m]/NS, iec61853['i_mp'][n][m]/NP, 'x',
68+
iec61853['v_oc'][n][m]/NS, 0.0, 'x',
69+
0.0, iec61853['i_sc'][n][m]/NP, 'x',
70+
iec61853['v_mp'][n][m]/NS, iec61853['p_mp'][n][m]/NS/NP, 'o',
71+
)
72+
mpp = np.argmax(pvc.Pcell)
73+
res_norm += (
74+
pvc.Icell[mpp] - iec61853['i_mp'][n][m]/NP
75+
)**2 / (IMP0/NP)**2
76+
res_norm += (
77+
pvc.Vcell[mpp] - iec61853['v_mp'][n][m]/NS
78+
)**2 / (VMP0/NS)**2
79+
voc = pvc.calcVcell(0.0)
80+
res_norm += (
81+
voc - iec61853['v_oc'][n][m]/NS
82+
)**2 / (VOC0/NS)**2
83+
isc = pvc.calcIcell(0.0)
84+
res_norm += (
85+
isc - iec61853['i_sc'][n][m]/NP
86+
)**2 / (ISC0/NP)**2
87+
rel_diff = (pvc.Pcell[mpp]*NS*NP - iec61853['p_mp'][n][m]) / PMP0
88+
plt.annotate('$\Delta_{STC}$ = %.2g%%' % (rel_diff*100),
89+
(0.65, iec61853['p_mp'][n][m]/NS/NP))
90+
plt.annotate(
91+
'$E_e$ = %.2g[suns], $V_{oc}$ = %.2g[V}, $I_{sc}$ = %.g[A]' % (_irr/1000, voc, isc),
92+
(0.05, 0.05+iec61853['i_sc'][n][m]/NP))
93+
plt.annotate('STC RMSE = %.2g%%' % (np.sqrt(res_norm / (7*4))*100),
94+
(0.65, 7.5))
95+
plt.annotate('$I_{sat,1}$ = %.4g' % isat1,
96+
(0.65, 7.2))
97+
plt.annotate('$I_{sat,2}$ = %.4g' % isat2,
98+
(0.65, 6.9))
99+
plt.annotate('$R_s$ = %.4g' % rs,
100+
(0.65, 6.6))
101+
plt.annotate('$R_{sh}$ = %.4g' % rsh,
102+
(0.65, 6.3))
103+
104+
plt.grid(True)
105+
plt.title(
106+
'PVMismatch Generated Coefficients for %s at Tc = %g' % (PROD_NAME, _tc)
107+
)
108+
plt.xlabel('voltage')
109+
plt.ylabel('current [A], power [W]')
110+
plt.xlabel('voltage [V]')
111+
plt.tight_layout()
112+
if len(sys.argv) > 2:
113+
test_save_dir = sys.argv[2]
114+
os.makedirs(test_save_dir, exist_ok=True)
115+
f1.savefig(os.path.join(test_save_dir, sys.argv[1]))
116+
else:
117+
f1.show()

pvmismatch/contrib/module_mismatch_simulator.py

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,33 @@
22

33
'''
44
Created on Mar 29, 2013
5+
56
This script allows the user to dynamically investigate the IV and PV
67
characteristics of a single module. The user chooses the modules size--72 or 96
7-
cells. A UI is then generated that allows the user to change the size, location,
8+
cells. A GUI is then generated that allows the user to change the size, location,
89
and irradiance level of a single "shade rectangle". Outputs include cell,
910
substring, and module level IV and PV curves as well as a module diagram showing
1011
the shade location, any reverse biased cells, and any active diodes.
12+
1113
@author: bmeyers
1214
'''
1315
# ==============================================================================
1416
# Importing standard libraries
1517
# ==============================================================================
1618

17-
import matplotlib.pyplot as plt
18-
import matplotlib.gridspec as gridspec
19+
from __future__ import (
20+
absolute_import, division, unicode_literals, print_function)
21+
import json
22+
from functools import partial
23+
from copy import deepcopy
24+
import os
1925
import numpy as np
2026
from scipy.interpolate import interp1d
27+
import matplotlib.pyplot as plt
28+
import matplotlib.gridspec as gridspec
2129
from matplotlib.widgets import Slider
2230
from matplotlib.widgets import Button
23-
import json
24-
from functools import partial
25-
from copy import deepcopy
31+
from past.builtins import raw_input, xrange
2632

2733
# ==============================================================================
2834
# Import PVmismatch items
@@ -32,8 +38,8 @@
3238
from pvmismatch import PVsystem, PVmodule, PVcell, PVconstants
3339
from pvmismatch.pvmismatch_lib.pvmodule import STD72, STD96, STD128
3440
except ImportError:
35-
print "PVMismatch not found on path! Please use 'pip install -e path/to/pvmismatch'"
36-
print "or 'export PYTHONPATH=path/to/pvmismatch:$PYTHONPATH' first."
41+
print("PVMismatch not found on path! Please use 'pip install -e path/to/pvmismatch'")
42+
print("or 'export PYTHONPATH=path/to/pvmismatch:$PYTHONPATH' first.")
3743
import sys
3844
sys.exit(-1)
3945

@@ -74,8 +80,8 @@ class ShadeObj(object):
7480
def __init__(self, pershade=90, shd_width=1, shd_height=1, shd_x=4,
7581
shd_y=6, numberCells=96):
7682
modHeight = modheight(numberCells)
77-
module = np.empty([numberCells / modHeight, modHeight], dtype=int)
78-
for n in range(numberCells / modHeight):
83+
module = np.empty([numberCells // modHeight, modHeight], dtype=int)
84+
for n in range(numberCells // modHeight):
7985
if n % 2 == 0:
8086
module[n] = np.arange(n * modHeight, (n + 1) * modHeight, 1)
8187
else:
@@ -155,8 +161,8 @@ def plotting_calcs(pvmod, ivp=None):
155161
reversebias = [n for n in range(len(ivp.Icell.T))
156162
if -np.interp(-ivp.Imp, -ivp.Icell.T[n], -ivp.Vcell.T[n]) < 0]
157163
boolindx = np.array(reversebias)
158-
module = np.empty([pvmod.numberCells / ivp.modHeight, ivp.modHeight], dtype=int)
159-
for n in range(pvmod.numberCells / ivp.modHeight):
164+
module = np.empty([pvmod.numberCells // ivp.modHeight, ivp.modHeight], dtype=int)
165+
for n in range(pvmod.numberCells // ivp.modHeight):
160166
if n % 2 == 0:
161167
module[n] = np.arange(n * ivp.modHeight, (n + 1) * ivp.modHeight, 1)
162168
else:
@@ -427,24 +433,26 @@ def full_update(val, output=None, ivp0=None, plotobjs=None):
427433
output['ax12'], output['ax03'], output['ax_4'], output['x'], output['y'])
428434
plt.draw()
429435
t1 = (sw * sh, s_ps.val, ivp0.Pmp, 100 * ivp0.Pmp / Pmp0)
430-
print '{0:^6} {1:^6,.2f} {2:^6,.2f} {3:^7,.2f}'.format(*t1)
436+
print('{0:^6} {1:^6,.2f} {2:^6,.2f} {3:^7,.2f}'.format(*t1))
431437

432438

433439
def set_the_shade(val):
434440
ivp0.shade.insert(0, ivp0.shade[-1])
435441

436442

437443
def save_the_shade(val):
444+
this_dir = os.getcwd()
445+
save_dir = os.path.join(this_dir, 'JSONshade')
446+
os.makedirs(save_dir, exist_ok=True)
438447
dicts = []
439448
for shd in ivp0.shade:
440449
dicts.append({'ps': shd.pershade, 'sw': shd.sw, 'sh': shd.sh,
441450
'sy': shd.sy, 'sx': shd.sx,
442451
'numberCells': pvmod1.numberCells})
443452
filename = raw_input("File name? ")
444-
filename = 'JSONshade/' + filename + '.json'
445-
fo = open(filename, 'w')
446-
shades_json = json.dump(dicts, fo, sort_keys=True, indent=2)
447-
fo.close()
453+
filename = os.path.join(save_dir, filename + '.json')
454+
with open(filename, 'w') as fo:
455+
json.dump(dicts, fo, sort_keys=True, indent=2)
448456

449457

450458
def clear_last_full(val, update=None):
@@ -465,10 +473,10 @@ def clear_last_full(val, update=None):
465473
plotobjs = PlotObjs()
466474
update = partial(full_update, output=output, ivp0=ivp0, plotobjs=plotobjs)
467475
ClearLast = partial(clear_last_full, update=update)
468-
print "Pmp0: {}".format(Pmp0)
469-
print ""
470-
print '{0:6} {1:^6} {2:^6} {3:^7}'.format('#Cells', '%Shade', 'Pmp', '%ofPmp0')
471-
print '----------------------------'
476+
print("Pmp0: {}".format(Pmp0))
477+
print("")
478+
print('{0:6} {1:^6} {2:^6} {3:^7}'.format('#Cells', '%Shade', 'Pmp', '%ofPmp0'))
479+
print('----------------------------')
472480
ps0 = 90
473481
sw0 = 1
474482
sh0 = 1

0 commit comments

Comments
 (0)