← 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 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 :-)

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.

> I guess it will not be a problem to have an implementation similare to the one
> we have today for python functors. Instead of sending an element and a mesh,
> to the function when instantiating it we send, a FunctionSpace, and then
> extract the element from this and instantiate the FFC/UFL version of the
> Function with that?
>
> Am I totaly off? :)
>
> Johan

Sending a dolfin::FunctionSpace (C++ object) around introduces
no new issues compared to sending dolfin::Mesh around.
It's the interaction between multiple inherited stuff across
C++ and Python that causes the mess.

--
Martin
class A:
    def __init__(self, *args):
        self.a_args = args
    def a(self):
        print "a", self.a_args

class B:
    def __init__(self, *args):
        self.b_args = args
    def b(self):
        print "b", self.b_args

def construct(c1, c2, args1, args2):
    class C(c1, c2):
        def __init__(self, a, b):
            c1.__init__(self, *a)
            c2.__init__(self, *b)
    o = C(args1, args2)
    return o

x = construct(A, B, (1,2,3), (4,5,6))
x.a()
x.b()


Follow ups

References