← Back to team overview

dolfin team mailing list archive

Re: [HG DOLFIN] Work on new sub Function logic.

 

On Monday 24 August 2009 13:41:24 Garth N. Wells wrote:
> Johan Hake wrote:
> > On Monday 24 August 2009 13:04:45 Garth N. Wells wrote:
> >> Johan Hake wrote:
> >>> On Monday 24 August 2009 10:30:52 Garth N. Wells wrote:
> >>>> Johan Hake wrote:
> >>>>> On Monday 24 August 2009 10:11:49 Garth N. Wells wrote:
> >>>>>>>>>>>> dolfin/swig/dolfin_headers.i description: Work on new sub
> >>>>>>>>>>>> Function logic.
> >>>>>>>>>>>
> >>>>>>>>>>> I am not sure we can completely wrap the new logic to PyDOLFIN.
> >>>>>>>>>>>
> >>>>>>>>>>> To be able to have the double inheritance of cpp.Function and
> >>>>>>>>>>> ufl.Function in PyDOLFIN, new Functions have to be constructed
> >>>>>>>>>>> in the Python interface (function.py).
> >>>>>>>>>>>
> >>>>>>>>>>> The operator[] is mapped to a hidden function _sub. The created
> >>>>>>>>>>> Function that is returned from this is passed to the copy
> >>>>>>>>>>> constructor in the Python version of sub (creating a new
> >>>>>>>>>>> Function object). This is basically just how we did it before
> >>>>>>>>>>> the new design, because previously operator[] returned a
> >>>>>>>>>>> SubFunctionData, which was passed to a Function constructor.
> >>>>>>>>>>> The transition to the new logic works in PyDOLFIN because the
> >>>>>>>>>>> Function copy constructor is used instead of the removed
> >>>>>>>>>>> SubFunctionData constructor.
> >>>>>>>>>>>
> >>>>>>>>>>> This means that the handy operator[], which returns a Function
> >>>>>>>>>>> with a shared vector, cannot fully be used from PyDOLFIN. Would
> >>>>>>>>>>> it be possible to add a shallow copy function in some way.
> >>>>>>>>>>> Would this work with the present SubFunction design?
> >>>>>>>>>>
> >>>>>>>>>> Would something like
> >>>>>>>>>>
> >>>>>>>>>>      Function::sub_function(Function& sub_function, uint i)
> >>>>>>>>>
> >>>>>>>>> Yes I think so. If we could make this a constructor (shallow copy
> >>>>>>>>> constructor) I would be most happy!
> >>>>>>>>
> >>>>>>>> So a constructor
> >>>>>>>>
> >>>>>>>>      Function::Function(uint i)
> >>>>>>>>
> >>>>>>>> would be better?
> >>>>>>>
> >>>>>>> Yes, but then we could not fetch the shared Vector?
> >>>>>>>
> >>>>>>>> I'm reluctant to add a constructor since it breaks the
> >>>>>>>> paradigm that a Function constructor gives a deep copy.
> >>>>>>>
> >>>>>>> Ok.
> >>>>>>>
> >>>>>>>> Could you create
> >>>>>>>> an empty Function internally on the PyDOLFIN side and then pass it
> >>>>>>>> to
> >>>>>>>>
> >>>>>>>>      Function::sub_function(Function& sub_function, uint i)
> >>>>>>>>
> >>>>>>>> to attach the shared data to create the sub-Function
> >>>>>>>> 'sub_function'?
> >>>>>>>
> >>>>>>> Yes, this should be fine. I guess such a function will then just
> >>>>>>> destroy any present vector and exchange it with the one shared with
> >>>>>>> the FullFunction?
> >>>>>>
> >>>>>> Yes. We can throw an error if there is any data already attached to
> >>>>>> the Function.
> >>>>>
> >>>>> When we create a new Function in PyDOLFIN using the DiscreteFunction,
> >>>>> we do create a vector, so this will prevent us using this class. We
> >>>>> use the DiscreteFunction to circumvent some director (SWIG stuff to
> >>>>> be able to inherit a cpp.Function in Python) overhead wrt to call the
> >>>>> eval function during assemble. I guess we will not assemble the
> >>>>> function returned from operator[] so then we can create the Function
> >>>>> using cpp.Function instead.
> >>>>
> >>>> What if we add a constructor to DiscreteFunction to take care of
> >>>> sub-functions? Would that work?
> >>>
> >>> Yes, this should work. Then we could add a constructor taking a
> >>> Function and a number as you suggested above.
> >>
> >> This is trickier than I anticipated. The problem with
> >>
> >>      Function::Function(const Function& v, uint i)
> >>
> >> is that v cannot be const since v keeps track of its sub-functions and
> >> create and stores them on-demand. I could just create a sub-function and
> >> not cache it, but then it would be re-created every time. The problem
> >> with this is that creating a sub-dof map is not trivial if the dof map
> >> has been renumbered.
> >>
> >> I'm also a bit uncomfortable with shallow copies because bad things can
> >> happen when something goes out of scope.
> >
> > If we make _vector protected, we should be able to handle everything in
> > something like:
> >
> >    DiscreteFunction::DiscreteFunction(Function& v, uint i)
> >
> >
> > Here we just let the new DiscreteFunction share both _vector and
> > _function_space. Maybe this was what you did not like?
> >
> >> Could this be taken care of on the Python side by introducing something
> >> like a SubFunction? Function::operator[] returns a reference, and
> >> PyDOLFIN could take are of things through the assignment operators of
> >> the Python Function and SubFunction classes?
> >
> > This is exactly what happens now (if I understand your suggestion
> > correctly :) ) and this is probably why the new SubFunction design just
> > works in PyDOLFIN now. The thing is that we make a deep copy. The sharing
> > of data we get from operator[] is lost. This might not be a big problem.
>
> It would help me understand what to do on the C++ side if I knew what
> the Python would/should look like. In C++ we can do
>
>     // We know that u0 will share the data because of the reference
>     Function& u0_ref = u[0];
>
>     // We know that u0 will be a copy because there is no reference
>     Function u0_copy = u[0];
>
> What's the plan to distinguish the two cases in Python?

