← Back to team overview

dolfin team mailing list archive

Re: Reading functions from file

 

2008/12/9 Anders Logg <logg@xxxxxxxxx>:
> On Tue, Dec 09, 2008 at 09:47:03AM +0100, Martin Sandve Alnæs wrote:
>> 2008/12/9 Anders Logg <logg@xxxxxxxxx>:
>> > On Tue, Dec 09, 2008 at 08:44:49AM +0100, Johan Hake wrote:
>> >> On Monday 08 December 2008 22:31:58 Martin Sandve Alnæs wrote:
>> >> > 2008/12/8 Johan Hake <hake@xxxxxxxxx>:
>> >> > > On Monday 08 December 2008 14:53:09 Anders Logg wrote:
>> >> > >> On Mon, Dec 08, 2008 at 02:31:23PM +0100, Johan Hake wrote:
>> >> > >> > On Monday 08 December 2008 13:55:26 Anders Logg wrote:
>> >> > >> > > On Mon, Dec 08, 2008 at 08:01:32AM +0100, Johan Hake wrote:
>> >> > >> > > > On Sunday 07 December 2008 23:33:53 Anders Logg wrote:
>> >> > >> > > > > Something that needs to be added to the new Function interface
>> >> > >> > > > > is reading functions from file. This has worked before:
>> >> > >> > > > >
>> >> > >> > > > >   f = Function("function.xml")
>> >> > >> > > > >
>> >> > >> > > > > Can this be added to the metaclass machinery?
>> >> > >> > > >
>> >> > >> > > > No, but to the __new__ function ;) so I guess yes!
>> >> > >> > > >
>> >> > >> > > > The metalclass produces Function classes. The __new__ function
>> >> > >> > > > instantiate new Functions from what ever argument. This is such a
>> >> > >> > > > case.
>> >> > >> > >
>> >> > >> > > It looks like some work is needed to get this in place.
>> >> > >> > >
>> >> > >> > > The "constructor" currently looks as follows:
>> >> > >> > >
>> >> > >> > >     def __new__(cls, V, **kwargs):
>> >> > >> > >
>> >> > >> > > so to do
>> >> > >> > >
>> >> > >> > >     f = Function("function.xml")
>> >> > >> > >
>> >> > >> > > we need to check if V is a string. Could you add some hooks for
>> >> > >> > > this?
>> >> > >> >
>> >> > >> > I will have a look at it. I do not like V beeing both a FunctionSpace
>> >> > >> > and potentially a filename. I consider having a bunch of kwargs, all
>> >> > >> > defaulting to None. E.g.
>> >> > >> >
>> >> > >> >   def __new__(cls, V=None,cpparg=None,defaults=None,filename=None):
>> >> > >> >
>> >> > >> > Then if you create a Function from file you do:
>> >> > >> >
>> >> > >> >   f = Function(filename="some_function.xml")
>> >> > >> >
>> >> > >> > the cpparg can the repreresent what is sent to compile_function.
>> >> > >>
>> >> > >> Then it would be different from the C++ constructor (which doesn't
>> >> > >> handle named default arguments) and the Mesh constructor in both C++
>> >> > >> and Python:
>> >> > >>
>> >> > >>   mesh = Mesh("mesh.xml")
>> >> > >
>> >> > > The Mesh class has a much clearer C++ interface. It is either
>> >> > > instantiated with a filename-string or from another mesh. The Function
>> >> > > class is a versatile class already in the C++ interface, which defines
>> >> > > different constructors, but evenmore so in the python interface.
>> >> > >
>> >> > > Then I do not think that adding a kwarg for filename is that bad. The
>> >> > > design goal of having as similare interface as possible is good, but then
>> >> > > there are different programming cultures too, to take into acount. I am
>> >> > > not religious about it, but as far as we can I think we should define
>> >> > > kwargs to reflect different instantiation protocol.
>> >> > >
>> >> > > kwargs gives information about how to instantiate a Function. A common
>> >> > > way to figure out how to use a class in python, is to look at the args
>> >> > > and kwargs in class.__init__.
>> >> >
>> >> > Here, let me fix that for you:
>> >> >
>> >> >   A common way to figure out how to use a
>> >> >   poorly designed and/or documented
>> >> >   class in python, is to look at the args and kwargs in class.__init__.
>> >> >
>> >> > Forcing the user to look at the source code rarely reflects good code.
>> >>
>> >> I suppose you do not mean that forcing a user to look at the argument list is
>> >> reflecting bad code design, but rather the other way around. If we have bad
>> >> code design we may force the user to look at the argument list together with
>> >> other documentation?
>> >>
>> >> > But I agree that keyword arguments isn't such a bad thing in python.
>> >>
>> >> Good!
>> >>
>> >> > >> Yes, first create the cpp.Function, then extract
>> >> > >> v.function_space.element().signature() and use that to create the form
>> >> > >> compiler element. Will that work?
>> >> > >
>> >> > > Yes it will be doable. I didn't know that the FiniteElements in the
>> >> > > elementlibrary all defined its signature. To accomplish this we need to
>> >> > > add some construction options for the dolfin.FunctionSpace though, as it
>> >> > > is too restrictive now.
>> >> >
>> >> > Whatever requirements you decide to put on the element signature,
>> >> > it would be nice if they were documented formally (i.e. not as ffc code),
>> >> > and placed in the UFC manual.
>> >> >
>> >> > > Martin has also asked for a way to instantiate a dolfin.FunctionSpace
>> >> > > either with an ufc form or from an ufc_finite_element together with an
>> >> > > ufc_dofmap. We should not forget the pure ufc interface. I think it is
>> >> > > good that Martin keep up that pressure! To reverse engeneer a
>> >> > > dolfin.FunctionSpace from these entities will probably not be doable.
>> >> > >
>> >> > > But for that usercase it should be sufficient to construct a
>> >> > > cpp.FunctionSpace which can be sent to assemble, e.g:
>> >> > >
>> >> > >  V = cpp.FunctionSpace(mesh,ufc_element,ufc_dofmap)
>> >> > >  f = Function(V,...) # We need to add support for sending in just
>> >> > >                      # a cpp.FunctionSpace to a Function, and then return
>> >> > >                      # a cpp.Function
>> >> > >
>> >> > >  A = assemble(compiled_ufc_form,function_spaces=V,coefficients=[f])
>> >> > >
>> >> > > We already have checks for cpp.FunctionSpace and cpp.Function for the
>> >> > > coefficients.
>> >> > >
>> >> > > I do not know how the coefficient kwarg should work though.
>> >> > >
>> >> > >  assemble(...,coefficient ={f_ufl:f_cpp})
>> >> > >
>> >> > > as the form that is sent in to the assemble function is compiled and does
>> >> > > not carry any information about the ufl/ffc coefficient function. Do you
>> >> > > have anything to add here Martin?
>> >> >
>> >> > Looks good. Either a dict or a list should be accepted.
>> >> > UFC does not require a UFL source!
>> >>
>> >> Exactly.
>> >>
>> >> > Maybe we can add a helper function:
>> >> >
>> >> > def function_spaces(form, mesh):
>> >> >     spaces = []
>> >> >     ... loop over form arguments and create FunctionSpace objects with
>> >> > reuse if possible
>> >> >     return spaces
>> >>
>> >> My hands on experience with pure ffc code is nil, so please be more explicit.
>> >>
>> >> > Maybe not exactly what's needed. But preferably we will have tools
>> >> > that enable easy use of UFC code directly, but which are also
>> >> > used behind the scenes regularly by common code such that
>> >> > they are actually in use and being maintained.
>> >> > Otherwise somebody(tm) will just keep making design decisions
>> >> > that breaks UFC compatibility, which sort of weakens the point of
>> >> > a unified interface...
>> >>
>> >> I think I see your point. Instead of hiding the ufc stuff, in ffc.jit, which
>> >> is the case today, we should use it more explicitly in the code, together
>> >> with hooks to the outside world, for people that want to use PyDOLFIN with
>> >> pure ufc.
>> >>
>> >> But isn't this desing desision already broken in C++ DOLFIN?
>> >>
>> >> Johan
>> >
>> > Not necessarily. Before we had an interface to the assembler functions
>> > that allowed sending in ufc objects. This has been removed.
>> >
>> > Instead we now provide a simple way to wrap ufc objects into a Form
>> > and then sending it to assemble():
>> >
>> >  Form dolfin_form(ufc_form, ...);
>> >  assemble(A, dolfin_form);
>> >
>> > (By the way, it looks like we should change the order of arguments in
>> > the Form constructor above so the ufc::form is first.)
>> >
>> > This gives the same flexibility as before at the expense of an extra
>> > line of code but simplifies the assembler interface.
>>
>> Ok! And Form is usable from Python then?
>> I'll take a look at the source code before I say more.
>
> In Python there is another Form class (subclass of cpp.Form) that
> takes care of this. It takes "anything" as input and produces
> something that can be used with the C++ assemble functions (so it does
> JIT compilation when necessary but not if it gets a UFC form).
>
> Take a look at form.py.

Looks good.

What I'm not confident about now is the python FunctionSpace hierarchy.
In which cases will a dolfin-constructed MixedFunctionSpace behave
differently from a cpp.FunctionSpace constructed from a mixed ufc element?

--
Martin


Follow ups

References