external mesh deformation base class#9
external mesh deformation base class#9bangerth merged 10 commits intolandlab-aspect:landlab-aspectfrom
Conversation
d7f4b32 to
5f7cb5f
Compare
| - why put when to call set_evaluation_points() with invalidation logic into user code? turn around? | ||
| - user function should be called update() or do_surface_step(dt) or something | ||
| - nodes: derived class should define dim-1 surface points, we translate to 3d points | ||
| - initial topography ASPECT->external or the other way around? compute_initial_deformation_on_boundary() <- std::vector<> initial_topography() |
There was a problem hiding this comment.
we need compute_initial_deformation_on_boundary(). we should get the initial deformation from landlab
There was a problem hiding this comment.
need to refactor code in base class where we build a vector of initial deformations, so that we query by node index, not by node point.
5134922 to
d1a58cc
Compare
|
@bangerth Thank you. This is now ready for a first review. We will need to switch to 9.6 for the tests to pass, see geodynamics#6638 Additionally, I have not done the refactor for the initial conditions so far, but I think this could be merged without. |
|
@bangerth ping |
5c5ba97 to
79afe3e
Compare
79afe3e to
2023709
Compare
|
@bangerth Ping. The tests now pass. |
bangerth
left a comment
There was a problem hiding this comment.
Here's the PointDataOut class. I'm going to have to go teach, but will be back at this for the rest when back.
| // We have one more data components than dataset_names (the particle id) | ||
| patches[i].data.reinit(n_data_components, 1); | ||
|
|
||
| patches[i].data(0, 0) = i; // id |
There was a problem hiding this comment.
Do we eventually want to make this so that in parallel, we don't re-use ids?
There was a problem hiding this comment.
That would make sense, but the class has currently no knowledge about the MPI communicator to use.
There was a problem hiding this comment.
Yes, perhaps an issue for the future.
bangerth
left a comment
There was a problem hiding this comment.
Here's the rest. I apologize for taking so long to actually get to this! :-(
| const LinearAlgebra::Vector v_interpolated | ||
| = interpolate_velocities_to_surface_points(external_surface_velocities); | ||
|
|
||
| // TODO: need ghost values of v_interpolated? |
There was a problem hiding this comment.
Yes. We use this vector to evaluate at all boundary DoFs of the current process because this is what DoFTools::extract_boundary_dofs does:
/**
* Extract all degrees of freedom which are at the boundary and belong to
* specified components of the solution. The function returns its results in
* the form of an IndexSet that contains those entries that correspond to
* these selected degrees of freedom, i.e., which are at the boundary and
* belong to one of the selected components.
*
* By specifying the @p boundary_ids variable, you can select which boundary
* indicators the faces have to have on which the degrees of freedom are
* located that shall be extracted. If it is an empty list (the default), then
* all boundary indicators are accepted.
*
* This function is used in step-11 and step-15, for example.
*
* @note If the DoFHandler object is defined on a
* parallel Triangulation object, then the computed index set
* will contain only those degrees of freedom on the boundary that belong to
* the locally relevant set (see **********************************
* @ref GlossLocallyRelevantDof "locally relevant DoFs"), i.e., the function
* only considers faces of locally owned and ghost cells, but not of
* artificial cells.
* [...]
*/
template <int dim, int spacedim>
IndexSet
extract_boundary_dofs(const DoFHandler<dim, spacedim> &dof_handler,
const ComponentMask &component_mask = {},
const std::set<types::boundary_id> &boundary_ids = {});
| // constraint. We later make that consistent across processors to | ||
| // ensure we also know about the locally relevant DoFs' | ||
| // constraints: |
There was a problem hiding this comment.
You may want to adjust this part of the comment. We are already consistent on the locally relevant DoFs. The call to make_consistent() is probably not wrong, but I think also not necessary.
| } | ||
| } | ||
|
|
||
| // TODO: make consistent? |
|
Thank you @bangerth. I replied to some of your comments and will address everything else in the next few days. |
ef3ba5f to
bcdab67
Compare
|
Tell me if you want me to take another look! |
bcdab67 to
a7f7720
Compare
|
I am stuck with one problem prompted by your remarks:
I don't think this is true: On each rank we receive data in all evaluation points that are contained within any of our locally owned cells. We then proceed to set each surface DoF of the ASPECT mesh to the value of the closest point and store this in This means:
I don't see an easy way to do this correctly, because it would involve communicating values and distance to evaluation point for each DoF on a processor interface and then picking the best value (probably by the DoF owner followed by a broadcast to the others). Sigh. |
|
That said, I would also be okay to merge what we have here and incrementally improve. The tests are passing... |
Oh yes, I get it now. You're computing this also going for ghost nodes, which I thought was enough to be consistent. But you may be computing the wrong thing. So yes, you need to call the
Hm, good point. And if two processes write constraints into locally active DoFs that are different, then At first I was thinking that you need to make sure that you only ever create constraints for locally owned DoFs. That avoids the conflicts, but you're right that that doesn't mean that these are correct. In particular, you'd end up with a situation where you'd get different results depending on the number of processes. Do you need to split the loop you have into two? Perhaps something like this (I think this is what you are proposing above):
|
Let's see if we can come up with a solution over the next couple of days. If not, we'll just go with what you have for now... |
Yes, this is what I was hinting at. But, as you said, it would involve a custom communication step.
You make an important point. I think this situation is not only theoretical but very likely to happen considering one use case are structured grids that do not line up. I think the only reasonable option is to (somehow) precompute a globally injective mapping from evaluation point index to DoF. The owner of the DoF can play the role of the tie breaker for points with equal distance. With this mapping, each processors loops over their list and writes their values into a global vector. After a call to I would construct the list in the following way:
With this map in place it is as simple as iterating over all evaluation points, looking them up in the map, and setting the values. This sounds like a custom MPI routine where I can not use any linear algebra or other high-level deal.II functionality. Luckily, this is all point to point communication. Any suggestions for simplification? |
|
ping @bangerth |
|
Yes, tough problem. You're right that that sounds like a function best suited to I think that encapsulates the interface you need. The steps you mention seem reasonable to me. Perhaps you need to pass more information in than just the locally owned node points (the locally relevant ones? but then you also have to express ownership). I did think that at one point you have to perform some kind of min-operations on the elements of a vector of distances (or a vector of (distance, rank) tuples) to figure out who has the closest node. (If you store 1/distance, then it just becomes a max operation, and if you need a tie breaker, that involves the |
|
Do you think you can break this problem into smaller chunks that you could independently implement and test? I think that would make it much easier to review and convince ourselves that that's the way to go. |
|
Yeah, sounds good. I think we can implement these things independently of the mesh deformation class (the code in this PR) and unit test it.
I think we should do all of this in this repo even though 2 could go into deal.II eventually. |
|
Hi @bangerth . As discussed at the last call, this is a first draft of the sequential version. It seems to work. For now, I left debug information in. I am happy to walk you through it or discuss the next steps. |
|
Note for later: |
bangerth
left a comment
There was a problem hiding this comment.
Today from another backwood Canadian town (Clearwater, BC). I apologize for the delay.
What do you think of just putting this directly into ASPECT (or perhaps after merging it here)? It doesn't seem like we need a faster-moving ASPECT-branch for now, might as well have it in the master branch.
| /** | ||
| * Return the value of the ASPECT solution at the set of points | ||
| * previously set via set_evaluation_points(). | ||
| * | ||
| * For each point, the return object contains a vector with as | ||
| * many components as there are ASPECT solution components. | ||
| */ | ||
| std::vector<std::vector<double>> | ||
| evaluate_aspect_variables_at_points () const; | ||
|
|
||
| /** | ||
| * Interpolate from velocities given in the evaluation points | ||
| * to ASPECT velocities on the surface in form of a finite | ||
| * element field. | ||
| * | ||
| * The @p velocities, which are typically vertical, were previously | ||
| * computed by the external tool and belong to the current process. | ||
| * The Output is a (global) finite element field vector that in | ||
| * the velocity components of surface nodes corresponds to an | ||
| * interpolation of the velocities provided. | ||
| * | ||
| * The output of this function can then be used as input for a | ||
| * function that implements the | ||
| * compute_velocity_constraints_on_boundary() function of the | ||
| * base class. | ||
| */ | ||
| LinearAlgebra::Vector | ||
| interpolate_external_velocities_to_surface_support_points (const std::vector<Tensor<1,dim>> &velocities) const; |
There was a problem hiding this comment.
I think that these two functions could actually be private, right?
There was a problem hiding this comment.
Far be it from me to point out mistakes in my original design :-) I just finished reading a book in which people eventually become gods, and I consider myself equal to them. So do as you see fit ;-)
| LinearAlgebra::Vector | ||
| interpolate_external_velocities_to_surface_support_points (const std::vector<Tensor<1,dim>> &velocities) const; | ||
|
|
||
| protected: |
There was a problem hiding this comment.
Did you mean for these variables to be private?
| /** | ||
| * A struct to map between DoF indices and evaluation points | ||
| */ | ||
| struct dof_to_eval_point_data | ||
| { | ||
| types::global_dof_index dof_index; | ||
| unsigned int evaluation_point_index; | ||
| unsigned int component; | ||
| }; | ||
|
|
||
| /** | ||
| * A vector to store a map between Dof indices and evaluation points | ||
| */ | ||
| std::vector<dof_to_eval_point_data> map_dof_to_eval_point; |
There was a problem hiding this comment.
As the name of the variable suggests, what do you think of just using std::map here?
I think that what the variable name (and the member evaluation_point_index) is missing is that you're mapping to the closest point, right? So perhaps map_dof_to_closest_eval_point? Or perhaps I'm misunderstanding the mapping?
There was a problem hiding this comment.
It is a map between DoFs and evaluation point, yes. I extended the documentation.
I don't see an advantage in storing it as a std::map from pair<dof_index, component> to evaluation_point_index as this is less efficient and less clear (first and second vs correct names).
| remote_point_evaluator->reinit(this->evaluation_points, this->get_triangulation(), mapping); | ||
|
|
||
|
|
||
| // Create a mapping from evaluation points to support points. Note that one evaluation point can map to |
There was a problem hiding this comment.
I think the map actually goes the other way around (from support points to evaluation points), right?
There was a problem hiding this comment.
It is a mapping between support point and closest evaluation point but used to copy data from evaluation points. :-)
Indeed. I've been thinking about this as well, and it should not be very difficult to gather everything on process 0, and then share it back out via the |
At some point, but this is not in a stage where things are working and providing useful functionality:
Several reasons to have a separate repo in my opinion:
In theory this would allow us to move faster (not sure this is true in practice). |
mesh deformation plugins.
5bb0cba to
49d6495
Compare
bangerth
left a comment
There was a problem hiding this comment.
Let me know when you want me to take another look. I'm ok with merging here first.
|
Debug code removed. The test failures with deal.II master are due to geodynamics#6822 (@bangerth are you planning on fixing this?) |
be4038b to
fdff57e
Compare
|
Yes, thanks for pointing me at that ASPECT issue -- I had missed that. |
FYI @bangerth