← Back to team overview

dolfin team mailing list archive

Re: new Function design

 


On Tue, 21 Oct 2008, 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.

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

I agree with this last statement very much!!!!  :(

- Shawn

References