← Back to team overview

dolfin team mailing list archive

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

 

On Monday 24 August 2009 23:57:26 Garth N. Wells wrote:
> Johan Hake wrote:
> > 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.
>
> I've added a constructor to DiscreteFunction, so go ahead and see what
> you can do on the Python side. I'm not entirely comfortable with the C++
> interface, but we may be able to refine it once how things will look on
> the Python side become clearer.

I have now added a first iteration on the python interface of subfunctions, 
see commit message. I think it is quite nice. More explicit than the c++ 
interface ;)

Johan

> Garth
>
> > 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