I can't say whether that's the same problem that is commonly reported in the
literature, but this sort of mesh entanglement is the motivation for people to
look at Arbitrary Lagrangian Eulerian (ALE) methods, instead of just Eulerian
methods.
Can you show a picture of your mesh? Is it that just the mesh is bad, or the
whole geometry?
Best
W.
GridGenerator::hyper_sphere(triangulation,Point<spacedim>(0,0,0),1.0);
triangulation.set_all_manifold_ids(0);
triangulation.refine_global(initial_global_refinement);
/* interpolate the map from triangulation to desired geometry for the first time
a,b,c are the axes of an ellipsoid */
VectorTools::interpolate(bending_dof_handler,
Initial_map_sphere_to_ellipsoid<spacedim>(a,b,c),
global_euler_vector);
const MappingQEulerian<dim,Vector<double>,spacedim> mapping (2, dof_handler, euler_vector);
double Initial_map_sphere_to_ellipsoid<spacedim>::value(const Point<spacedim> &p, const unsigned int component) const
{
double norm_p = p.distance(Point<spacedim>(0,0,0));
// return the difference between the point on the ellipse and the point on the original geometry
return abc_coeffs[component]*p(component)/norm_p - p(component);
}
In the case that the mesh lives in a higher dimensional space, you also
have to enforce -- either as part of the problem formulation, or as a
postprocess step for the output of Mesquite -- that the new nodes still
need to lie on the geometry as described before. In other words, nodal
points can move *within* the surface, but not perpendicular to it.
I have no idea whether that is possible within Mesquite, but in the
worst case you can always project back to the previous surface.
With Mesquite I believe one would essentially redefine a triangulation in whatever format it wants (I think you provide nodal coordinate and connectivities), tell it which nodes remain fixed (i.e. provide some constraints) and then simply specify some algorithm with which it must update the nodal coordinate positions. So you would effectively give it a set of mapped coordinates of the triangulation and it returns a new set for you. The difference between these new mapped points and the reference grid nodal coordinates is your euler vector.Ignoring here are only three real complexities with this approach:
1. ...
2. Translating this Euler vector into something thats relevant to the FESystem for your problem (this is what you're struggling to conceptualise, but its really not difficult at all).
3. ...
I think that one must disconnect the problem that you're solving and solve the mesh update problem as a completely separate problem; this ties in with what Wolfgang has also said below. This is advantageous because one can then reuse this algorithm for various multiphysics problems (e.g. fluid-structure interaction), or just to increase the mesh quality before starting a simulation!
I would create a auxiliary system of dim x linear FE_Q's, which have support points that coincide with the vertices. Then the issue of relating DoFs and vertex locations is trivial because of these three functions, which are valid when using Q1 elements. Once one's solved the mesh update problem, one then populates the Euler solution (displacement) vector corresponding to this auxiliary DoFHandler. Then you can just do an interpolation / projection of this vector onto your current FE space, or update the triangulation vertices if desired. This also eliminates any issues that arise if, say, your primary problem solves some displacement with quadratic or discontinuous elements. or if the displacement is only one component of the global solution.My thinking is that one could add the following functionality to the library:1. A function that takes in a triangulation and a (possibly empty) Euler vector defining the initial mapped coordinates of the triangulation vertices, and some boolean constraints vector. This would then return an optimal Q1 Euler vector as computed by Mesquite.3. A function that would interpolate the optimal Q1 Euler vector onto a given FE space, which would presumably represent the displacement solution to some other problem.Does this make sense?
Hi Tom,An alternative to implemented a complex mesh update strategy is to define an initial mesh that, when mapped via your Euler vector, produces a higher quality mesh. For example, you could remove the "top" element of the coarse mesh and substitute it with a set of elements with an o-grid topology. Of course you would have to have some rough idea as to what your final grid topology will look like in order to optimise this.
By the way, in the images you've shown can you indicate where the low quality elements are? In Paraview there's a filter that can help with this. If one considers the mesh on this ellipse as something mapped from a cubic grid, then I presume you're referring to the elements at the cube corners.
Yes, this is what I am struggling to understand, you have put it very simply.
I can see how this is trivial for linear FE_Q's, and these three links should make this concrete.
Yes, this makes sense. To be clear, the functionality in step 2. A function that actually moves the triangulation vertices for you based on this vector. is not strictly necessary in the present context, is that right?
My thinking is that one could add the following functionality to the library:1. A function that takes in a triangulation and a (possibly empty) Euler vector defining the initial mapped coordinates of the triangulation vertices, and some boolean constraints vector. This would then return an optimal Q1 Euler vector as computed by Mesquite.
3. A function that would interpolate the optimal Q1 Euler vector onto a given FE space, which would presumably represent the displacement solution to some other problem.
1 /* update euler_vector to describe smoothed mesh */2 FESystem<dim,spacedim> vertex_fe(FE_Q<dim,spacedim>(1), spacedim);3 DoFHandler<dim,spacedim> vertex_dof_handler(temp_triangulation);4 vertex_dof_handler.distribute_dofs(vertex_fe);5 Vector<double> vertex_euler_vector(vertex_dof_handler.n_dofs());67 std::vector<Point<spacedim> > original_vertices = triangulation.get_vertices();8 std::vector<bool> vertex_touched(temp_triangulation.n_vertices(),false);910 typename DoFHandler<dim,spacedim>::active_cell_iterator11 cell=vertex_dof_handler.begin_active(),12 endc=vertex_dof_handler.end();13 for (; cell!=endc; ++cell)14 for (unsigned int v=0; v < 4; ++v)15 if (!vertex_touched[cell->vertex_index(v)])16 {17 for (unsigned int k=0; k<spacedim; ++k)18 {19 vertex_euler_vector(cell->vertex_dof_index(v,k))20 = cell->vertex(v)(k) - original_vertices[cell->vertex_index(v)](k);21 }22 vertex_touched[cell->vertex_index(v)] = true;23 }
I have taken some good advice from Oded (it turns out we work one building over from each other!),
I'm trying hard to maintain the MappingQEulerian approach in my current model setup, and I wanted to return to the outline you put forward to see if 'Mesquite' can simply be replaced with 'Cubit' in your writeup:
(exporting from cubit is actually a bit of a problem, I have submitted a service email to them - but if I do this through the gui it all works)
3. A function that would interpolate the optimal Q1 Euler vector onto a given FE space, which would presumably represent the displacement solution to some other problem.Here's where my question for you comes in: At this stage I have a new Triangulation (call it temp_triangulation) which holds my optimal vertices. The vertices in temp_triangulation live on some surface in real space. I have my original Triangulation (call it orig_triangulation) which satisfies orig_triangulation.n_vertices() == temp_triangulation.n_vertices(). How to I obtain a new euler_vector of size dof_handler.n_dofs() at this stage?I have done the following so far:1 /* update euler_vector to describe smoothed mesh */2 FESystem<dim,spacedim> vertex_fe(FE_Q<dim,spacedim>(1), spacedim);3 DoFHandler<dim,spacedim> vertex_dof_handler(temp_triangulation);4 vertex_dof_handler.distribute_dofs(vertex_fe);5 Vector<double> vertex_euler_vector(vertex_dof_handler.n_dofs());67 std::vector<Point<spacedim> > original_vertices = triangulation.get_vertices();8 std::vector<bool> vertex_touched(temp_triangulation.n_vertices(),false);910 typename DoFHandler<dim,spacedim>::active_cell_iterator11 cell=vertex_dof_handler.begin_active(),12 endc=vertex_dof_handler.end();13 for (; cell!=endc; ++cell)14 for (unsigned int v=0; v < 4; ++v)
15 if (!vertex_touched[cell->vertex_index(v)])16 {17 for (unsigned int k=0; k<spacedim; ++k)18 {19 vertex_euler_vector(cell->vertex_dof_index(v,k))20 = cell->vertex(v)(k) - original_vertices[cell->vertex_index(v)](k);21 }22 vertex_touched[cell->vertex_index(v)] = true;23 }
Basically, I have built vertex_euler_vector to map the vertices of my original reference domain to new points which are more or less on my original surface. This holds essentially Q1 information - only information at the nodes of the original reference domain. I think that what I need to do now is interpolate vertex_euler_vector onto my original finite element space (which was Q2, but could be anything). If that is what is needed, I would appreciate some advice on how to accomplish it!
Does any of this sound reasonable?
Its great that you're getting somewhere with this!
(exporting from cubit is actually a bit of a problem, I have submitted a service email to them - but if I do this through the gui it all works)
Ok, so this might be a point of difficulty. See the answer to your next question...
3. A function that would interpolate the optimal Q1 Euler vector onto a given FE space, which would presumably represent the displacement solution to some other problem.Here's where my question for you comes in: At this stage I have a new Triangulation (call it temp_triangulation) which holds my optimal vertices. The vertices in temp_triangulation live on some surface in real space. I have my original Triangulation (call it orig_triangulation) which satisfies orig_triangulation.n_vertices() == temp_triangulation.n_vertices(). How to I obtain a new euler_vector of size dof_handler.n_dofs() at this stage?
I have done the following so far:1 /* update euler_vector to describe smoothed mesh */2 FESystem<dim,spacedim> vertex_fe(FE_Q<dim,spacedim>(1), spacedim);3 DoFHandler<dim,spacedim> vertex_dof_handler(temp_triangulation);4 vertex_dof_handler.distribute_dofs(vertex_fe);5 Vector<double> vertex_euler_vector(vertex_dof_handler.n_dofs());67 std::vector<Point<spacedim> > original_vertices = triangulation.get_vertices();8 std::vector<bool> vertex_touched(temp_triangulation.n_vertices(),false);910 typename DoFHandler<dim,spacedim>::active_cell_iterator11 cell=vertex_dof_handler.begin_active(),12 endc=vertex_dof_handler.end();13 for (; cell!=endc; ++cell)14 for (unsigned int v=0; v < 4; ++v)
By "4", you mean GeometryInfo<spacedim>::vertices_per_cell, right?
15 if (!vertex_touched[cell->vertex_index(v)])16 {17 for (unsigned int k=0; k<spacedim; ++k)18 {19 vertex_euler_vector(cell->vertex_dof_index(v,k))20 = cell->vertex(v)(k) - original_vertices[cell->vertex_index(v)](k);21 }22 vertex_touched[cell->vertex_index(v)] = true;23
At first glance this looks ok. My biggest concern is that you've assumed that your old and new triangulations have the same vertex ordering. I'm inclined to think that this is ok because I don't think that Cubit renumbers mesh entities unless you ask it to, and nor does deal.II. But you should probably hand-check this on a small geometry.
Basically, I have built vertex_euler_vector to map the vertices of my original reference domain to new points which are more or less on my original surface. This holds essentially Q1 information - only information at the nodes of the original reference domain. I think that what I need to do now is interpolate vertex_euler_vector onto my original finite element space (which was Q2, but could be anything). If that is what is needed, I would appreciate some advice on how to accomplish it!
I think that VectorTools::interpolate_to_different_mesh could do the job for you; even though it needs the same FE discretisation I think this means that both DoFHandlers (both based on the original triangulation) must be structured like FESystem<dim>(FE_Q(n),dim). If I'm wrong on this then you could wrap your Euler vector in an FEFieldFunction and just use apply of the VectorTools::interpolate functions - this may be slow if the triangulation is huge, but it would be a good first step to take.
Does any of this sound reasonable?
Well, none of it sounds crazy :-)
J-P, you (and the entire dealii community) are a paragon of encouragement, and I appreciate that very much.
here's what I came up with yesterday
This works on a small geometry, thankfully. Even after mesh modifications from within cubit. This would not work for remeshing from within cubit, unfortunately. The complete remeshing case is interesting to me, but that's not what I'm aimed at right now.
get cubit to export codimension 1 surfaces using its export commands. The output is always flattened in the sense that the z-coordinates of vertices are stripped. Perhaps your code snippet will help me by setting the dimension to 3. For now this works nicely, and cubit is slowly responding to my email.As well, I choose ucd because GridIn::read_abaqus() doesn't like to find that z-vertex from standard abaqus .inp files for codimension 1 surfaces, but read_ucd() works as expected.)
I will try VectorTools::interpolate_to_different_mesh when I get to my desk this morning. I tried the FEFieldFunction yesterday, but
FEFieldFunction<spacedim, DoFHandler<dim,spacedim> > FEFieldFunction(vector_dof_handler, euler_vector)
is very unhappy - it is not implemented for DoFHandlerType = DoFHandler<dim,spacedim> (and Mapping<dim,spacedim>), rather it is implemented for dim=spacedim problems only.