← Back to team overview

dolfin team mailing list archive

Re: Function and DofMap

 

2008/9/8 Anders Logg <logg@xxxxxxxxx>:
> On Mon, Sep 08, 2008 at 11:12:14AM +0200, Martin Sandve Alnæs wrote:
>> 2008/9/8 Johan Hoffman <jhoffman@xxxxxxxxxx>:
>> >> 2008/9/8 Dag Lindbo <dag@xxxxxxxxxx>:
>> >>> Anders Logg wrote:
>> >>>> There seems to be a problem (among many) with the current design of
>> >>>> the Function classes (see thread "evaluating higher order mesh
>> >>>> function").
>> >>>>
>> >>>> In particular, the finite element is missing in DiscreteFunction. My
>> >>>> suggestion would be to just add it and let a DiscreteFunction consist
>> >>>> of the following four items which are always available:
>> >>>>
>> >>>>   mesh, x, dof_map, finite_element
>> >>>>
>> >>>> Is this enough, and what other issues to we need to fix?
>> >>>>
>> >>>
>> >>> I'm not sure I agree that the dof map and finite element should be owned
>> >>> by the discrete function. There was a great suggestion from Martin, in a
>> >>> thread "Abstraction idea" from 06/05/2008, to create a class
>> >>> FunctionSpace where the mesh, element and dof_map(s) are aggregated.
>> >>> Citing Martin:
>> >>> U = FunctionSpace(mesh, dofmapset, form, 0) # or something similar
>> >>> u = Function(U)
>> >>> v = Function(U)
>> >>>
>> >>> This seems a solid approach to me since it would provide a way of
>> >>> encapsulating the mathematical formulation of the problem, which is more
>> >>> or less const and likely to be reused by many discrete functions in a
>> >>> solver.
>> >>>
>> >>> It seems to me that there is an obvious risk that a lot of redundant
>> >>> initialization would occur if all discrete functions should own their
>> >>> own elements and dof maps. There seems to be consensus that the mesh
>> >>> should be "global" for efficiency reasons, so why not treat the function
>> >>> space the same way?
>> >>>
>> >>> Is there a problem with an approach where the funciton _always_ owns the
>> >>> vector and _never_ owns the function space (and mesh)? A very strict
>> >>> design would avoid shared/smart pointers, provide a comprehensible user
>> >>> interface and probably help the parallellization effort.
>> >>>
>> >>> /Dag
>> >>
>> >> If the Function always owns the vector, there are cases you'll have to
>> >> make unneccessary copies of a vector, in particular such scenarios
>> >> may occur when trying to combine dolfin with something else.
>> >>
>> >> If the Function never owns the function space, it must always be
>> >> constructed explicitly by the user. This may not be a bad thing.
>> >> However, if the Function is loaded from a file, nobody owns the
>> >> FunctionSpace.
>> >>
>> >
>> > Conceptually, I agree with Dag (and Martin?) that it is natural to have
>> > global function spaces. And if the explicit construction of such spaces
>> > can be made simple, it may not be a bad thing but a natural part in
>> > setting up the mathematical problem. And I do not really like that
>> > functions should be initialized from a form, which defines an equation.
>> >
>> > I think one idea was to not force less mathematically oriented users to
>> > worry about function spaces. I guess there are (at least) 2 types of
>> > functions: (i) functions part of the form, and (ii) functions not part of
>> > the form, but used in pre/postprocessing etc.
>> >
>> > For (i) it may be natural to construct the function space from the form,
>> > and for (ii) it may be convenient in some cases, but it is not really
>> > obvious that this is the best solution.
>> >
>> > Maybe an explicit construction of a function space can come with a
>> > default, such as a nodal basis of piecewise linears?
>> >
>> > /Johan
>>
>> So:
>>   FunctionSpace V(mesh);
>>   Function f(V);
>> gives a function f on piecewise linears?
>> That's ok with me.
>>
>>
>> About ownership, I think the only both robust and intuitive solution
>> is that an object never should store a reference (or regular pointer)
>> to another object. But as long as we are aware of the cost of doing
>> this and state it clearly in the documentation, I'm ok with keeping
>> e.g. the Mesh references like we do.
>>
>> Any time object A stores a reference to object B, the user must
>> take care that the lifetime of B exceeds the lifetime of A. There
>> are no exceptions to this. This puts some real limitations on the
>> way the user must structure his program, e.g. he must sometimes
>> (often?) keep objects around longer than they're explicitly needed.
>>
>> This may be a good thing, since it forces the user to think about
>> dependencies and object lifetimes, and the objects in question
>> use some memory.
>
> I think this is ok. There are many ways to create a segfault in C++.
> If you program in C++, you will have to think about memory.
>
>> But if we use references instead of shared_ptr,
>> we should never have default values:
>> - A Function has a reference to a Mesh, which is ok since
>>   it's always created outside.
>> - If a DiscreteFunction is to have a reference to a Vector, or a
>>   Function is to have a reference to a FunctionSpace, it cannot
>>   create its own without adding memory management code.
>>
>> Every place we accept these limitations and requirements of how
>> the user structures his programs, we can use references and be
>> done with it. But don't think that the pretty syntax means the user
>> doesn't have to think about memory management, since all the
>> responsibility for memory management (object destruction order)
>> is in fact placed on the user, and errors from the users side will
>> lead to invalid references we cannot detect and segfaults.
>
> I want the syntax to be simple and pretty, but I don't necessarily
> want to hide the user from problems that are part of the design of
> C++. It isn't Python or Java. You should be expected to know what
> you are doing. :-)

It's not only about knowing what you're doing. It forces very
hard restrictions on the design/flow of your program, which can

1) Be a major source of bugs in nontrivial apps (see Garths email), which
are not locally visible because they depend on the global program flow.

2) Make it impossible to initialize e.g. Function in e.g a file reader,
since the caller of the file reader would need to get the objects
Function depends on. This is not limited to file readers, but is
a recurring pattern in nontrivial apps.

If we want to use dolfin or want dolfin to be used in apps that
are more complicated than the traditional "read input, compute
something, output something" app, these restrictions become
a larger problem.

> Anyway, I like the idea about having a FunctionSpace class which
> several Functions may share. The problem we need to solve is
> reading from file:
>
>  FunctionSpace V(mesh);
>  Function u(V);
>  file >> u;
>
> The last line just fills out the data in both u and V.
>
> This will lead to side effects as V might be changed when doing
>
>  FunctionSpace V(mesh);
>  Function u(V);
>  Function v(V);
>  file >> u;
>
> V will be changed, both for u and v. In fact, the mesh will also be
> changed.
>
> The best thing would be if we could do
>
>  file >> (mesh, V, u);

This is _exactly_ the kind of issue that smart pointers solve.

Btw, I tried to search the swig documentation for shared_ptr, and
found nothing...
I don't know what exactly they mean by "shared_ptr support".

--
Martin


Follow ups

References