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
26 changes: 16 additions & 10 deletions pvmismatch/pvmismatch_lib/pvcell.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ class PVcell(object):
:param pvconst: configuration constants object
:type pvconst: :class:`~pvmismatch.pvmismatch_lib.pvconstants.PVconstants`
"""

_calc_now = False #: if True ``calcCells()`` is called in ``__setattr__``

def __init__(self, Rs=RS, Rsh=RSH, Isat1_T0=ISAT1_T0, Isat2=ISAT2,
Isc0_T0=ISC0_T0, aRBD=ARBD, bRBD=BRBD, VRBD=VRBD_,
nRBD=NRBD, Eg=EG, alpha_Isc=ALPHA_ISC,
Expand All @@ -69,6 +72,8 @@ def __init__(self, Rs=RS, Rsh=RSH, Isat1_T0=ISAT1_T0, Isat2=ISAT2,
self.Icell = None #: cell currents on IV curve [A]
self.Vcell = None #: cell voltages on IV curve [V]
self.Pcell = None #: cell power on IV curve [W]
# set calculation flag
self._calc_now = True # overwrites the class attribute

def __str__(self):
fmt = '<PVcell(Ee=%g[suns], Tcell=%g[K], Isc=%g[A], Voc=%g[V])>'
Expand All @@ -78,27 +83,28 @@ def __repr__(self):
return str(self)

def __setattr__(self, key, value):
# check for floats
try:
value = np.float64(value)
except (TypeError, ValueError):
pass
pass # fail silently if not float, eg: pvconst or _calc_now
super(PVcell, self).__setattr__(key, value)
# after all attributes have been initialized, recalculate IV curve
# every time __setattr__() is called
if hasattr(self, 'pvconst'):
# recalculate IV curve
if self._calc_now:
Icell, Vcell, Pcell = self.calcCell()
super(PVcell, self).__setattr__('Icell', Icell)
super(PVcell, self).__setattr__('Vcell', Vcell)
super(PVcell, self).__setattr__('Pcell', Pcell)
self.__dict__.update(Icell=Icell, Vcell=Vcell, Pcell=Pcell)

def update(self, **kwargs):
"""
Update user-defined constants.
"""
# TODO: use __dict__.update(), check for floats and update IV curve
# self.__dict__.update(kwargs)
# turn off calculation flag until all attributes are updated
self._calc_now = False
# don't use __dict__.update() instead use setattr() to go through
# custom __setattr__() so that numbers are cast to floats
for k, v in iteritems(kwargs):
setattr(self, k, v)
self._calc_now = True # recalculate

@property
def Vt(self):
Expand Down Expand Up @@ -276,4 +282,4 @@ def plot(self):
plt.xlim(0, self.Voc)
plt.ylim(0, (self.Isc + 1) * self.Voc)
plt.grid()
return cell_plot
return cell_plot
4 changes: 1 addition & 3 deletions pvmismatch/pvmismatch_lib/pvmodule.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,8 @@ def __init__(self, cell_pos=STD96, pvcells=None, pvconst=PVconstants(),
self.Vbypass = Vbypass #: [V] trigger voltage of bypass diode
self.cellArea = cellArea #: [cm^2] cell area
if pvcells is None:
# faster to use copy instead of making each object in a for-loop
# use copy instead of deepcopy to keep same pvconst for all objects
# PVcell.calcCell() creates new np.ndarray if attributes change
pvcells = PVcell(pvconst=self.pvconst)
# expand pvcells to list
if isinstance(pvcells, PVcell):
pvcells = [pvcells] * self.numberCells
if len(pvcells) != self.numberCells:
Expand Down
15 changes: 9 additions & 6 deletions pvmismatch/pvmismatch_lib/pvstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,21 @@ def __init__(self, numberMods=NUMBERMODS, pvmods=None,
self.pvconst = pvconst
self.numberMods = numberMods
if pvmods is None:
# use deepcopy instead of making each object in for-loop, 2x faster
pvmods = PVmodule(pvconst=self.pvconst)
# expand pvmods to list
if isinstance(pvmods, PVmodule):
pvmods = [pvmods] * self.numberMods
# reset pvconsts in all pvcells and pvmodules
for p in pvmods:
for c in p.pvcells:
c.pvconst = self.pvconst
p.pvconst = self.pvconst
if len(pvmods) != self.numberMods:
# TODO: use pvmismatch exceptions
raise Exception("Number of modules doesn't match.")
# check that pvconst if given, is the same for all cells
# don't assign pvcell.pvconst here since it triggers a recalc
for p in pvmods:
for c in p.pvcells:
if c.pvconst is not self.pvconst:
raise Exception('PVconstant must be the same for all cells')
if p.pvconst is not self.pvconst:
raise Exception('PVconstant must be the same for all cells')
self.pvmods = pvmods
self.Istring, self.Vstring, self.Pstring = self.calcString()

Expand Down
2 changes: 1 addition & 1 deletion pvmismatch/pvmismatch_lib/pvsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def __init__(self, pvconst=PVconstants(), numberStrs=NUMBERSTRS,
if pvstrs is None:
pvstrs = PVstring(numberMods=self.numberMods, pvmods=pvmods,
pvconst=self.pvconst)
# use deep copy instead of making each object in a for-loop
# expand pvstrs to list
if isinstance(pvstrs, PVstring):
pvstrs = [pvstrs] * self.numberStrs
if len(pvstrs) != self.numberStrs:
Expand Down
17 changes: 16 additions & 1 deletion pvmismatch/tests/test_pvcell.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,21 @@ def test_pvcell_calc_rbd():
pvc2 = PVcell(bRBD=-0.056)
ok_(isinstance(pvc2, PVcell))



def test_pvcell_calc_now_flag():
pvc = PVcell()
itest, vtest, ptest = pvc.Icell, pvc.Vcell, pvc.Pcell
pvc._calc_now = False
pvc.Rs = 0.001
assert np.allclose(itest, pvc.Icell)
assert np.allclose(vtest, pvc.Vcell)
assert np.allclose(ptest, pvc.Pcell)
icell, vcell, pcell = pvc.calcCell()
pvc._calc_now = True
assert np.allclose(icell, pvc.Icell)
assert np.allclose(vcell, pvc.Vcell)
assert np.allclose(pcell, pvc.Pcell)


if __name__ == "__main__":
test_calc_series()