dolfin team mailing list archive
-
dolfin team
-
Mailing list archive
-
Message #10276
Re: new Function design
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. Wells
> >> >> >
> >> >> > wrote:
> >> >> >> >> >> >> >> >> Anders Logg wrote:
> >> >> >> >> >> >> >> >> > On Tue, Oct 21, 2008 at 04:45:01PM +0100, Garth N.
> >> >> >> >> >> >> >> >> > Wells
> >> >> >> >
> >> >> >> > wrote:
> >> >> >> >> >> >> >> >> >> 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.
> >> >> >> >> >> >> >> >>
> >> >> >> >> >> >> >> >> Garth
> >> >> >> >> >> >> >> >
> >> >> >> >> >> >> >> > Are 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 doing
> >> >> >> >> >> >>
> >> >> >> >> >> >> If 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. This
> >> >> >> >> >> >>
> >> >> >> >> >> >> What 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 Function
> >> >
> >> > I 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 Function
> >>
> >>
> >> >> The 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.
> >> >
> >> > Johan
> >>
> >> Seems 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.FiniteElement
>
>
> > DOLFIN 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).
--
Anders
Attachment:
signature.asc
Description: Digital signature
Follow ups
References