summaryrefslogtreecommitdiff
path: root/diffeq.py
blob: c9447ac323e1125ded157ebfb88515af4e6f6e12 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import time, types, itertools
from scipy.interpolate import interp1d
from scipy.integrate import odeint
from scipy.optimize import fmin

class DiffEq(object):
    """designed to make numerically solving diff eqs more readable
    
    setattr and getattr have been modified to provide the following effects:
        >>> self._foo=2 #setting an _under var makes it still callable by...
        >>> self.foo
        2
        >>> self.foo is self._foo
        True            #so they share the same namespace
        
        >>> # Now suppose foo is a class method and bar is in _flags.
        >>> self.foo    #finding the object having not found it before:
                        #self.foo() is called and stored to self.__foo
                        #self.__foo is returned
        
        >>> self.foo    #finding the object having asked for it before:
                        #self.__foo is returned
        
        >>> self.bar=2  #setting bar resets all self.__foos
        
        >>> self.foo    #self.foo() is called again, self.__foo returned
        
        >>> # You can also subvert the second mechanism using the first,
        >>> # as the mechansims do not recurse on themselves.
        >>> # Suppose _foo is a class method, bar is in _flags
        >>> self.foo
        <bound method self._foo of ... >
        >>> self._foo
        <bound method self._foo of ... >
        >>> self
        >>> # The class is prevented from creating ___trunder variables.
    
    Note that with this behavior you should treat all variable names, be they
    normal, _under, or __dunder, as in the same namespace. Don't even try to
    differentiate variable names by underscores.
    
    The benefit of this system is that every normal float will be optimized,
    while every _under float will be ignored completely, and flagging a var
    with _under will not change the behavior of any functions using it.
    Additionally, if you suddenly realize that foo should be dependent on
    bar and baz, you simply define:
        def foo(self):
            return do_stuff(self.bar, self.baz)
    and bar and baz will be optimized while foo will be ignored -- and no
    function code using foo needs to be rewritten.
    """
    def __init__(self):
        self._time = time.time()
        
        # All floats lacking an initial _underscore will be optimized.
        self.opt_vars = {}
        for key, value in vars(self).items():
            if key[0] == "_": continue
            if type(value) != float: continue
            self.opt_vars[key] = value
    def time(self): return time.time() - self.time
    
    def __getattribute__(self, attr):
        """Provides a few getattr psuedonyms:
        
        allows self.foo to return:
            self._foo  if self.foo DNE
            self.foo() if self.foo is a method
        see DiffEq's documentation for examples
        """
        sgetattr = super(DiffEq, self).__getattribute__
        try:
            value = sgetattr(attr)
            if type(value) == types.MethodType and attr[0] != '_':
                try: value = sgetattr("__" + attr)
                except AttributeError:
                    value = value()
                    setattr(self, "__" + attr, value)
        except AttributeError as err:
            if attr[0] == "_": raise err
            else: value = sgetattr("_" + attr)
        return value
    
    _flags = []
    def __setattribute__(self, attr):
        """Clears dunder updates when a flag variable is changed."""
        sgetattr = super(DiffEq, self).__getattribute__
        ssetattr = super(DiffEq, self).__setattribute__
        if attr in self.flags:
            for key, value in vars(self).items():
                if key[:2] != "__": continue
                if type(sgetattr(key[2:])) != types.MethodType: continue
                vars(self).remove(key)
        ssetattr(attr)
    
    def check_in(self, variables):
        """Usage is as follows.
        
        def method(self, var0=None, var1=None, ...):
            self.check_in(locals())
            return do_stuff_with_vars()
        """
        for key, value in variables.items():
            if value == None:
                variables[key] = getattr(self, key)
    
    def solve_diff_eq(self, diff_eq=None, init_vars=None, timespace=None):
        """wrapper for odeint and self.diff_eq"""
        self.check_in(locals())
        time0 = self.time()
        time = numpy.linspace(*timespace)
        soln = odeint(diff_eq, init_vars, time)
        print "  ::  took %s seconds" % (self.time() - time0)
        return time, soln
    
    #in optimize(): vars().update(self.opt_vars)
    def opt_fun(guesses, f, func, init_vars):
        """Optimize the free variables for the diff eq."""
        global r0, r_roll, length, m, Mass
        r0, r_roll, length, m, Mass = guesses
        
        time, soln = solve_fun(func, init_vars)
        
        phi, phi1, v_tan, tension, moment, momentum, max_arg = \
            interpret(soln)
        phi, phi1, v_tan, tension, moment, momentum, time = \
            map(lambda x: numpy.ndarray.__getslice__(x, 0, max_arg+1),
                [phi, phi1, v_tan, tension, moment, momentum, time])
        
        time, (phi, phi1, v_tan, tension, moment, momentum, max_arg) = \
            solver(func, init_vars, returns=['time', 'slices'])
        
        # Parameter values.
        f.write("%s, " % r_roll)
        f.write("%s, " % length)
        f.write("%s, " % r0)
        f.write("%s, " % m)
        f.write("%s, " % Mass)
        
        # Values at the max velocity point.
        f.write("%s, " %     phi[-1])
        f.write("%s, " %    phi1[-1])
        f.write("%s, " % tension[-1])
        f.write("%s\n" %   v_tan[-1])
        
        time, soln = self.solve_func(
        
        self.interpret()
        
        return self.optimize_value()