Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ pvmismatch.sublime-workspace

# files
*.pyc
*.orig

# build
dist/
pvmismatch.egg-info/
build/
Makefile
version.py
__pycache__/

# docs
!docs/_templates/
Expand All @@ -28,7 +28,7 @@ testPV/
benchmark_*/
.coverage
.cache/
__pycache__/
.pytest_cache/

# virtualenv
venv/
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@ history is also on `GitHub <https://github.com/SunPower/releases/>`__.
:target: https://travis-ci.org/SunPower/PVMismatch

Other Projects that use PVMismatch
-----
----------------------------------
System level mismatch loss calculator using PVMismatch tool (STC and Annual energy loss)
https://github.com/SunPower/MismatchLossStudy
41 changes: 27 additions & 14 deletions pvmismatch/contrib/gen_coeffs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,37 @@
# IEC 61853 test matrix
TC_C = [15.0, 25.0, 50.0, 75.0]
IRR_W_M2 = [100.0, 200.0, 400.0, 600.0, 800.0, 1000.0, 1100.0]
TEST_MAT = np.meshgrid(TC_C, IRR_W_M2)
TEST_MAT = np.meshgrid(TC_C, IRR_W_M2) #: IEC61853 test matrix

def gen_iec_61853_from_sapm(pvmodule):
"""
Generate an IEC 61853 test from Sandia Array Performance Model (sapm).

:param pvmodule: PV module to be tested
:type pvmodule: dict
:param dict pvmodule: PV module to be tested
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding docstrings everywhere!
Our future users will thanks us plenty for that :)

:returns: a pandas dataframe with columns ``i_mp``, ``v_mp``, ``i_sc``, and
``v_oc`` and rows corresponding to the IEC61853 test conditions

Module is a dictionary according to :def:`pvlib.pvsystem.sapm`.
Module is a dictionary according to ``pvlib.pvsystem.sapm``.
"""
tc, irr = TEST_MAT
return sapm(irr / 1000.0, tc, pvmodule)


def gen_two_diode(isc, voc, imp, vmp, nseries, nparallel,
tc, x0 = None, *args, **kwargs):
tc, x0=None, *args, **kwargs):
"""
Generate two-diode model parameters for ``pvcell`` given
Generate two-diode model parameters for ``pvcell`` given.

:param numeric isc: short circuit current [A]
:param numeric voc: open circuit voltage [V]
:param numeric imp: max power current [A]
:param numeric vmp: max power voltage [V]
:param int nseries: number of cells in series
:param int nparallel: number of parallel substrings in PV module
:param numeric tc: cell temperature [C]
:param x0: optional list of initial guesses, default is ``None``
:returns: tuple ``(isat1, isat2, rs, rsh)`` of generated coefficients and
the solver output
"""
isc_cell = isc / nparallel
voc_cell = voc / nseries
Expand Down Expand Up @@ -78,17 +90,17 @@ def gen_sapm(iec_61853):
return isc0, alpha_isc



def residual_two_diode(x, isc, voc, imp, vmp, tc):
"""
Objective function to solve 2-diode model.
:param x: parameters isat1, isat2, rs and rsh
:param isc: short circuit current [A] at tc [C]
:param voc: open circuit voltage [V] at tc [C]
:param imp: max power current [A] at tc [C]
:param vmp: max power voltage [V] at tc [C]

:param x: parameters ``isat1``, ``isat2``, ``rs``, and ``rsh``
:param isc: short circuit current [A] at ``tc`` [C]
:param voc: open circuit voltage [V] at ``tc`` [C]
:param imp: max power current [A] at ``tc`` [C]
:param vmp: max power voltage [V] at ``tc`` [C]
:param tc: cell temperature [C]
:return: norm of the residuals its sensitivity
:returns: norm of the residuals and the Jacobian matrix
"""
# Constants
q = diode.QE # [C/electron] elementary electric charge
Expand All @@ -99,11 +111,12 @@ def residual_two_diode(x, isc, voc, imp, vmp, tc):
vt = kb * tck / q # [V] thermal voltage
# Rescale Variables
isat1_t0 = np.exp(x[0])
isat2 = np.exp(x[1])
isat2_t0 = np.exp(x[1])
rs = x[2] ** 2.0
rsh = x[3] ** 2.0
# first diode saturation current
isat1 = diode.isat_t(tc, isat1_t0)
isat2 = diode.isat_t(tc, isat2_t0)
# Short Circuit
vd_isc, _ = diode.fvd(vc=0.0, ic=isc, rs=rs)
id1_isc, _ = diode.fid(isat=isat1, vd=vd_isc, m=1.0, vt=vt)
Expand Down
144 changes: 90 additions & 54 deletions pvmismatch/contrib/gen_coeffs/example.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-

