← Back to team overview

dolfin team mailing list archive

Re: PyDOLFIN Function

 

On Tue, Sep 23, 2008 at 12:35:48PM +0200, Marie Rognes wrote:
> Anders Logg wrote:
> > On Wed, Sep 17, 2008 at 01:23:17PM +0200, Evan Lezar wrote:
> >   
> >> On Tue, Sep 16, 2008 at 2:18 PM, Anders Logg <logg@xxxxxxxxx> wrote:
> >>
> >>     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).
> >>
> >>
> >>
> >> Hi
> >>
> >> What is the correct way to initialize a  discrete function in python.  I see
> >> that the way I was using in the electromagnetic demo no longer works.  f =
> >> Function(element, mesh, vector)
> >>
> >> Evan
> >>     
> >
> > That is the correct way, but the vector will be ignored since the
> > Function from now on will create and own its vector of degrees of
> > freedom.
> >
> > So you can do something like
> >
> >   f = Function(element, mesh, Vector())
> >   x = f.vector()
> >
> > The vector you send in will be ignored and then you need to pick it
> > out afterwards.
> >
> > Once the new design is in place it will be
> >
> >   f = Function(V) # V is a FunctionSpace
> >   x = f.vector()
> >
> >   
> 
> 
> Follow-up question: So, what is the right way to initialize a Function 
> with a vector of computed values?

You can't. You have to create the function first, then set the values
in the vector:

  f = Function(element, mesh, Vector())
  x = f.vector()

  solve(A, x, b)

-- 
Anders

Attachment: signature.asc
Description: Digital signature


Follow ups

References