dolfin team mailing list archive
-
dolfin team
-
Mailing list archive
-
Message #10305
Re: new Function design
On Wednesday 22 October 2008 23:04:02 Martin Sandve Alnæs wrote:
> 2008/10/22 Johan Hake <hake@xxxxxxxxx>:
> > On Wednesday 22 October 2008 21:49:10 Martin Sandve Alnæs wrote:
> >> 2008/10/22 Anders Logg <logg@xxxxxxxxx>:
> >> > On Wed, Oct 22, 2008 at 09:36:27PM +0200, Johan Hake wrote:
> >> >> On Wednesday 22 October 2008 18:37:44 Anders Logg wrote:
> >> >> > 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)
> >> >> > ...
> >> >> >
> >> >> > 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).
> >> >>
> >> >> So you don't like the way we have it now, where a jit compiled
> >> >> function can't be used in a form statement? Or are you fine with it?
> >> >> If you are fine with it we do not need the dynamics inheritance.
> >> >>
> >> >> Johan
> >> >
> >> > I think that there should be an easier way to use jit-compiled
> >> > functions. The optimal would be if one could do something like
> >> >
> >> > f = Function(V, cppstring)
> >>
> >> That should be possible to do with some Python tricks (metaclasses).
> >
> > Isn't the above example only possible with an envelope design? I.e. you
> > compile the string and get a cpp_function.
> >
> > A metaclass kicks in when you creates a python class. You could e.g.
> >
> > __metaclass__ = compiler_class_builder
> > MyFunction(Function):
> > cppstring = "whatever"
> >
> > and the metaclass compiler_class_builder can compile the cppstring while
> > createing the class, and the instantiation would be:
> >
> > f = MyFunction(V)
> >
> > That still does not help us using that class for dynamical inheritance.
> >
> > We could use:
> >
> > f = CompiledFunction(V,cppstring)
> >
> > which resembles what we have today. Here CompiledFunction is a function
> > not a class. Maybee still not what we want?
> >
> > Johan
>
> Using CompiledFunction like this is close enough if we can't use
> metaclasses. But then again, it's much faster to compile multiple functions
> simultaneously like compile_functions can do today.
With the introduction of function spaces, and using them in the call to
compile functions, we can only compile functions with the same space,
limiting the nmber of functions we can compile at the same time.
> I haven't actually used metaclasses for anything useful, but the point is
> that you can return an object of a different class than Function with
> the syntax "f = Function(...)", right?
Well, metaclasses are the 'types' of classes. The base metaclass is type, and
should also be the baseclass of all metaclasses.
class A(object):pass
print type(A) # Will print <type 'type'>
class ACreator(type):pass
class A(object):
__metaclass__ = ACreator
print type(A) # Will print <class '__main__.ACreator'>
Metaclasses are used when a class is created. I have only used them to change
class attributes when creating a class. But I have looked into it again and
we should be able to change what classes a class inherit from. This can be
usefull to fix the dynamical inheritance feature we have talked about. (in
contrast to what I wrote above, this can be done using metaclasses :P)
> Since Function here is a manually implemented Python class, it can
> use the metaclass mechanism to call up the dynamic class creation
> if it gets a cppstring and return an object of this class instead.
You use a metaclass when a _class_ is created, not when an instance is
created.
We could, as I wrote above use a metaclass in Function to override the
inheritance of cpp_Function. I have done that in the attached script.
Here is an example on how the syntax could look like using this approach:
MyCompiledFunction(Function):
cppcode = "Whatever"
f = MyCompiledFunction(V)
This is maybee not much better than:
f = CompileFunction(V,cppcode)
We also have a problem with the type of f. isinstance(f,Function) will return
False in both cases. This is maybee not a big problem using CompileFunction,
as you won't expect it, but in the first metaclass example it is unintuitive.
This last thing could maybee be fixed with more digging.
Enough digging for now...
Johan
Attachment:
metaclasstester.py
Description: application/python
References