"""
Generate coefficients example.
"""

from __future__ import (
absolute_import, division, print_function, unicode_literals)
import os
import sys
from pvmismatch.contrib import gen_coeffs
from pvmismatch import *
from matplotlib import pyplot as plt
Expand All @@ -22,60 +29,89 @@

iec61853 = gen_coeffs.gen_iec_61853_from_sapm(gen_coeffs.PVMODULES[PROD_NAME])
iec61853['i_mp'] = iec61853['p_mp'] / iec61853['v_mp']
#isc0, alpha_isc = gen_coeffs.gen_sapm(iec61853)
#x, sol = gen_coeffs.gen_two_diode(ISC0, VOC0, IMP0, VMP0, NS, NP, T0)
x, sol = gen_coeffs.gen_two_diode(
iec61853['i_sc'], iec61853['v_oc'], iec61853['i_mp'],
iec61853['v_mp'], NS, NP, tc=TC, method='lm',
x0=(2.25e-11, 1.5e-6, 0.004, 10.0)
)
isat1, isat2, rs, rsh = x

pvc = pvcell.PVcell(
Rs=rs, Rsh=rsh, Isat1_T0=isat1, Isat2_T0=isat2,
Isc0_T0=ISC0/NP, alpha_Isc=AISC
)
f1 = plt.figure()
isc0, alpha_isc = gen_coeffs.gen_sapm(iec61853)
assert np.isclose(isc0, ISC0)
assert np.isclose(alpha_isc, AISC)

