dolfin team mailing list archive
-
dolfin team
-
Mailing list archive
-
Message #10353
Re: Work on a new function.py
2008/10/24 Johan Hake <hake@xxxxxxxxx>:
> On Friday 24 October 2008 10:42:23 Martin Sandve Alnæs wrote:
>> 2008/10/23 Johan Hake <hake@xxxxxxxxx>:
>> > Hello!
>> >
>> > I have worked on a "new" function.py. Please have a look. It is built
>> > around a metaclass for Functions. It makes it "easy" to create both a)
>> > userdefined python functions and b) compiled functions.
>> >
>> > The work flow is the same for both cases:
>> >
>> > 1) Define a class
>> > 2) Instantiate it with a FunctionSpace, and any user specific arguments
>> >
>> > With the suggested design a user can define classes a la:
>> >
>> > class MyCustomFunction(Function):
>> > def __init__(self,V,dummy):
>> > # Note no call to Function.__init__!
>> > self.dummy = dummy
>> >
>> > def eval(self,val,x):
>> > val[0] = 0.5
>> > val[1] = 0.5
>> >
>> > def dim(self):
>> > return 2
>> >
>> > def rank(self):
>> > return 1
>> >
>> > # Can also declare dim and rank as int attributes, see below
>> >
>> > class MyCustomCompiledFunction(Function):
>> > cppcode = "Jada"
>> > dim = 1
>> > rank = 0
>> >
>> > They are then instantiated by:
>> >
>> > mesh = UnitSquare(10,10)
>> > element = FiniteElement('Lagrange','triangle',1)
>> >
>> > print ""
>> > V = FunctionSpace(mesh,element)
>> >
>> > print ""
>> > f0 = MyCustomFunction(V,"Dummy")
>> >
>> > print ""
>> > f1 = MyCustomCompiledFunction(V)
>> >
>> > try:
>> > # Cannot instantiate the Function class
>> > f2 = Function(V)
>> > except Exception, e:
>> > print e
>> >
>> > With this interface only one user defined function can be compiled at a
>> > time, but we will gain in similare workflow between pure python and
>> > compiled functions.
>> >
>> > I realize that the C++ design must be in place first before we can
>> > advance, but this is a proof of principle, that we can use metaclasses to
>> > do nice stuff for the end user.
>> >
>> > Johan
>>
>> A possible improvement: deducting dim and rank from cppcode like
>> compile_functions does today (can probably just reuse the code).
>
> I had that in mind. But a common error for user defined functions is to not
> define the dim and rank, producing nasty errors. My thought was to force the
> user to define these, at least in the python interface. Then we can choose to
> not force them to be set in the compiled interface, but forcing the user to
> do it make the interfaces consistent.
I think forcing the user to set them when we can't deduct them
and give good error messages is the best of both worlds.
>> Also, compile_functions extracts variable names from the strings.
>> Then we can do just:
>>
>> class MyFunc(Function):
>> cppcode = ("t*sin(x[0]", "cos(x[1])", "x[0]*x[2]")
>>
>> f = MyFunc(V)
>> f.t = 0.0
>
> That's right. We could also extract them from the cppcode before we send them
> to the code compiler, createing an __init__ function on the fly, which the
> code already do, so we will get:
>
> class MyFunc(Function):
> cppcode = ("t*sin(x[0]", "cos(x[1])", "x[0]*x[2]")
>
> f = MyFunc(V,0.0)
Perhaps better to require keyword arguments for those:
class MyFunc(Function):
cppcode = ("t*sin(x[0]", "cos(x[1])", "x[0]*x[2]")
f = MyFunc(V, t = 0.0)
> Maybe this would be a bit too magic? This is only possible when the cppcode is
> given in the nice tuple representation you already have implemented. This
> will be a bit more cumbersome when a whole code segment is handed, but
> probaby doable with the right regexp? If not we could add an extra
> attribute, 'args' which take a list of strings that are added to the created
> __init__ function and then passed to the compiled cpp_function.
What do you mean by "whole code segment"? Implementation of eval?
Or code for the whole class? We could check for either "cppcode"
or "evalcode" to have a distinction between the two. In the latter case,
the constructor arguments can be found with a regexp.
class MyFunc(Function):
evalcode = ("t*sin(x[0]", "cos(x[1])", "x[0]*x[2]")
f = MyFunc(V, t = 0.0)
class MyFunc(Function):
cppcode = """
class MyFunc(Function)
{
MyFunc(FunctionSpace & V, MeshFunction & mf):
Function(V), mf(mf) {}
void eval(...) {}
};"""
f = MyFunc(V, mf = my_mesh_function)
_However_, the big question is whether SWIG directors will
kick in and destroy performance or not with this code.
As long as we inherit cpp_Function in any Python base class
of MyCompiledFunction, I'm not sure if this will work out.
>From function.py:
f1 = MyCustomCompiledFunction(V)
print "f1 bases:"
print f1.__class__.__bases__
f1 bases:
(<class '__main__.Function'>, <class '__main__.custom_cpp_Function'>,
<class ffc.compiler.language.algebra.Function at 0x83ce77c>)
--
Martin
Follow ups
References