craffael / lehrfempp

Simple Finite Element Framework for research and education
MIT License
28 stars 17 forks source link

MeshBuilder #13

Closed hiptmair closed 6 years ago

hiptmair commented 6 years ago

Imagine the following use case, which is of practical relevance: The cells are affine, but the edge carry a parameterization that provides an exact description of the boundary. In this case just providing vertex coordinates and cell geometries is not enough.

Conversely, vertex positions can be deduced from cell geometries; hence, this piece of information is really redundant.

craffael commented 6 years ago
  1. This is something I haven't considered yet. So far I have always assumed that geometrically speaking, a codim=n entity is always bounded by its codim=n+1 entities. But I know, this doesn't have to be the case, I've used such an approach myself in my master thesis.

  2. Yes you are of course correct, this could be deduced from the element geometries.

A possible solution to both issues would be to replace the AddPoint() and AddElement() methods with the following method:

/**
 * @brief Insert a new entity explicitly into the mesh
 * @param ref_el The reference element of the entity
 * @param node_indices The zero based node-indices that make up this entity. 
                       If ref_el == RefEl::kPoint, this range should be empty.
 * @param geom  The geometric description of this entity. 
                If it is a nullptr, we try to deduce the geometric shape from entities with lower codimensions that have this entity as a sub-entity.
                If this is not, the entity will not be equipped with a geometry and calling Entity::Geometry() on it will return an exception.
 * @returns The unique, zero-based consecutive index of the entity (after the mesh has been constructed).
 */
size_type AddEntity(RefEl ref_el, ForwardRange<size_type> node_indices, std::unique_ptr<Geometry> geom)` 
hiptmair commented 6 years ago

I think, mesh generation facilities should not be included in the mesh class hierarchy and should be solely provided by MeshBuilders.

Therefore remove the constructor from hybrid2d::Mesh and include it into a MeshBuilder.

This comes at the price of allowing MeshBuilders unrestricted access to the data members of Mesh.

craffael commented 6 years ago

I agree with you that the constructor of the Mesh class should be private. We want the users to build meshes using the MeshBuilder and not the constructor of the hybrid2d mesh class. This means that the hybrid2d MeshBuilder must be a friend of the Mesh class.

From a users point of view I think it doesn't matter if the functionality that is currently stored in the constructor of the Mesh class is moved to the MeshBuilder. From the users perspective, its the package MeshBuilder + Mesh that work together in an expected way. The user shouldn't have to care about how the mesh internals work...

craffael commented 6 years ago

I just realized that we have different ideas about what a MeshBuilder is: For me the term MeshBuilder describes two concepts and I guess we should change the naming if we want to continue using them

  1. Concept A: A Mesh Builder is an abstract interface that describes how a hybrid mesh can be constructed. This is currently the class lf::mesh::MeshBuilder. Then there are implementations of this interface such as lf::mesh::hybrid2d::MeshBuilder.

  2. Concept B: A Mesh builder is any class that constructs a mesh from a set of parameters and uses the builder pattern. Currently class lf::mesh::TPTriagMeshBuilder would fulfill this concept. I would argue that these classes should not depend on the concrete implementation of the Mesh Manager. I.e. they should take as an input a lf::mesh::MeshBuilder and use it to construct the particular mesh. In this way they would also work with other mesh managers and if internals of the Mesh Manager are changed it is not necessary to change them too.

Maybe we should rename MeshBuilders of type A to MeshFactory...?

hiptmair commented 6 years ago

An idea for a very simple implementation of the generalized constructor for hybrid2d::Mesh:

This initializes the Entity vectors in strict sequential order so that pointers to entities will always be valid. Moreover, it is a very straightforward implementation that dumps much of the algorithmic complexity onto std::map.

craffael commented 6 years ago

I agree this would be an option. However I'm usually not a big fan of using std::map for a large number of elements unless there is no other option. Although using std::map instead of std::sort gives similar (theoretical) algorithmic complexity, I prefer the latter for the following (performance) reasons:

So if we want to write an algorithm that enables us to use entity pointers instead of "pointer to mesh" + index, I suggest to use a "two-stage sort"-like algorithm.

hiptmair commented 6 years ago

Of using std::map might sacrifice efficiency, but this is irrelevant, because the construction of meshes will hardly ever be a performance bottleneck unless incessant adaptive refinement and coarsening is employed in the context of timestepping. There are no plans to do this.