dolfin team mailing list archive
-
dolfin team
-
Mailing list archive
-
Message #10532
Re: Patch with fixes to PyDOLFIN
On Monday 03 November 2008 10:36:52 Martin Sandve Alnæs wrote:
> 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.
I think I mixed ufc::Form and dolfin::Form. I thought jit returned a compiled
and instantiated dolfin::Form. But I suppose we then need some form of jit
compiling of a dolfin::Dofmap and dolfin::FiniteElement, based on the
ufc::dofmap, and ufc::FiniteElement that resides in the module returned from
jit today.
> If we want to construct dolfin.Form objects in PyDOLFIN, we need to do
> something more.
Ok, I think I am a bit lost with regard to [dolfin,ufc]::Form. The
dolfin::Assembler takes a dolfin::Form. Where is this "instantiated" today?
> > 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.
Ok, you go for inheritance then ;)?
> 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.
Agree.
> 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.
Exactly.
> (But at least then we can get the function names from the .ufl file.)
How is this different from the earlier scenario?
> 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 ;)
If we let jit compile the form with -l dolfin we can extract the
FunctionSpaces from the module. These will only need a mesh to be
instantiated.
jit can then be called with either a mesh, which implies that new
FunctionSpaces are instantiated and used to instantiate the dolfin::form, or
it can be called with an allready instantiated FunctionSpace(s), which are
used to instantiate the dolfin::Form.
With this we have reuse of FunctionSpaces, but with the cost that we are using
dolfin::Form instead of ufc::Form.
> >> > 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
Johan
Follow ups
References