← Back to team overview

dolfin team mailing list archive

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

 

On Tuesday 25 August 2009 11:56:15 Garth N. Wells wrote:
> Johan Hake wrote:
> > 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 ;)
>
> To the untrained eye ;).

True, but the distinction in c++ is not very self documented.

> Could you add examples of its use to some of the existing Python demos?
> Good examples would be shallow copies for plotting and a deep copy to
> compute norms of the vector.

Done

Johan

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


References