← Back to team overview

dolfin team mailing list archive

Re: PyDOLFIN Function

 

On Tue, Sep 16, 2008 at 01:10:35PM +0100, Garth N. Wells wrote:
> 
> 
> Anders Logg wrote:
> > On Mon, Sep 15, 2008 at 08:09:43PM +0100, Garth N. Wells wrote:
> >>
> >> Anders Logg wrote:
> >>> On Mon, Sep 15, 2008 at 06:31:56PM +0200, Anders Logg wrote:
> >>>> On Mon, Sep 15, 2008 at 04:42:32PM +0100, Garth N. Wells wrote:
> >>>>> Anders Logg wrote:
> >>>>>> On Mon, Sep 15, 2008 at 04:32:38PM +0100, Garth N. Wells wrote:
> >>>>>>> Anders Logg wrote:
> >>>>>>>> On Mon, Sep 15, 2008 at 03:47:59PM +0100, Garth N. Wells wrote:
> >>>>>>>>> Anders Logg wrote:
> >>>>>>>>>> On Mon, Sep 15, 2008 at 03:12:58PM +0100, Garth N. Wells wrote:
> >>>>>>>>>>> Anders Logg wrote:
> >>>>>>>>>>>> On Mon, Sep 15, 2008 at 03:06:55PM +0200, Johan Hake wrote:
> >>>>>>>>>>>>> On Monday 15 September 2008 14:29:08 Garth N. Wells wrote:
> >>>>>>>>>>>>>> Could a Python expert take a look at site-packges/dolfin/function.py?
> >>>>>>>>>>>>>> The code directly following the comment
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>>   # Special case, Function(element, mesh, x), need to create simple form
> >>>>>>>>>>>>>> to get arguments
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> need to be updated but I don't understand it well.
> >>>>>>>>>>>>> The first special case is for initializing a Function with a given Vector, by 
> >>>>>>>>>>>>> constructing a dofmap from the handed element.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> As constructing a Function from a vector is removed from the cpp interface, 
> >>>>>>>>>>>>> and we have not, (or have we?) figured out how to wrap a shared_ptr in swig, 
> >>>>>>>>>>>>> we should probably just remove the first case for now.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> Johan
> >>>>>>>>>>>> The question is how we want to create discrete Functions in Python.
> >>>>>>>>>>>> Previously, this was done by
> >>>>>>>>>>>>
> >>>>>>>>>>>>   u = Function(element, mesh, Vector())
> >>>>>>>>>>>>
> >>>>>>>>>>>> but now the third argument is not needed anymore. If we remove it,
> >>>>>>>>>>>> we get
> >>>>>>>>>>>>
> >>>>>>>>>>>>   u = Function(element, mesh)
> >>>>>>>>>>>>
> >>>>>>>>>>>> but that doesn't work since that is the way to initialize a
> >>>>>>>>>>>> user-defined function (something overloading eval()).
> >>>>>>>>>>>>
> >>>>>>>>>>>> We could put in a flag and make "discrete" the default. Then all
> >>>>>>>>>>>> user-defined functions need to set the flag to "user".
> >>>>>>>>>>>>
> >>>>>>>>>>>> Suggestions? This is a good time to worry about how we want to design
> >>>>>>>>>>>> the Function interface.
> >>>>>>>>>>>>
> >>>>>>>>>>> Sounds ok to me. This is basically what Vector() was doing, and a flag 
> >>>>>>>>>>> would be more descriptive.
> >>>>>>>>>>>
> >>>>>>>>>>> Garth
> >>>>>>>>>> Maybe we could first try to think seriously about reducing the number
> >>>>>>>>>> of different constructors in Function. There are 14 now! See below.
> >>>>>>>>>>
> >>>>>>>>>> I guess we need the following two basic constructors (empty and copy):
> >>>>>>>>>>
> >>>>>>>>>>   /// Create empty function (read data from file)
> >>>>>>>>>>   Function();
> >>>>>>>>>>
> >>>>>>>>>>   /// Copy constructor
> >>>>>>>>>>   Function(const Function& f);
> >>>>>>>>>>
> >>>>>>>>>> Then we have one for reading from file, which seems ok:
> >>>>>>>>>>
> >>>>>>>>>>   /// Create function from data file
> >>>>>>>>>>   explicit Function(const std::string filename);
> >>>>>>>>>>
> >>>>>>>>>> And then the following set of constructors for constants:
> >>>>>>>>>>
> >>>>>>>>>>   /// Create constant scalar function from given value
> >>>>>>>>>>   Function(Mesh& mesh, real value);
> >>>>>>>>>>
> >>>>>>>>> This one is useful.
> >>>>>>>>>
> >>>>>>>>>>   /// Create constant vector function from given size and value
> >>>>>>>>>>   Function(Mesh& mesh, uint size, real value);
> >>>>>>>>>>
> >>>>>>>>> We could get rid of this one and use the below constructor.
> >>>>>>>>>
> >>>>>>>>>>   /// Create constant vector function from given size and values
> >>>>>>>>>>   Function(Mesh& mesh, const Array<real>& values);
> >>>>>>>>>>
> >>>>>>>>> This one is useful.
> >>>>>>>>>
> >>>>>>>>>>   /// Create constant tensor function from given shape and values
> >>>>>>>>>>   Function(Mesh& mesh, const Array<uint>& shape, const Array<real>& values);
> >>>>>>>>>>
> >>>>>>>>> This is the most generic of the constant functions, so I guess we need it.
> >>>>>>>>>
> >>>>>>>>>> And then there's this constructor which is needed for w.split(u, p):
> >>>>>>>>>>
> >>>>>>>>>>   /// Create discrete function from sub function
> >>>>>>>>>>   explicit Function(SubFunction sub_function);
> >>>>>>>>>>
> >>>>>>>>>> But then there's the following mess of constructors:
> >>>>>>>>>>
> >>>>>>>>> Some of these constructors are necessary to support the PyDOLFIN 
> >>>>>>>>> interface. Can we get around this somehow to avoid duplication?
> >>>>>>>>>
> >>>>>>>>>>   /// Create function from given ufc::function
> >>>>>>>>>>   Function(Mesh& mesh, const ufc::function& function, uint size);
> >>>>>>>>>>     
> >>>>>>>>>>   /// Create discrete function for argument function i of form
> >>>>>>>>>>   Function(Mesh& mesh, Form& form, uint i = 1);
> >>>>>>>>>>     
> >>>>>>>>>>   /// Create discrete function for argument function i of form
> >>>>>>>>>>   Function(Mesh& mesh, DofMap& dof_map, const ufc::form& form, uint i = 1);
> >>>>>>>>>>
> >>>>>>>>>>   /// Create discrete function for argument function i of form (data may be shared)
> >>>>>>>>>>   Function(std::tr1::shared_ptr<Mesh> mesh,
> >>>>>>>>>>            std::tr1::shared_ptr<GenericVector> x,
> >>>>>>>>>>            std::tr1::shared_ptr<DofMap> dof_map, const ufc::form& form, uint i = 1);
> >>>>>>>>>>
> >>>>>>>>>>   /// Create discrete function based on signatures
> >>>>>>>>>>   Function(std::tr1::shared_ptr<Mesh> mesh,
> >>>>>>>>>>            const std::string finite_element_signature,
> >>>>>>>>>>            const std::string dof_map_signature);
> >>>>>>>>>>
> >>>>>>>>>>   /// Create user-defined function (evaluation operator must be overloaded)
> >>>>>>>>>>   explicit Function(Mesh& mesh);
> >>>>>>>>>>
> >>>>>>>>> We need this one.
> >>>>>>>>>
> >>>>>>>>> Garth
> >>>>>>>> If we just consider discrete functions for a while, the question is
> >>>>>>>> how these may be most conveniently (and naturally) defined in C++ and
> >>>>>>>> Python.
> >>>>>>>>
> >>>>>>>> In C++, one only has a dolfin::Form, for example PoissonBilinearForm,
> >>>>>>>> and then it's simple to create a discrete Function by
> >>>>>>>>
> >>>>>>>>   Function u(mesh, form);
> >>>>>>>>
> >>>>>>>> This will extract the element and dof map for the second argument of
> >>>>>>>> the form (the trial function) which is normally what is needed.
> >>>>>>>>
> >>>>>>>> In Python, one does not have a dolfin::Form, but instead one has a
> >>>>>>>> FiniteElement, and then the simplest thing to do is
> >>>>>>>>
> >>>>>>>>   u = Function(element, mesh)
> >>>>>>>>
> >>>>>>>> The element is the first argument for practical reasons (see
> >>>>>>>> function.py) but maybe it shouldn't. I'd like to change this so that
> >>>>>>>> the mesh is always first. All Functions require a Mesh and then it's
> >>>>>>>> natural to put this first.
> >>>>>>>>
> >>>>>>>> So then we would have
> >>>>>>>>
> >>>>>>>>   C++:    Function u(mesh, form);
> >>>>>>>>   Python: u = Function(mesh, element)
> >>>>>>>>
> >>>>>>>> On the other hand, we've been discussing adding a FunctionSpace class,
> >>>>>>>> and then it might be natural to just have
> >>>>>>>>
> >>>>>>>>   C++:    Function u(V);
> >>>>>>>>   Python: u = Function(V)
> >>>>>>>>
> >>>>>>>> This would create a discrete Function. Constant Functions and
> >>>>>>>> user-defined Functions may be created without reference to a
> >>>>>>>> FunctionSpace. This would solve the problem of overloading
> >>>>>>>> constructors. It would be very clear that whenever a FunctionSpace is
> >>>>>>>> involved, it is a discrete Function.
> >>>>>>>>
> >>>>>>> Agree. It's not appropriate to initialise a discrete function with a 
> >>>>>>> form. It seems that using a FunctionSpace will simplify the interface 
> >>>>>>> and provide uniformity across the C++ and Python interfaces, so let's 
> >>>>>>> get FunctionSpace (or something similar with another name) in place and 
> >>>>>>>   then remove some the Function constructors.
> >>>>>> I think FunctionSpace is a good name.
> >>>>>>
> >>>>>>> Function spaces are not only associated with DiscreteFunctions. We 
> >>>>>>> usually interpolate user defined function in the finite element space, 
> >>>>>>> so perhaps there is some scope to unify discrete and user defined 
> >>>>>>> functions?
> >>>>>>>
> >>>>>>> Garth
> >>>>>> Perhaps, but it would require storing an extra vector of values for
> >>>>>> user-defined functions unnecessarily.
> >>>>>>
> >>>>> I'm not suggesting that we store a vector of values - just pointing out 
> >>>>> a function space is also associated with user-defined functions.
> >>>>>
> >>>>> Garth
> >>>> Yes, I forgot for a minute that user-defined functions also need to be
> >>>> associated with a particular function space when used in a form.
> >>>>
> >>>> Then it seems we still have the problem of differentiating between
> >>>> user-defined functions and discrete functions when overloading
> >>>> constructors.
> >>> I have a suggestion that would solve this as well as another problem
> >>> we've discussed a few times before regarding Functions
> >>> (time-stepping). It's rather drastic but it might work.
> >>>
> >>> * Remove GenericFunction, DiscreteFunction, UserFunction etc and just
> >>>   have one single class named Function.
> >>>
> >> Sounds good to me. I often found the current design a bit complicated. A 
> >> simpler design will make parallisation easier.
> >>
> >>> * Always require a FunctionSpace in the inialization of Function:
> >>>
> >>>   u = Function(V)
> >>>
> >>> * Let a Function consist of two things, a function space V and
> >>>   degrees of freedom U:
> >>>
> >>>   u_h = \sum_i U_i \phi_i
> >>>
> >>>   where {\phi_i} are the basis functions for V.
> >>>
> >> As long as it's general enough to permit 'quadrature functions'.
> >>
> >>> * Keep a shared_ptr to V, and a pointer to a Vector U.
> >>>
> >> It might be useful to make the vector a shared pointer (need to check 
> >> what a smart pointer returns if it doesn't point to anything) because it 
> >> would be useful with sub-functions.
> >>
> >>> * The Vector pointer is initialized to 0 by default and as long as it
> >>>   remains 0, the Function behaves like a user-defined Function, that
> >>>   is, the eval() function is used.
> >>>
> >>> * Whenever someone calls the vector() member function, the Vector U is
> >>>   initialized to a Vector of correct size (determined by the DofMap in
> >>>   the FunctionSpace V). From this point on, the Function behaves like
> >>>   a discrete Function, that is, the values in U are used, not eval().
> >>>
> >> Sounds good.
> >>
> >>> * This would make the behavior dynamic. For example, one may set u0 to
> >>>   an initial value in time-stepping and then do u0 = u1 and u0 would
> >>>   change behavior from user-defined to discrete.
> >>>
> >>> * Constant functions are handled by a new class named simply Constant,
> >>>   which is a subclass of Function that overloads eval() and returns
> >>>   the constant value.
> >>>
> >> Perhaps ConstantFunction would be a better name?
> > 
> > Maybe, but it would also be natural to couple this to Constant in UFL
> > in the same way as we couple Function to Function.
> > 
> > Another reason to use Constant is that ConstantFunction implies that
> > there may be other subclasses of Function than Constant and it would
> > be good to avoid building a hierarchy (again). The class Constant just
> > happens to be implemented as a subclass of Function, but it should be
> > thought of as something different.
> > 
> >> No matter what happens, it seems that FunctionSpace is the next step. 
> >> Related to this, is it the intention that a FiniteElement owns a 
> >> ufc::finite_elememt and provides wrapper, like DofMap?
> > 
> > Yes, that was my plan. It only needs to overload the subset of
> > functionality of ufc::finite_element that we need now.
> >
> 
> OK. I can put FiniteElement together pretty quickly if you haven't 
> started already.
> 
> Garth

I haven't (not more than the empty template that's currently there).

-- 
Anders

Attachment: signature.asc
Description: Digital signature


Follow ups

References