On Tue, Sep 09, 2008 at 09:06:00AM +0200, Anders Logg wrote: > On Mon, Sep 08, 2008 at 11:53:09PM +0200, Johan Hake wrote: > > On Monday 08 September 2008 21:45:27 Martin Sandve Alnæs wrote: > > > 2008/9/8 Anders Logg <[EMAIL PROTECTED]>: > > > > On Mon, Sep 08, 2008 at 11:12:14AM +0200, Martin Sandve Alnæs wrote: > > > >> 2008/9/8 Johan Hoffman <[EMAIL PROTECTED]>: > > > >> >> 2008/9/8 Dag Lindbo <[EMAIL PROTECTED]>: > > > >> >>> 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". > > > > It seems to be a set of typemaps that should kick in at the "right places". > > They are defined in > > > > <Lib/python/boost_shared_ptr.i> > > > > and used very rudimentary in > > > > <Examples/test_suite/li_boost_shared_ptr.i> > > > > in the source tree of the 1.3.36 release. It seems that it is not specific > > for > > boost::share_ptr but should also figure out tr1::shared_ptr > > > > I think: > > > > %include <boost_shared_ptr.i> > > > > at the appropriate place should do the trick. > > > > Johan > > I was about to start sketching on a FunctionSpace class when I > realized that this class might look different for different types of > Functions. > > In particular, some functions (DiscreteFunctions) need to have a > dof map and a finite element, while this is not needed for other > functions. > > This means that we might need to duplicate the hierarchy of different > function classes in a number of different function space classes. > > I see two other options: > > 1. Require that a function space consists of > > mesh, finite_element, dof_map > > and pick a suitable (trivial) dof map for example constant functions. > > 2. Implement a number of different function space classes but have a > single Function class which holds data but where each function call > is passed on to the specific type of function call in the > corresponding function space class.
Here are some more thoughts about the shared_ptr issue.
As I understand it, using shared_ptr to store the data in
DiscreteFunction means that users will need to use shared_ptr.
In particular, a Function must be initialized with a shared_ptr to a
Mesh, not a reference to a Mesh. Then in all demos, we would have to
change from
Mesh mesh("mesh.xml");
to
shared_ptr<Mesh> mesh = new Mesh("mesh.xml");
This doesn't look very pretty, and it would make the interface
inconsistent. One would need to know that some classes likes Mesh
should "always" be stored in a shared_ptr (since it will most
certainly be used to initialize a Function), while other classes like
Matrix, File, KrylovSolver, can be created as usual.
There seem to be two options:
1. Don't use shared_ptr anywhere (at least not in public interfaces)
If we choose this option, we will need to decide who owns what. For
example, we could say that a Function always owns the Vector, but
never the Mesh.
This works out also for reading from files:
Mesh mesh;
Function u(mesh);
file >> u;
This would lead to side effects (the mesh will be changed), but that
might be ok.
2. Use shared_ptr everywhere
If we choose this option, we need to redefine Mesh so that it is not a
mesh class, but a shared_ptr for a mesh class (which we rename to
something else). Sundance does something like this and it gives a nice
and pretty syntax:
Mesh mesh = new Mesh(); // no need to delete
Function u = new Function(mesh); // no need to delete
Essentially, it would look like Java. See here:
http://software.sandia.gov/sundance/basics.html
in the section "Handles and Memory Management".
I think we need to pick one of these options and not a mix since it
would make the interface inconsistent.
Opinions? 1 or 2?
--
Anders
signature.asc
Description: Digital signature
_______________________________________________ DOLFIN-dev mailing list [email protected] http://www.fenics.org/mailman/listinfo/dolfin-dev
