I submitted my pull request a little while ago for my code. Currently, it looks like I’m waiting on a few things. I think the biggest thing is the pull request Brian Granger has opened. I’ve already written a branch which uses his code, and everything works. I just have to wait for his pull request to get accepted in order to switch what is in my pull request. Then of course, that has to get approved. This delay has had some positive benefits though.
Working with other people’s branches has definitely helped my familiarity and confidence with using git. Using the fetch and merge commands with multiple remotes, along with getting merge errors and fixing them, has given some me good experience. Also, rebasing to play with commit history, and amending commits has been helpful.
I feel like I had the functionality I wanted before from my ‘DynamicSymbol’ class, but with the ability to take the derivatives of functions now, with respect to functions means that this object is no longer needed really. What I did before was have ‘DynamicSymbol’ extend ‘Symbol’. I then used the ability of the ‘symbols’ function to to supply my own class for creation in the ‘dynamicsymbols':
def dynamicsymbols(names): """Wraps sympy.symbols to use DynamicSymbol. """ return symbols(names, cls=DynamicSymbol)
This was an OK solution. Now though, one can use functions to represent these time varying quantities; instead of q1, q1d, q1dd we’ll have q1(t), Derivative(q1(t), t), etc. This is certainly more consistent with the rest of SymPy. Now my ‘dynamicsymbols’ function uses symbols with ‘cls=Function’ to create a number of undefined function objects, which then are called with ‘(t)’ as an argument (I actually do something like dynamicsymbols._t = Symbol(‘t’). This way the time value is specified in only one place, but that place is still associated with dynamic symbols. The ability of Python to set an attribute to a function is certainly interesting…). I also added the ability to specify the level of differentiation; you can call:
In : dynamicsymbols('q1 q2') (q1, q2) In : dynamicsymbols('q1 q2', 1) (q1', q2')
This means you can’t create q1, q1′, q1” on one line, but at least all dynamic symbols of the same level of differentiation can be called together. The above snippet leads to the next topic though: printing.
When you start up ipython, and do the following:
In : from sympy.physics.mechanics import * In : dynamicsymbols('q1 q2', 1) Out: (Derivative(q1(t), t), Derivative(q2(t), t))
the output isn’t actually the same as what I presented a few lines above. This is because of the default printing of Derivative (shown in the line immediately above). When dealing with multibody dynamics problems, you get a lot of derivatives of these dynamic symbols (generalized coordinates and speeds). It gets out of control pretty fast if we print out every time derivative in the above form. So, what I did was to write my own printer which shows derivatives as q’ instead of Derivative(q(t), t), under certain conditions (and q” for Derivative(q(t),t,t) and so forth). The conditions are that that the derivative has to have been taken with respect to the time symbol (stored in dynamicsymbols._t, as previously mentioned) and that its first argument (the value which is being differentiated) has to be an ‘UndefinedFunction’ and that the derivative can only be taken with respect to t.
def _print_Derivative(self, e): from sympy.core.function import UndefinedFunction t = dynamicsymbols._t if (bool([i == t for i in e.variables]) & isinstance(type(e.args), UndefinedFunction)): ol = str(e.args.func) for i, v in enumerate(e.variables): ol += '\'' return ol else: return StrPrinter().doprint(e)
The above block of code shows how it is done; the first part of the undefined function function is added to our output string (e.args.func), then for each entry in e.variables we print a ‘. In this method, ‘e’ is the expression to be printed. Then our final list is returned, or just the normal printer’s output if the given expression did not meet our criteria. Now, this actually doesn’t print out everything perfectly:
In : Derivative(q1,t) q1' In : Derivative(q1*x,t) Derivative(x*q1(t), t) In : Derivative(q1*x,t, evaluate=True) x*q1'
We can still get examples where it prints out the long way: when evaluate is not true and the ‘Derivative’ object does not only contain an undefined function. I’m willing to live with this at the moment though, as I think that all my code uses ‘diff’ where evaluate always gets called. I also wrote a ‘_print_Function’ method, in order to print q1(t) as q1:
def _print_Function(self, e): from sympy.core.function import UndefinedFunction t = dynamicsymbols._t temp = StrPrinter().doprint(e) if isinstance(type(e), UndefinedFunction): return temp.replace('(t)', '') return temp
I think this one is even simpler. If the Function is an undefined function, print it to a string, then replace the ‘(t)’ in the string with nothing. Checking that it is an undefined function is definitely important, otherwise you get:
In : cos(t) cos
And I don’t think anyone wants that. So the last two bits of this are interactive printing and integration of my non-SymPy objects with the printing system.
In order to get my Vector and and Dyad to print out with a SymPy printer correctly, I had to do the following: have one method (I chose __str__) which creates the string representation of the object, and then set __repr__ = __str__, _sympystr = __str__, _sympyrepr = __str__. Those last two functions are what the printer looks for, I believe. I’m actually still not completely clear on the difference between repr and str in Python, but I think one is supposed to satisfy: x == eval(repr(x)), except that I’m not sure if it is repr or str there. Additionally, in doctests, unless you do print, a returned value which you check against is called with repr I believe. SymPy does not exactly follow the above obj=eval(repr(obj)), and now, my code doesn’t always either, which I think is an acceptable tradeoff considering the readability issues. The way that the printer (which currently only has _print_Derivative and _print_Function methods overridden) is used by vector is as follows: the string value of a vector’s non-zero measure number is obtained with MechanicsStrPrinter().doprint(ar[i]). Here ar[i] is what I am supplying, but whatever is put in .doprint() will be printed by the printer.
Now for most of the work in physics.mechanics, you’ll be dealing with Vector quantities, so my printer is used automatically. But at the end of forming Kane’s equations, you are left with Fr + Fr* = 0. This is a r x 1 vector represented by a SymPy Matrix of the same shape. It will also be full of qdots and udots (generalized coordinate and speed time derivatives). This can end up being the biggest expression generated in forming the equations of motion, so being concise where it is practical is a good idea. Unfortunately my printer won’t get called here, because Matrix uses the default StrPrinter. Enter the display hook:
def mechanics_printing(): def mydhook(ar): print MechanicsStrPrinter().doprint(ar) sys.displayhook = mydhook return 'displayhook set'
This function, mechanics_printing(), will be called by the user at the beginning of an interactive session in order to print out Derivatives and Functions which are not part of a mechanics Vector in the way I have chosen. You can set sys.displayhook to use a custom printing function of your creation. Here, a one line function to just use my printer is defined, then the system displayhook is set to use my displayhook. At this point I learned there is no such thing as a “void” function in Python; everything has to return a value, and if you don’t, None is automatically returned. I just had it print that the displayhook was set, rather than it printing ‘None’ when you call this function. I believe this only sets the interactive printing though; I’m not sure what will be printed when you have a script file you have written and it prints out the screen or a file; this still needs to be worked out.
Hopefully all of this will help out someone in the future with their SymPy printing issues.