Hi Justin,
I'm kind of surprised that the vector access is the slow bit here (as opposed to the solve), but yes, you can make this much faster. There are two ways of getting access to a numpy array containing the DoF values. One is:
u.vector().array()
the other is:
u.dat.data (or u.dat.data_ro if you want to tell PyOP2 you're not going to change the data).
The two are essentially equivalent for scalar Functions. For functions in a VectorFunctionSpace the dat version returns a 2D array while vector() is flattened to 1D.
Regards,
David