← Back to team overview

dolfin team mailing list archive

Re: new Function design

 

2008/10/21 Martin Sandve Alnæs <martinal@xxxxxxxxx>:
> 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


However, I do agree that keeping track of the order of Functions in a
form is a real pain, in particular when reusing code for slightly
different equations.

As an example, consider code along these lines:

from ufl import *
...
f = Function(element)
g = Function(element)
a = f*g*...*dx

from dolfin import *
...
V = ...
fc, gc = compiled_functions([..., ...], V)


Then instead of (or in addition to):
A = assemble(mesh, a, coefficients=[fc, gc])


We can (at least with UFL) easily implement:
A = assemble(mesh, a, coefficients={ g: gc, f: fc })


Here the user explicitly associates (~= "attaches" using Johans wording)
dolfin::Function objects to ufl.Function objects, and assemble can figure
out the correct order. It's not as elegant as the multiple inheritance solution
that can be used with PyDOLFIN/FFC and Python functors today,
but it's good enough for me and will avoid all these messy crosslanguage
multiple inheritance issues.

--
Martin


References