dolfin team mailing list archive
-
dolfin team
-
Mailing list archive
-
Message #09818
Re: PyDOLFIN Function
On Tuesday 23 September 2008 17:05:38 Anders Logg wrote:
> On Tue, Sep 23, 2008 at 11:03:16AM -0400, Shawn Walker wrote:
> > On Tue, 23 Sep 2008, Anders Logg wrote:
> >> 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()
> >>
And if you would like to set values in the vector you use:
x.set(your_array)
where your_array is a numpy array.
Johan
References