dolfin team mailing list archive
-
dolfin team
-
Mailing list archive
-
Message #10530
Re: Patch with fixes to PyDOLFIN
2008/11/3 Johan Hake <hake@xxxxxxxxx>:
> On Monday 03 November 2008 09:00:58 Martin Sandve Alnæs wrote:
>> 2008/11/2 Johan Hake <hake@xxxxxxxxx>:
>> > On Sunday 02 November 2008 17:44:39 Anders Logg wrote:
>> >> On Sun, Nov 02, 2008 at 04:28:45PM +0100, Johan Hake wrote:
>> >> > Hello!
>> >> >
>> >> > I have fixed the swig interface so it compiles. I had to add
>> >> > SubFunction in dolfin_function.h. If we do not want this we have to
>> >> > add
>> >> >
>> >> > #include "SubFunction.h"
>> >> >
>> >> > to Function.h, as it is in 0.8.1.
>> >> >
>> >> > As function inherits ufc::function I also %imported ufc.h and added
>> >> > ufc-1 as swig dependencies in scons.cfg.
>> >> >
>> >> > I %renamed function.in to function._in and %extended the interface to
>> >> > SubFunction so one can write
>> >> >
>> >> > u in V
>> >> >
>> >> > as discussed previously.
>> >>
>> >> Very nice!
>> >>
>> >> > I am curious of how we will fix the whole PyDOLFIN interface with the
>> >> > precompiled function spaces aso. These can all be hidden in the
>> >> > assemble function and/or in the LinearPDE class. But what if we want
>> >> > to expose a FunctionSpace from a compiled form? Do we want this?
>> >> >
>> >> > Or what if we want to create a FunctionSpace from an element and a
>> >> > mesh. This can be done more or less in the same way as we have solved
>> >> > Function in 0.8.1, but then we miss the whole point of reusing
>> >> > FunctionSpaces.
>> >> >
>> >> > Any good suggestions?
>> >> >
>> >> > Johan
>> >>
>> >> Here's a good suggestion... :-)
>> >>
>> >> We create a new Python class (in site-packages/dolfin/functionspace.py)
>> >> which either inherits from FormCompiler.FiniteElement and
>> >> dolfin::FunctionSpace or owns such objects as member data.
>> >
>> > I suppose it have to inherit from FormCompiler.FiniteElement, but it only
>> > have to owe a cpp:FunctionSpace.
>>
>> To clarify: The (only?) reason for owning and not inheriting is that
>> we want to reuse function spaces.
>>
>> > So the __init__ function in FunctionSpace would be something like:
>> >
>> > def __init__(self, mesh, family, order):
>> > FormCompiler.FiniteElement.__init__\
>> > (self,family,dim2shape(mesh.geometry().dim(),order))
>> > if self.value_dimension(0) > 1:
>> > form = ffc.TestFunction(self)[0]*ffc.dx
>> > else:
>> > form = ffc.TestFunction(self)*ffc.dx
>> > (compiled_form, module, form_data) = jit(form,mesh)
>> > self._V = compiled_form.function_space(0)
>> >
>> > and then we can add something like
>> >
>> > def cpp_function_space(self):
>> > return self._V
>> >
>> > to the interface, where the latter function is used in the __init__
>> > function of the Function class to initialize the cpp_Function?
>> >
>> > Maybee we for compatability should let FunctionSpace inherit
>> > cpp_FunctionSpace. If we do that the __init__ function from above would
>> > look a bit differently.
>>
>> To still allow reusing FunctionSpaces, it would have to be some kind
>> of envelope-letter design then.
>
> Can't it just pass it self to where ever it is needed, instead of passing the
> function space that it owe?
That's the point of inheritance, yes. And that's why "itself" must act
as an "envelope" around the "letter" that it owns by forwarding calls,
giving an envelope-letter design.
>> >> In PyDOLFIN, FunctionSpace will then be something that replaces both
>> >> FormCompiler.FiniteElement and dolfin::FunctionSpace:
>> >>
>> >> V = FunctionSpace(mesh, "Lagrange", 1)
>> >> v = TestFunction(V)
>> >> u = TrialFunction(V)
>> >> f = Function(V)
>> >> a = dot(grad(v), grad(u))*dx
>> >> L = v*f*dx
>> >> A = assemble(a)
>> >> b = assemble(L)
>> >>
>> >> Note that the "triangle" or "tetrahedron" argument is not needed since
>> >> this can be deduced from the mesh.
>> >
>> > Looks nice. Would it then be possible to reuse the cpp_FunctionSpace that
>> > is initialized for V when the form is compiled using jit?
>>
>> The reuse must happen when V is constructed, so this has nothing to do
>> with compiling forms with jit.
>
> If V is constructed as suggested above, see __init__, jit needs to instantiate
> the functionspace from the compiled form module, and then use this to
> instantiate the form. (In this case where we only is interested in the
> actuall function space we do not need jit to instantiate the form though)
That jit is used to compile a finite_element and dof_map in FunctionSpace
is a separate use of jit from compiling the forms a and L. The
ufl.Form instances
a and L won't see the dolfin.FunctionSpace, only the ufl.FiniteElement.
So the form is never instantiated with any function space.
If we want to construct dolfin.Form objects in PyDOLFIN, we need to do
something more.
> But when jit is called in the assemble function, it needs to instantiate the
> form using the functions spaces that are provided through the uncompiled form
> instead of instantiating them from the compiled form module.
Yes. I think that can work out ok. When fetching ufl.FiniteElement objects
from a ufl.Form, those objects will actually be the dolfin.FunctionSpace objects
because of the inheritance above.
formdata = ufl.FormData(a)
# formdata.elements == [V, ...]
Thus the mesh will actually be implicitly defined through the function
spaces, and can be optional in assemble!
So this should be possible:
mesh = ...
V = FunctionSpace(mesh, "CG", 1)
f = Function(V)
a = f**2*dx
b = assemble(a)
or this way for separately defined functions:
mesh = ...
V = FunctionSpace(mesh, "CG", 1)
f = Function(V)
a = f**2*dx
fc = compiled_function...
b = assemble(a, {f: fc})
However, I'd still like to be able to define forms separately in .ufl files
instead of always having to merge them with the PyDOLFIN application.
In that case we need to instantiate dolfin.FunctionSpace from the elements
in a given form. And we need to reuse those across forms, e.g. a and L.
(But at least then we can get the function names from the .ufl file.)
forms = ufl.load_forms(ufl_filename) # can add returning functions later
a, L = forms
formdata_a = ufl.FormData(a)
formdata_L = ufl.FormData(L)
a_spaces = [dolfin.FunctionSpace(mesh, element) for element in
formdata_a.elements]
L_spaces = [dolfin.FunctionSpace(mesh, element) for element in
formdata_a.elements]
a_form = dolfin.Form(a, a_spaces)
Eh... This is getting complicated.
Maybe we can generate PyDOLFIN code as well ;)
>> > I see that this is a very handy way of defining and assemble a form, but
>> > would it be possible with this design to define a form using only the
>> > FormCompiler language, and then pass this form to jit, or directly to
>> > assemble. Then either add the functions manually to the form a là c++ or
>> > as it is in the assemble function now, i.e., passing them as an optional
>> > kwarg. The latter could be facilitated as Martin suggested previously as
>> >
>> > assemble(..., coefficients = {'f':f,'g':g},...)
>>
>> That's not exactly what I suggested, and it won't work here, since the
>> names of functions are not known! We just agreed to remove "name" from
>> ufl.Function, but if we keep that this would be possible.
>>
>> What I suggested was using the (FormCompiler.) ufl.Function instances as
>> keys:
>>
>> ...
>> fu = ufl.Function(element)
>> a = ...*fu*dx
>> fc = compiled_function
>>
>> assemble(a, ..., coefficients = {fu: fc},...)
>
> Ok!
>
> Johan
>
--
Martin
Follow ups
References