Thread Previous • Date Previous • Date Next • Thread Next |
Martin Sandve Alnæs wrote:
2008/10/22 Anders Logg <logg@xxxxxxxxx>:On Wed, Oct 22, 2008 at 07:27:06PM +0200, Martin Sandve Alnæs wrote:2008/10/22 Anders Logg <logg@xxxxxxxxx>:On Wed, Oct 22, 2008 at 11:55:10AM +0200, Martin Sandve Alnæs wrote:2008/10/22 Johan Hake <hake@xxxxxxxxx>:On Wednesday 22 October 2008 11:12:02 Martin Sandve Alnæs wrote:2008/10/22 Johan Hake <hake@xxxxxxxxx>:On Wednesday 22 October 2008 10:17:43 Martin Sandve Alnæs wrote:2008/10/22 Johan Hake <hake@xxxxxxxxx>:On Wednesday 22 October 2008 09:32:31 Martin Sandve Alnæs wrote:2008/10/22 Johan Hake <hake@xxxxxxxxx>:On Tuesday 21 October 2008 23:23:27 Martin Sandve Alnæs wrote:2008/10/21 Johan Hake <hake@xxxxxxxxx>:On Tuesday 21 October 2008 22:34:04 Martin Sandve Alnæs wrote:2008/10/21 Johan Hake <hake@xxxxxxxxx>:On Tuesday 21 October 2008 21:37:13 Martin Sandve Alnæs wrote:2008/10/21 Anders Logg <logg@xxxxxxxxx>:On Tue, Oct 21, 2008 at 06:01:53PM +0100, Garth N. Wellswrote:Anders Logg wrote:On Tue, Oct 21, 2008 at 04:45:01PM +0100, Garth N. Wellswrote:I have a few questions and thoughts regarding the new Function design * It's not clear to me what the intention is with user-defined functions. The functions Function::interpolate(...) never call eval(..), so they can't pick up user-defined values. Should Function::interpolate test for the presence of a GenericVector to decide whether or not the Function is discrete or user-defined?Yes, sorry. I've missed this. I'll fix it.* It would be useful to declare user-defined functions without associating a FunctionSpace. If we want to interpolate the function, a FunctionSpace must then be provided. Anyone see any problems with this?The reasoning here is that all Functions must always be associated with a FunctionSpace so that they may be correctly interpreted in forms and correctly plotted. When a Function is created in PyDOLFIN, it must always be associated with a certain FiniteElement (and in a while FunctionSpace). It would simplify the handling of Functions if they are always associated with a FunctionSpace.I agree that is makes life simple if every function has a space, but it is a bit clunky for declaring user-defined functions. The forms must be declared first to extract the finite element to create the function space. Could look nasty when a lot of functions are involved. We have a function Function::interpolate which takes a function space V as an argument and it interpolates the function u in V. What if we permit undefined function spaces (which perhaps only have a domain)? We would then interpolate the user defined function u in the provided space V. GarthAre user-defined functions ever used without being related to a particular element/function space? It don't think it will be very clumsy. The clumsy thing will be to (in C++) get from something compiled by a form compiler to a FunctionSpace. If we can make that operation smooth, then creating (user-defined) functions will be very simple and convenient. One just needs to supply the variable V holding the function space. The current way of extracting function space data from the form is not very nice (in C++). What would be the optimal way to initialize a FunctionSpace in C++? We could think of extending the code generation to generate code that makes this convenient.The current way of extracting function space data from the form is not very nice in Python either, since it doesn't work with compiled functions. (Never mind that the current code is FFC-specific, this will be the same with UFL). Using Python functors can easily make the assembly slower than solving the linear system, so it's not really interesting to do in real applications... To make a function object that is both of a C++ subclass of dolfin::Function and of the Python class ufl.Function, we can't use the fixed multiple inheritance solution in the current PyDOLFIN. We would have to define a new class dynamically in python, inheriting from both ufl.Function and the freshly compiled C++ Function subclass. After all this work cleaning up the Function class hierarchy, is that really something you want? I'm not sure if that is even possible to do while maintaining efficiency, with cross-language inheritance and SWIG directors and all that. If anyone has another solution, I'm very interested in hearing it! Otherwise, I'm all for keeping the ufl.Function objects used in form definition separated from dolfin.Function objects used in assembly.I agree with Martin that we need to have a solution for PyDOLFIN users that does not depend on using python functors, as it will take forever for a complex form together with a moderate mesh to just assemble the form. Is it possible to let compile_functions compile a cpp function, with a FunctionSpace and all, instead of a mesh as it is today. Then after doingIf you have a dolfin::FunctionSpace object already, there's no reason compile_functions can't take this instead of dolfin::Mesh. That's exactly the same and no problem at all.this compile_function extract the element, and instantiate a UFL/FFC/PyFunction-function, and "attach" the compiled version to it. ThisWhat I state above is that this "attachment" must be done with dynamic creation of a new class with multiple inheritance. And I am unsure whether this will work out properly with SWIG directors etc. I believe it _may_ work, but I don't dare to keep my hopes up :-)Ok, I get it. For a moment I thought we could get away by defineing our own PyDOLFIN::Function class that could inherit from UFL/FFC, and then have a cpp_Function, but I realise this will not work.See the attached python file for a prototype of dynamic class creation with multiple inheritance using pure python classes. (I think this is called "aspect oriented programming" by some people)can be used to define forms, but more important it can be handed to the python assembly that check if the function has a compiled version attached to it and send this to the cpp_assembler?If the "attachment" is anything other than inheritance, it will have to be checked with manually written python code _everywhere_ a dolfin::Function is expected... We can't have one kind of functions for assembly and one for other stuff.Ok, I guess we have three different cases: 1) PyFunctions inherting from both UFL/FFC and cpp_Function as today, taking a functionsspace in its constructor. This will work with both user defined and discrete functions, more or less as we have it today. 2) The special functions, MeshSize, etc, can also be defined in the same way as now, right? 3) Using compile_functions, that creates a multi inheritance object that can be sent to any function expecting a cpp_Function, without manually extending the python interface.I'm with you up to this point.Could the last be done by letting compile_function create a muliti inheritance Function. Instantiate the cpp_one with the function space and by that creating a dummy cpp_function. Then "attach" the compiled function to a protected attribute and define eval, by overloading it in python. This will then just call the attached and compiled cpp_functions eval.What you describe here sounds like the envelope-letter design that was just _removed_ from dolfin.Yes, but only for compiled functions in Python. No other places.What I'm suggesting is that compile_functions dynamically creates a Python class that inherits from ufl.Function and the freshly compiled C++ class, which is a dolfin::Function subclass. Then it can construct an object of this new class, passing a FunctionSpace object given by the user to the dolfin::Function constructor, and an ufl.FiniteElement to the ufl.Function constructor.This sounds doable. I realize now that this was what you were talking about in your previous emails, but I did not get it until now ;)This of course requires that dolfin.FunctionSpace is a Python subclass of dolfin::FunctionSpace with an additional ufl.FiniteElement member variable. Using jit, dolfin.FunctionSpace can compile the ufc::finite_element and ufc::dof_map classes it needs from an ufl.FiniteElement. And then there's the issue of reusing dofmaps, where DofMapSet enters the play...Do we need to jit compile ufc::finite_elements and ufc::dof_maps from the created ufl.FiniteElement? What about the one that follows from the FunctionSpace?I was thinking about when _constructing_ the FunctionSpace. Just like PyDOLFIN uses jit in Function.__init__ today.Ok, something like: # Note pseudo code... class FunctionSpace(cpp_FunctionSpace): def __init__(self,ufl_finite_element,mesh): ufc_finit_element = jit(ufl_finite_element) form = ufl.FiniteElement*ufl.TestFunction*ufl.dx dof_map = jit(form) cpp_FucntionSpace.__init__(mesh,ufc_FinitElement,dof_map) self._UFL_FiniteElement = ufl_finite_element def UFL_FiniteElement(self): return self._UFL_FiniteElement By this the the ufc_element, ufl_element, the dofmaps and the mesh, are cached in the FunctionSpace. The Function would then be something like: class Function(cpp_Function,ufl.Function): def __init__(self,function_space): cpp_Function.__init__(function_space): ufl.Function.__init__(function_space.UFL_FiniteElement()) and dynamical created code in compile_functions() class MyFunction(MyCompiledFunction,ufl.Function): def __init__(self,function_space): MyCompiledFunction.__init__(function_space): ufl.Function.__init__(function_space.UFL_FiniteElement())Something like that, yes. This is close to the current PyDOLFIN. But FunctionSpace might become a subclass of ufl.FunctionSpace if we introduce that in UFL, and it should be possible to get cached initialized and renumbered DofMaps from a DofMapSet. Since a DofMapSet will typically be initialized with a Form, a Form depends on a Function, and a Function depends on a FunctionSpace which should be initialized by the DofMapSet, we have a cirular dependency right there.But won't you have this circular dependency in UFL already?In UFL this is simple: FiniteElement depends on nothing Function depends on FiniteElement Form depends on FunctionI ment for a potential FunctionSpace class in UFL.Then it is simply: FiniteElement depends on nothing FunctionSpace depends on FiniteElement Function depends on FunctionSpace Form depends on FunctionThe relation between UFL and UFC code is (at a certain level) simple: ufc::* is generated by form compilers from ufl.* (equivalently ffc.*) once generated, ufc::* depends on nothing In DOLFIN (C++) it is also simple: dolfin::* does not depend directly on ufl.* (equivalently ffc.*) dolfin::* depends on ufc::* PyDOLFIN could (should!) be kept simple. I've been using PyDOLFIN without the FFC-dependent multiple inheritance stuff all the time with the dolfin.cpp_* classes.Me too.I believe that's also how it must be done if you have external UFC code, e.g. precompiled in a library or manually written.Agree.So no matter what, it should at least be possible to avoid relying on the multiple inheritance and JIT in PyDOLFIN.and more or less have the possibilities we have today. I agree. JohanSeems at least we're on the same page here.I've been in meetings the whole day and it took me a while to get through the discussion... :-) I agree that life would be easier if we did not need to worry about integrating UFL, UFC, and DOLFIN, but I think we should. I don't think a user should need to worry about the relations between all the different flavours of for example the finite element abstractions. We have quite a few: ufl.FiniteElement ufc::finite_element dolfin::FiniteElement dolfin.FiniteElement FIAT.FiniteElement (ffc.FiniteElement) ...There are different kinds of "worry about" though. Beginners might only want to copy literally what the manual and demos do. But I believe semi-technical users who want to debug their applications by looking into the underlying code would find clear separation between these types easier to comprehend. In reality, they would only need to worry about: ufl.FiniteElement ufc::finite_element dolfin::FiniteElement == dolfin.FiniteElementDOLFIN plays two roles here: 1. It functions as a user interface/problem-solving environment and as such should present a clean and simple interface where only one of the above flavours should be visible. 2. It provides a general assembler, and as such should provide an assembly algorithm that just works with ufc:: types (+ mesh and linear algebra). So, we need to provide (1) for general use (and make it efficient) but also allow (2) for experts. So far, I like Martin's idea with dynamic inheritance to solve (1).Do you have a solution for the circular dependency problem when reusing dofmaps?Do we really need the DofMapSet class? If all Functions are associated with a FunctionSpace, then they will automatically share the DofMap (if they are in the same space).True. Maybe we can just drop it then.
Yes, classes, like Form, which currently have (pointers to) FiniteElements and a DofMapSet can just have an Array of FunctionSpace pointers.
Garth
-- Martin _______________________________________________ DOLFIN-dev mailing list DOLFIN-dev@xxxxxxxxxx http://www.fenics.org/mailman/listinfo/dolfin-dev
Thread Previous • Date Previous • Date Next • Thread Next |