This discussion ;)

The above would map to Python as:

  u0_ref = u._sub(0)
  u0_copy = cpp.Function(u._sub(0))

as the copy constructor cannot be implicitly called. The first one will be a 
shared version of the subfunction and the second one will be a deepcopy.

However the returned function is a pure cpp.Function that do not inherits 
ufl.Function. To accomplish this we need to dynamically construct such class, 
and initialize it. The initialization of the cpp.Function has been done using 
the DiscreteFunction constructor that takes a SubFunctionData and now a 
Function.

I am not sure how/if we can do something similar for the reference version. 
The suggestion above, using a separate constructor could work. It will then be 
a different instance of the actual Function, than the one stored in the 
original mixed function, but it will have the same shared vector and function 
space.

Johan


> Garth
>
> > Johan
> >
> >> I don't really understand
> >> how things work on the Python side for Functions, so I'm clutching at
> >> straws.
> >>
> >>
> >>
> >> Garth
> >>
> >>>>>> It would be neat if we could somehow make member functions 'private'
> >>>>>> to PyDOLFIN.
> >>>>>
> >>>>> We can, just rename them in dolfin_function_pre.i
> >>>>>
> >>>>>   %rename (_foo) dolfin::Function::foo;
> >>>>>
> >>>>> We do this for some of the functions (_sub: operator[] and _in for
> >>>>> in) already.
> >>>>
> >>>> I meant C++ member functions which are intended for use through
> >>>> PyDOLFIN only.
> >>>
> >>> I see :)
> >>>
> >>> We could hide some python specific classes, like the DiscreteFunction
> >>> class, by not including it in dolfin_function.h, and then manually add
> >>> it to dolfin_headers.i.
> >>>
> >>> With this we hide it from
> >>>
> >>>   #include <dolfin.h>
> >>>
> >>> We then have to manually add them as #includes in dolfin.i and to
> >>> dolfin_headers.i. We can automate this by adding a
> >>> dolfin_pydolfin_headers.i, which lists the #includes. This file is then
> >>> parsed by generate.py.
> >>>
> >>> If this sounds reasonable I can look into it.
> >>>
> >>> Johan
> >>>
> >>>> Garth


Follow ups

References