for m, _tc in enumerate(gen_coeffs.TC_C):
pvc.Tcell = _tc + 273.15
plt.subplot(2, 2, m+1)
plt.xlim([0, 0.8])
plt.ylim([0, 8])
res_norm = 0
for n, _irr in enumerate(gen_coeffs.IRR_W_M2):
pvc.Ee = _irr / 1000.0
plt.plot(pvc.Vcell, pvc.Icell, '-', pvc.Vcell, pvc.Pcell, ':')
plt.plot(
iec61853['v_mp'][n][m]/NS, iec61853['i_mp'][n][m]/NP, 'x',
iec61853['v_oc'][n][m]/NS, 0.0, 'x',
0.0, iec61853['i_sc'][n][m]/NP, 'x',
iec61853['v_mp'][n][m]/NS, iec61853['p_mp'][n][m]/NS/NP, 'o',
if __name__ == '__main__':
test_cond = 'STC'
if len(sys.argv) > 1:
test_cond = sys.argv[1]
if test_cond.upper() == 'STC':
x, sol = gen_coeffs.gen_two_diode(ISC0, VOC0, IMP0, VMP0, NS, NP, T0)
else:
x, sol = gen_coeffs.gen_two_diode(
iec61853['i_sc'], iec61853['v_oc'], iec61853['i_mp'],
iec61853['v_mp'], NS, NP, tc=TC, method='lm',
x0=(2.25e-11, 1.5e-6, 0.004, 10.0)
)
res_norm += (
pvc.calcIcell(iec61853['v_mp'][n][m]/NS)
- iec61853['i_mp'][n][m]/NP
)**2 / (iec61853['i_mp'][n][m]/NP)**2
res_norm += (
pvc.calcVcell(iec61853['i_mp'][n][m]/NP)
- iec61853['v_mp'][n][m]/NS
)**2 / (iec61853['v_mp'][n][m]/NS)**2
res_norm += (
pvc.calcVcell(0.0) - iec61853['v_oc'][n][m]/NS
)**2 / (iec61853['v_oc'][n][m]/NS)**2
res_norm += (
pvc.calcIcell(0.0) - iec61853['i_sc'][n][m]/NP
)**2 / (iec61853['i_sc'][n][m]/NP)**2
rel_diff = (pvc.Pcell.max()*NS*NP - iec61853['p_mp'][n][m]) / PMP0
plt.annotate('$\Delta_{rel}$ = %.2g%%' % (rel_diff*100),
(0.65, iec61853['p_mp'][n][m]/NS/NP))
plt.annotate('norm of residuals = %g' % np.sqrt(res_norm / (7*4)),
(0.5, 7.5))
plt.grid(True)
plt.title(
'PVMismatch Generated Coefficients for %s at Tc = %g' % (PROD_NAME, _tc)
isat1, isat2, rs, rsh = x

pvc = pvcell.PVcell(
Rs=rs, Rsh=rsh, Isat1_T0=isat1, Isat2_T0=isat2,
Isc0_T0=ISC0/NP, alpha_Isc=AISC,
pvconst=pvconstants.PVconstants(npts=1001)
)
plt.xlabel('voltage')
plt.ylabel('current [A]')
plt.xlabel('voltage [V]')
f1.show()
f1 = plt.figure(figsize=(16, 10))

for m, _tc in enumerate(gen_coeffs.TC_C):
pvc.Tcell = _tc + 273.15
plt.subplot(2, 2, m+1)
plt.xlim([0, 0.8])
plt.ylim([0, 8])
res_norm = 0
for n, _irr in enumerate(gen_coeffs.IRR_W_M2):
pvc.Ee = _irr / 1000.0
plt.plot(pvc.Vcell, pvc.Icell, '-', pvc.Vcell, pvc.Pcell, ':')
plt.plot(
iec61853['v_mp'][n][m]/NS, iec61853['i_mp'][n][m]/NP, 'x',
iec61853['v_oc'][n][m]/NS, 0.0, 'x',
0.0, iec61853['i_sc'][n][m]/NP, 'x',
iec61853['v_mp'][n][m]/NS, iec61853['p_mp'][n][m]/NS/NP, 'o',
)
mpp = np.argmax(pvc.Pcell)
res_norm += (
pvc.Icell[mpp] - iec61853['i_mp'][n][m]/NP
)**2 / (IMP0/NP)**2
res_norm += (
pvc.Vcell[mpp] - iec61853['v_mp'][n][m]/NS
)**2 / (VMP0/NS)**2
voc = pvc.calcVcell(0.0)
res_norm += (
voc - iec61853['v_oc'][n][m]/NS
)**2 / (VOC0/NS)**2
isc = pvc.calcIcell(0.0)
res_norm += (
isc - iec61853['i_sc'][n][m]/NP
)**2 / (ISC0/NP)**2
rel_diff = (pvc.Pcell[mpp]*NS*NP - iec61853['p_mp'][n][m]) / PMP0
plt.annotate('$\Delta_{STC}$ = %.2g%%' % (rel_diff*100),
(0.65, iec61853['p_mp'][n][m]/NS/NP))
plt.annotate(
'$E_e$ = %.2g[suns], $V_{oc}$ = %.2g[V}, $I_{sc}$ = %.g[A]' % (_irr/1000, voc, isc),
(0.05, 0.05+iec61853['i_sc'][n][m]/NP))
plt.annotate('STC RMSE = %.2g%%' % (np.sqrt(res_norm / (7*4))*100),
(0.65, 7.5))
plt.annotate('$I_{sat,1}$ = %.4g' % isat1,
(0.65, 7.2))
plt.annotate('$I_{sat,2}$ = %.4g' % isat2,
(0.65, 6.9))
plt.annotate('$R_s$ = %.4g' % rs,
(0.65, 6.6))
plt.annotate('$R_{sh}$ = %.4g' % rsh,
(0.65, 6.3))

plt.grid(True)
plt.title(
'PVMismatch Generated Coefficients for %s at Tc = %g' % (PROD_NAME, _tc)
)
plt.xlabel('voltage')
plt.ylabel('current [A], power [W]')
plt.xlabel('voltage [V]')
plt.tight_layout()
if len(sys.argv) > 2:
test_save_dir = sys.argv[2]
os.makedirs(test_save_dir, exist_ok=True)
f1.savefig(os.path.join(test_save_dir, sys.argv[1]))
else:
f1.show()
50 changes: 29 additions & 21 deletions pvmismatch/contrib/module_mismatch_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,33 @@

'''
Created on Mar 29, 2013

This script allows the user to dynamically investigate the IV and PV
characteristics of a single module. The user chooses the modules size--72 or 96
cells. A UI is then generated that allows the user to change the size, location,
cells. A GUI is then generated that allows the user to change the size, location,
and irradiance level of a single "shade rectangle". Outputs include cell,
substring, and module level IV and PV curves as well as a module diagram showing
the shade location, any reverse biased cells, and any active diodes.

@author: bmeyers
'''
# ==============================================================================
# Importing standard libraries
# ==============================================================================

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
from __future__ import (
absolute_import, division, unicode_literals, print_function)
import json
from functools import partial
from copy import deepcopy
import os
import numpy as np
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
from matplotlib.widgets import Slider
from matplotlib.widgets import Button
import json
from functools import partial
from copy import deepcopy
from past.builtins import raw_input, xrange

# ==============================================================================
# Import PVmismatch items
Expand All @@ -32,8 +38,8 @@
from pvmismatch import PVsystem, PVmodule, PVcell, PVconstants
from pvmismatch.pvmismatch_lib.pvmodule import STD72, STD96, STD128
except ImportError:
print "PVMismatch not found on path! Please use 'pip install -e path/to/pvmismatch'"
print "or 'export PYTHONPATH=path/to/pvmismatch:$PYTHONPATH' first."
print("PVMismatch not found on path! Please use 'pip install -e path/to/pvmismatch'")
print("or 'export PYTHONPATH=path/to/pvmismatch:$PYTHONPATH' first.")
import sys
sys.exit(-1)

Expand Down Expand Up @@ -74,8 +80,8 @@ class ShadeObj(object):
def __init__(self, pershade=90, shd_width=1, shd_height=1, shd_x=4,
shd_y=6, numberCells=96):
modHeight = modheight(numberCells)
module = np.empty([numberCells / modHeight, modHeight], dtype=int)
for n in range(numberCells / modHeight):
module = np.empty([numberCells // modHeight, modHeight], dtype=int)
for n in range(numberCells // modHeight):
if n % 2 == 0:
module[n] = np.arange(n * modHeight, (n + 1) * modHeight, 1)
else:
Expand Down Expand Up @@ -155,8 +161,8 @@ def plotting_calcs(pvmod, ivp=None):
reversebias = [n for n in range(len(ivp.Icell.T))
if -np.interp(-ivp.Imp, -ivp.Icell.T[n], -ivp.Vcell.T[n]) < 0]
boolindx = np.array(reversebias)
module = np.empty([pvmod.numberCells / ivp.modHeight, ivp.modHeight], dtype=int)
for n in range(pvmod.numberCells / ivp.modHeight):
module = np.empty([pvmod.numberCells // ivp.modHeight, ivp.modHeight], dtype=int)
for n in range(pvmod.numberCells // ivp.modHeight):
if n % 2 == 0:
module[n] = np.arange(n * ivp.modHeight, (n + 1) * ivp.modHeight, 1)
else:
Expand Down Expand Up @@ -427,24 +433,26 @@ def full_update(val, output=None, ivp0=None, plotobjs=None):
output['ax12'], output['ax03'], output['ax_4'], output['x'], output['y'])
plt.draw()
t1 = (sw * sh, s_ps.val, ivp0.Pmp, 100 * ivp0.Pmp / Pmp0)
print '{0:^6} {1:^6,.2f} {2:^6,.2f} {3:^7,.2f}'.format(*t1)
print('{0:^6} {1:^6,.2f} {2:^6,.2f} {3:^7,.2f}'.format(*t1))


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


def save_the_shade(val):
this_dir = os.getcwd()
save_dir = os.path.join(this_dir, 'JSONshade')
os.makedirs(save_dir, exist_ok=True)
dicts = []
for shd in ivp0.shade:
dicts.append({'ps': shd.pershade, 'sw': shd.sw, 'sh': shd.sh,
'sy': shd.sy, 'sx': shd.sx,
'numberCells': pvmod1.numberCells})
filename = raw_input("File name? ")
filename = 'JSONshade/' + filename + '.json'
fo = open(filename, 'w')
shades_json = json.dump(dicts, fo, sort_keys=True, indent=2)
fo.close()
filename = os.path.join(save_dir, filename + '.json')
with open(filename, 'w') as fo:
json.dump(dicts, fo, sort_keys=True, indent=2)


def clear_last_full(val, update=None):
Expand All @@ -465,10 +473,10 @@ def clear_last_full(val, update=None):
plotobjs = PlotObjs()
update = partial(full_update, output=output, ivp0=ivp0, plotobjs=plotobjs)
ClearLast = partial(clear_last_full, update=update)
print "Pmp0: {}".format(Pmp0)
print ""
print '{0:6} {1:^6} {2:^6} {3:^7}'.format('#Cells', '%Shade', 'Pmp', '%ofPmp0')
print '----------------------------'
print("Pmp0: {}".format(Pmp0))
print("")
print('{0:6} {1:^6} {2:^6} {3:^7}'.format('#Cells', '%Shade', 'Pmp', '%ofPmp0'))
print('----------------------------')
ps0 = 90
sw0 = 1
sh0 = 1
Expand Down
Loading