← Back to team overview

dolfin team mailing list archive

Re: new Function design

 

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.
>> >> >
>> >> > --
>> >> > Anders
>> >>
>> >> 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. 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 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...

> This will hopefully work in all cases that expect a cpp_Function? In assembly
> where we need speed we extract the compiled function and send that one to
> cpp_assembly.

I'm not sure what you mean by extracting.

> Neat? No... but could work!

And it is pretty much guaranteed to blow the minds
of any non-experts who tries to understand it.

--
Martin


Follow ups

References