def find_best(err, lower, upper):
'''wrapper around scipy optimisation function'''
if upper <= lower:
- print 'find_best bounds'
- return upper
+ raise Exception('lower<upper')
def err1(x):
return err(x[0])
(ret, n, err) = scipy.optimize.fmin_tnc(err1, [float(0.5*(lower+upper))],
# a cache hack ... we evaluate the same parms a lot of times
-runonce_ret = {}
-def runonce(function):
- def runonce_wrapper(*args):
+cacheargs_ret = {}
+def cacheargs(function):
+ def cacheargs_wrapper(*args):
argsf = (args, str(function))
- if argsf in runonce_ret:
- return runonce_ret[argsf]
+ if argsf in cacheargs_ret:
+ return cacheargs_ret[argsf]
else:
ret = function(*args)
- runonce_ret[argsf] = ret
+ cacheargs_ret[argsf] = ret
return ret
- return runonce_wrapper
+ return cacheargs_wrapper
+
+# cacheing for object functions
+cacheobj_ret = {}
+def cacheobj(function):
+ def cacheobj_wrapper(*args):
+ obj=args[0]
+ argsf = (args[1:], getattr(obj, 'Isc', 0), getattr(obj, 'Voc', 0), str(function))
+ if argsf in cacheobj_ret:
+ return cacheobj_ret[argsf]
+ else:
+ ret = function(*args)
+ cacheobj_ret[argsf] = ret
+ return ret
+ return cacheobj_wrapper
-@runonce
+@cacheargs
def Imodel(V, Voc, Isc, a0, a1, shaded):
'''the core of the cell model for current. Very rough'''
if shaded:
return -1e100
return Isc - (a0/1e7) * math.exp(V/a1)
-@runonce
+@cacheargs
def Vmodel(I, Voc, Isc, a0, a1, shaded):
'''the core of the cell model for voltage. Very rough'''
if shaded:
return math.log((Isc - I)/(a0/1e7))*a1
+@cacheargs
+def find_cell_params(Vmp, Voc, Isc, Imp):
+ '''find the cell variables given the key parameters'''
+ # build the error function for the cell mode
+ # by combinding the current error and the power
+ # error. This produces much better parameters
+ # than just fitting by current
+ dataI = [ (0, Isc), (Vmp, Imp), (Voc, 0) ]
+ dataP = [ (0, 0), (Vmp, Vmp*Imp), (Voc, 0) ]
+ def err(a):
+ ret = 0
+ for (x,y) in dataI:
+ I = Imodel(x, Voc, Isc, a[0], a[1], False)
+ ret += (I-y)*(I-y)
+ for (x,y) in dataP:
+ I = Imodel(x, Voc, Isc, a[0], a[1], False)
+ e = (I*x) - y
+ ret += e*e
+ return ret
+ (ret, x1, x2) = scipy.optimize.fmin_tnc(err, [1, 1],
+ bounds=[(float(0.01), float(100)),
+ (float(0.01), float(100))],
+ messages=0,
+ maxfun=10000,
+ approx_grad=True,
+ xtol=1e-30)
+ return ret
+
+
def sumV(m, I):
'''add up a set of PV like elements in series'''
s = m[0]
Voc = cell_Voc/self.cell_scale
Isc = cell_Isc
Imp = cell_Imp
- # build the error function for the cell mode
- # by combinding the current error and the power
- # error. This produces much better parameters
- # than just fitting by current
- dataI = [ (0, Isc), (Vmp, Imp), (Voc, 0) ]
- dataP = [ (0, 0), (Vmp, Vmp*Imp), (Voc, 0) ]
- c = self
- def err(a):
- ret = 0
- self.a[0] = a[0]
- self.a[1] = a[1]
- for (x,y) in dataI:
- e = c.I(x) - y
- ret += e*e
- for (x,y) in dataP:
- e = c.P(x) - y
- ret += e*e
- return ret
- (ret, x1, x2) = scipy.optimize.fmin_tnc(err, [1, 1],
- bounds=[(float(0.01), float(100)),
- (float(0.01), float(100))],
- messages=0,
- maxfun=10000,
- approx_grad=True,
- xtol=1e-30)
- self.a[0] = ret[0]
- self.a[1] = ret[1]
-
+ self.a = find_cell_params(Vmp, Voc, Isc, Imp)
def I(self, V):
return Imodel(V, self.Voc, self.Isc, self.a[0], self.a[1], self.shaded)
class subpanel:
'''model a subpanel'''
def __init__(self):
- numcells = cells_per_subpanel
self.cells = []
- for i in range(0, numcells):
+ for i in range(0, cells_per_subpanel):
self.cells.append(cell())
- self.cells[0]._recalc()
- for i in range(1, numcells):
- self.cells[i].a = self.cells[0].a
self._recalc()
def _recalc(self):
ret = sumV(self.cells, I)
return ret
+ @cacheobj
def I(self, V):
+ if len(self.cells) == 1 and V < self.Voc:
+ return self.cells[0].I(V)
def Verr(x):
r = abs(V - self.V(x))
return r
def V(self, I):
return sumV(self.subpanels, I)
+ @cacheobj
def I(self, V):
+ if len(self.subpanels) == 1 and V < self.Voc:
+ return self.subpanels[0].I(V)
def Verr(x):
return abs(V - self.V(x))
return find_best(Verr, -cell_Isc, cell_Isc)
return self.Voc
return sumV(self.panels, I)
+ @cacheobj
def I(self, V):
if V >= self.Voc and blocking_diodes:
return 0
sumI += s.I(V)
return sumI
+ @cacheobj
def V(self, I):
def Ierr(x):
return abs(I - self.I(x))
I = self.I(V)
return I * V
+ @cacheobj
def MPPV(self):
def Perr(x):
p = self.P(x)