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 >>> self._foo >>> 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 _gettime(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 solve_diff_eq(self): """wrapper for odeint and self.diff_eq requires the following: self.diff_eq self.init_vars self.timespace """ time0 = self.gettime() self.time = numpy.linspace(*self.timespace) self.soln = odeint(self.diff_eq, self.init_vars, self.time) print " :: took %s seconds" % (self.gettime() - time0) #in optimize(): vars().update(self.opt_vars) def opt_fun(self, guesses, f, func, init_vars): """Optimize the free variables for the diff eq.""" for key, val in zip(opt_vars.iterkeys(), guesses): setattr(self, key, val) #map(setattr, self, opt_vars.iterkeys(), guesses) self.__dict__.update(dict(zip(self.opt_vars.iterkeys(), guesses))) outfile = self.__dict__.get('outfile') 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()