cache object functions
authorAndrew Tridgell <tridge@samba.org>
Tue, 31 Aug 2010 05:07:09 +0000 (15:07 +1000)
committerAndrew Tridgell <tridge@samba.org>
Tue, 31 Aug 2010 05:07:09 +0000 (15:07 +1000)
pv_model

index 834495de3b34775434a0a784a71cbb8c8dd67c7c..460039d076fe500803ea845b5e4aea86e9ad310e 100755 (executable)
--- a/pv_model
+++ b/pv_model
@@ -38,8 +38,7 @@ blocking_diodes = False
 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))],
@@ -81,19 +80,33 @@ def temperature_adjust(t):
 
 
 # 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:
@@ -107,7 +120,7 @@ def Imodel(V, Voc, Isc, a0, a1, 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:
@@ -122,6 +135,35 @@ def Vmodel(I, Voc, Isc, a0, a1, 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]
@@ -152,34 +194,7 @@ class cell:
         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)
@@ -197,13 +212,9 @@ class cell:
 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):
@@ -228,7 +239,10 @@ class subpanel:
         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
@@ -266,7 +280,10 @@ class panel:
     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)
@@ -305,6 +322,7 @@ class string:
             return self.Voc
         return sumV(self.panels, I)
 
+    @cacheobj
     def I(self, V):
         if V >= self.Voc and blocking_diodes:
             return 0
@@ -340,6 +358,7 @@ class pvarray:
             sumI += s.I(V)
         return sumI
 
+    @cacheobj
     def V(self, I):
         def Ierr(x):
             return abs(I - self.I(x))
@@ -358,6 +377,7 @@ class pvarray:
         I = self.I(V)
         return I * V
 
+    @cacheobj
     def MPPV(self):
         def Perr(x):
             p = self.P(x)