← Back to team overview

dolfin team mailing list archive

Re: new Function design

 



Martin Sandve Alnæs wrote:
2008/10/22 Anders Logg <logg@xxxxxxxxx>:
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).

True. Maybe we can just drop it then.


Yes, classes, like Form, which currently have (pointers to) FiniteElements and a DofMapSet can just have an Array of FunctionSpace pointers.

Garth


--
Martin
_______________________________________________
DOLFIN-dev mailing list
DOLFIN-dev@xxxxxxxxxx
http://www.fenics.org/mailman/listinfo/dolfin-dev




References