Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MappingQEulerian GMG bug #12791

Merged
merged 3 commits into from Oct 7, 2021
Merged

MappingQEulerian GMG bug #12791

merged 3 commits into from Oct 7, 2021

Conversation

tjhei
Copy link
Member

@tjhei tjhei commented Oct 5, 2021

This tests changes the mesh of tests/mappings/mapping_q_eulerian_08.cc to trigger the error

An error occurred in line <1638> of file <../include/deal.II/lac/la_parallel_vector.h> in function
    Number dealii::LinearAlgebra::distributed::Vector<Number, MemorySpace>::operator()(dealii::LinearAlgebra::distributed::Vector<Number, MemorySpace>::size_type) const [with Number = double; MemorySpace = dealii::MemorySpace::Host; dealii::LinearAlgebra::distributed::Vector<Number, MemorySpace>::size_type = unsigned int]
The violated condition was:
    partitioner->in_local_range(global_index) || partitioner->ghost_indices().is_element(global_index)

when updating a MappingQEulerian on multigrid (with 4 ranks only).

Any help is appreciated.

@tjhei
Copy link
Member Author

tjhei commented Oct 5, 2021

Here is the call-stack:

#0  0x00007fffd8b9f340 in PMPI_Abort () from /lib/x86_64-linux-gnu/libmpich.so.12
#1  0x00007fffec987737 in dealii::deal_II_exceptions::internals::abort (exc=...) at ../source/base/exceptions.cc:487
#2  0x00007fffe54cb6db in dealii::deal_II_exceptions::internals::issue_error_noreturn<dealii::LinearAlgebra::distributed::Vector<double, dealii::MemorySpace::Host>::ExcAccessToNonLocalElement> (handling=dealii::deal_II_exceptions::internals::abort_or_throw_on_exception, 
    file=0x7ffff27d29c0 "../include/deal.II/lac/la_parallel_vector.h", line=1638, 
    function=0x7ffff27f6d48 "Number dealii::LinearAlgebra::distributed::Vector<Number, MemorySpace>::operator()(dealii::LinearAlgebra::distributed::Vector<Number, MemorySpace>::size_type) const [with Number = double; MemorySpace "..., 
    cond=0x7ffff27d2818 "partitioner->in_local_range(global_index) || partitioner->ghost_indices().is_element(global_index)", 
    exc_name=0x7ffff27d2778 "ExcAccessToNonLocalElement(global_index, partitioner->local_range().first, partitioner->local_range().second - 1, partitioner->ghost_indices().n_elements())", e=...) at ../include/deal.II/base/exceptions.h:1363
#3  0x00007fffe57c878f in dealii::LinearAlgebra::distributed::Vector<double, dealii::MemorySpace::Host>::operator() (this=0x5555558a6590, global_index=690)
    at ../include/deal.II/lac/la_parallel_vector.h:1638
#4  0x00007fffe681eb90 in dealii::internal::ElementAccess<dealii::LinearAlgebra::distributed::Vector<double, dealii::MemorySpace::Host> >::get (V=..., i=690)
    at ../include/deal.II/lac/vector_element_access.h:76
#5  0x00007fffe83c2c77 in dealii::internal::get_vector_element<dealii::LinearAlgebra::distributed::Vector<double, dealii::MemorySpace::Host> > (vector=..., 
    cell_number=690) at ../source/fe/fe_values.cc:65
#6  0x00007fffe93cb84e in dealii::FEValuesBase<2, 2>::get_function_values<dealii::LinearAlgebra::distributed::Vector<double, dealii::MemorySpace::Host> > (
    this=0x5555558c3780, fe_function=..., indices=..., values=std::vector of length 9, capacity 9 = {...}) at ../source/fe/fe_values.cc:3615
#7  0x00007fffeb0cc4a4 in dealii::MappingQEulerian<2, dealii::LinearAlgebra::distributed::Vector<double, dealii::MemorySpace::Host>, 2>::compute_mapping_support_points (this=0x5555558c3500, cell=...) at ../source/fe/mapping_q_eulerian.cc:175
#8  0x00007fffe7e4544f in dealii::MappingQ<2, 2>::fill_fe_values (this=0x5555558c3500, cell=..., cell_similarity=dealii::CellSimilarity::invalid_next_cell, 
    quadrature=..., internal_data=..., output_data=...) at ../source/fe/mapping_q.cc:970
#9  0x00007fffeb0cc7ff in dealii::MappingQEulerian<2, dealii::LinearAlgebra::distributed::Vector<double, dealii::MemorySpace::Host>, 2>::fill_fe_values (
    this=0x5555558c3500, cell=..., quadrature=..., internal_data=..., output_data=...) at ../source/fe/mapping_q_eulerian.cc:208
#10 0x00007fffe82e4613 in dealii::FEValues<2, 2>::do_reinit (this=0x7fffffff7b80) at ../source/fe/fe_values.cc:4557
#11 0x00007fffe9289885 in dealii::FEValues<2, 2>::reinit (this=0x7fffffff7b80, cell=...) at ../source/fe/fe_values.cc:4507
#12 0x00007ffff10fbf17 in dealii::internal::MatrixFreeFunctions::ExtractCellHelper::mapping_q_query_fe_values<2> (begin_cell=0, end_cell=20, mapping_q=..., 
    tria=..., cell_array=std::vector of length 20, capacity 20 = {...}, jacobian_size=1.4142135623730951, 
    preliminary_cell_type=std::vector of length 20, capacity 20 = {...}, plain_quadrature_points=..., jacobians_on_stencil=...)
    at ../include/deal.II/matrix_free/mapping_info.templates.h:1256
#13 0x00007ffff11c8f96 in dealii::std_cxx17::apply_impl<void (* const&)(unsigned int, unsigned int, dealii::MappingQ<2, 2> const&, dealii::Triangulation<2, 2> const&, std::vector<std::pair<unsigned int, unsigned int>, std::allocator<std::pair<unsigned int, unsigned int> > > const&, double, std::vector<dealii::internal::MatrixFreeFunctions::GeometryType, std::allocator<dealii::internal::MatrixFreeFunctions::GeometryType> >&, dealii::AlignedVector<double>&, dealii::AlignedVector<std::array<dealii::Tensor<2, 2, double>, 3ul> >&), std::tuple<unsigned int, unsigned int, dealii::MappingQ<2, 2> const&, dealii::Triangulation<2, 2> const&, std::vector<std::pair<unsigned int, unsigned int>, std::allocator<std::pair<unsigned int, unsigned int> > > const&, double, std::vector<dealii::internal::MatrixFreeFunctions::GeometryType, std::allocator<dealii::internal::MatrixFreeFunctions::GeometryType> >&, dealii::AlignedVector<double>&, dealii::AlignedVector<std::array<dealii::Tensor<2, 2, double>, 3ul> >&> const&, 0ul, 1ul, 2ul, 3ul, 4ul, 5ul, 6ul, 7ul, 8ul> (
    fn=@0x555555889f50: 0x7ffff10fbd71 <dealii::internal::MatrixFreeFunctions::ExtractCellHelper::mapping_q_query_fe_values<2>(unsigned int, unsigned int, dealii::MappingQ<2, 2> const&, dealii::Triangulation<2, 2> const&, std::vector<std::pair<unsigned int, unsigned int>, std::allocator<std::pair<unsigned int, unsigned int> > > const&, double, std::vector<dealii::internal::MatrixFreeFunctions::GeometryType, std::allocator<dealii::internal::MatrixFreeFunctions::GeometryType> >&, dealii::AlignedVector<double>&, dealii::AlignedVector<std::array<dealii::Tensor<2, 2, double>, (2)+(1)> >&)>, t=std::tuple containing = {...})
    at ../include/deal.II/base/std_cxx17/tuple.h:31
#14 0x00007ffff11965c5 in dealii::std_cxx17::apply<void (* const&)(unsigned int, unsigned int, dealii::MappingQ<2, 2> const&, dealii::Triangulation<2, 2> const&, std::vector<std::pair<unsigned int, unsigned int>, std::allocator<std::pair<unsigned int, unsigned int> > > const&, double, std::vector<dealii::internal::MatrixFreeFunctions::GeometryType, std::allocator<dealii::internal::MatrixFreeFunctions::GeometryType> >&, dealii::AlignedVector<double>&, dealii::AlignedVector<std::array<dealii::Tensor<2, 2, double>, 3ul> >&), std::tuple<unsigned int, unsigned int, dealii::MappingQ<2, 2> const&, dealii::Triangulation<2, 2> const&, std::vector<std::pair<unsigned int, unsigned int>, std::allocator<std::pair<unsigned int, unsigned int> > > const&, double, std::vector<dealii::internal::MatrixFreeFunctions::GeometryType, std::allocator<dealii::internal::MatrixFreeFunctions::GeometryType> >&, dealii::AlignedVector<double>&, dealii::AlignedVector<std::array<dealii::Tensor<2, 2, double>, 3ul> >&> const&> (
    fn=@0x555555889f50: 0x7ffff10fbd71 <dealii::internal::MatrixFreeFunctions::ExtractCellHelper::mapping_q_query_fe_values<2>(unsigned int, unsigned int, dealii::MappingQ<2, 2> const&, dealii::Triangulation<2, 2> const&, std::vector<std::pair<unsigned int, unsigned int>, std::allocator<std::pair<unsigned int, unsigned int> > > const&, double, std::vector<dealii::internal::MatrixFreeFunctions::GeometryType, std::allocator<dealii::internal::MatrixFreeFunctions::GeometryType> >&, dealii::AlignedVector<double>&, dealii::AlignedVector<std::array<dealii::Tensor<2, 2, double>, (2)+(1)> >&)>, t=std::tuple containing = {...})
    at ../include/deal.II/base/std_cxx17/tuple.h:44
#15 0x00007ffff10fc8fe in dealii::Threads::new_task<void, unsigned int, unsigned int, dealii::MappingQ<2, 2> const&, dealii::Triangulation<2, 2> const&, std::vector<std::pair<unsigned int, unsigned int>, std::allocator<std::pair<unsigned int, unsigned int> > > const&, double, std::vector<dealii::internal::MatrixFreeFunctions::GeometryType, std::allocator<dealii::internal::MatrixFreeFunctions::GeometryType> >&, dealii::AlignedVector<double>&, dealii::AlignedVector<std::array<dealii::Tensor<2, 2, double>, 3ul> >&>(void (*)(unsigned int, unsigned int, dealii::MappingQ<2, 2> const&, dealii::Triangulation<2, 2> const&, std::vector<std::pair<unsigned int, unsigned int>, std::allocator<std::pair<unsigned int, unsigned int> > > const&, double, std::vector<dealii::internal::MatrixFreeFunctions::GeometryType, std::allocator<dealii::internal::MatrixFreeFunctions::GeometryType> >&, dealii::AlignedVector<double>&, dealii::AlignedVector<std::array<dealii::Tensor<2, 2, double>, 3ul> >&), dealii::identity<unsigned int>::type, dealii::identity<unsigned int>::type, dealii::identity<dealii::MappingQ<2, 2> const&>::type, dealii::identity<dealii::Triangulation<2, 2> const&>::type, dealii::identity<std::vector<std::pair<unsigned int, unsigned int>, std::allocator<std::pair<unsigned int, unsigned int> > > const&>::type, dealii::identity<double>::type, dealii::identity<std::vector<dealii::internal::MatrixFreeFunctions::GeometryType, std::allocator<dealii::internal::MatrixFreeFunctions::GeometryType> >&>::type, dealii::identity<dealii::AlignedVector<double>&>::type, dealii::identity<dealii::AlignedVector<std::array<dealii::Tensor<2, 2, double>, 3ul> >&>::type)::{lambda()#1}::operator()() const (this=0x555555889f10)
    at ../include/deal.II/base/thread_management.h:1434
#16 0x00007ffff11e9f78 in std::_Function_handler<void (), dealii::Threads::new_task<void, unsigned int, unsigned int, dealii::MappingQ<2, 2> const&, dealii::Triangulation<2, 2> const&, std::vector<std::pair<unsigned int, unsigned int>, std::allocator<std::pair<unsigned int, unsigned int> > > const&, double, std::vector<dealii::internal::MatrixFreeFunctions::GeometryType, std::allocator<dealii::internal::MatrixFreeFunctions::GeometryType> >&, dealii::AlignedVector<double>&, dealii::AlignedVector<std::array<dealii::Tensor<2, 2, double>, 3ul> >&>(void (*)(unsigned int, unsigned int, dealii::MappingQ<2, 2> const&, dealii::Triangulation<2, 2> const&, std::vector<std::pair<unsigned int, unsigned int>, std::allocator<std::pair<unsigned int, unsigned int> > > const&, double, std::vector<dealii::internal::MatrixFreeFunctions::GeometryType, std::allocator<dealii::internal::MatrixFreeFunctions::GeometryType> >&, dealii::AlignedVector<double>&, dealii::AlignedVector<std::array<dealii::Tensor<2, 2, double>, 3ul> >&), dealii::identity<unsigned int>::type, dealii::identity<unsigned int>::type, dealii::identity<dealii::MappingQ<2, 2> const&>::type, dealii::identity<dealii::Triangulation<2, 2> const&>::type, dealii::identity<std::vector<std::pair<unsigned int, unsigned int>, std::allocator<std::pair<unsigned int, unsigned int> > > const&>::type, dealii::identity<double>::type, dealii::identity<std::vector<dealii::internal::MatrixFreeFunctions::GeometryType, std::allocator<dealii::internal::MatrixFreeFunctions::GeometryType> >&>::type, dealii::identity<dealii::AlignedVector<double>&>::type, dealii::identity<dealii::AlignedVector<std::array<dealii::Tensor<2, 2, double>, 3ul> >&>::type)::{lambda()#1}>::_M_invoke(std::_Any_data const&) (
    __functor=...) at /usr/include/c++/9/bits/std_function.h:300
#17 0x00007fffe7ab57d2 in std::function<void ()>::operator()() const (this=0x7fffffff83b0) at /usr/include/c++/9/bits/std_function.h:688
#18 0x00007fffe7ac2865 in dealii::Threads::internal::evaluate_and_set_promise<std::function<void ()> const>(std::function<void ()> const&, std::promise<void>&) (function=..., promise=...) at ../include/deal.II/base/thread_management.h:960
#19 0x00007fffe7abea89 in dealii::Threads::Task<void>::Task(std::function<void ()> const&) (this=0x7fffffff8760, function_object=...)
    at ../include/deal.II/base/thread_management.h:1029
#20 0x00007fffe7abccd8 in dealii::Threads::new_task<void>(std::function<void ()> const&) (function=...) at ../include/deal.II/base/thread_management.h:1330
#21 0x00007ffff1196693 in dealii::Threads::new_task<dealii::Threads::new_task<void, unsigned int, unsigned int, dealii::MappingQ<2, 2> const&, dealii::Triangulation<2, 2> const&, std::vector<std::pair<unsigned int, unsigned int>, std::allocator<std::pair<unsigned int, unsigned int> > > const&, double, std::vector<dealii::internal::MatrixFreeFunctions::GeometryType, std::allocator<dealii::internal::MatrixFreeFunctions::GeometryType> >&, dealii::AlignedVector<double>&, dealii::AlignedVector<std::array<dealii::Tensor<2, 2, double>, 3ul> >&>(void (*)(unsigned int, unsigned int, dealii::MappingQ<2, 2> const&, dealii::Triangulation<2, 2> const&, std::vector<std::pair<unsigned int, unsigned int>, std::allocator<std::pair<unsigned int, unsigned int> > > const&, double, std::vector<dealii::internal::MatrixFreeFunctions::GeometryType, std::allocator<dealii::internal::MatrixFreeFunctions::GeometryType> >&, dealii::AlignedVector<double>&, dealii::AlignedVector<std::array<dealii::Tensor<2, 2, double>, 3ul> >&), dealii::identity<unsigned int>::type, dealii::identity<unsigned int>::type, dealii::identity<dealii::MappingQ<2, 2> const&>::type, dealii::identity<dealii::Triangulation<2, 2> const&>::type, dealii::identity<std::vector<std::pair<unsigned int, unsigned int>, std::allocator<std::pair<unsigned int, unsigned int> > > const&>::type, dealii::identity<double>::type, dealii::identity<std::vector<dealii::internal::MatrixFreeFunctions::GeometryType, std::allocator<dealii::internal::MatrixFreeFunctions::GeometryType> >&>::type, dealii::identity<dealii::AlignedVector<double>&>::type, dealii::identity<dealii::AlignedVector<std::array<dealii::Tensor<2, 2, double>, 3ul> >&>::type)::{lambda()#1}>(dealii::Threads::new_task<void, unsigned int, unsigned int, dealii::MappingQ<2, 2> const&, dealii::Triangulation<2, 2> const&, std::vector<std::pair<unsigned int, unsigned int>, std::allocator<std::pair<unsigned int, unsigned int> > > const&, double, std::vector<dealii::internal::MatrixFreeFunctions::GeometryType, std::allocator<dealii::internal::MatrixFreeFunctions::GeometryType> >&, dealii::AlignedVector<double>&, dealii::AlignedVector<std::array<dealii::Tensor<2, 2, double>, 3ul> >&>(void (*)(unsigned int, unsigned int, dealii::MappingQ<2, 2> const&, dealii::Triangulation<2, 2> const&, std::vector<std::pair<unsigned int, unsigned int>, std::allocator<std::pair<unsigned int, unsigned int> > > const&, double, std::vector<dealii::internal::MatrixFreeFunctions::GeometryType, std::allocator<dealii::internal::MatrixFreeFunctions::GeometryType> >&, dealii::AlignedVector<double>&, dealii::AlignedVector<std::array<dealii::Tensor<2, 2, double>, 3ul> >&), dealii::identity<unsigned int>::type, dealii::identity<unsigned int>::type, dealii::identity<dealii::MappingQ<2, 2> const&>::type, dealii::identity<dealii::Triangulation<2, 2> const&>::type, dealii::identity<std::vector<std::pair<unsigned int, unsigned int>, std::allocator<std::pair<unsigned int, unsigned int> > > const&>::type, dealii::identity<double>::type, dealii::identity<std::vector<dealii::internal::MatrixFreeFunctions::GeometryType, std::allocator<dealii::internal::MatrixFreeFunctions::GeometryType> >&>::type, dealii::identity<dealii::AlignedVector<double>&>::type, dealii::identity<dealii::AlignedVector<std::array<dealii::Tensor<2, 2, double>, 3ul> >&>::type)::{lambda()#1}) (function_object=...) at ../include/deal.II/base/thread_management.h:1417
#22 0x00007ffff10fcb4a in dealii::Threads::new_task<void, unsigned int, unsigned int, dealii::MappingQ<2, 2> const&, dealii::Triangulation<2, 2> const&, std::vector<std::pair<unsigned int, unsigned int>, std::allocator<std::pair<unsigned int, unsigned int> > > const&, double, std::vector<dealii::internal::MatrixFreeFunctions::GeometryType, std::allocator<dealii::internal::MatrixFreeFunctions::GeometryType> >&, dealii::AlignedVector<double>&, dealii::AlignedVector<std::array<dealii::Tensor<2, 2, double>, 3ul> >&> (
    fun_ptr=0x7ffff10fbd71 <dealii::internal::MatrixFreeFunctions::ExtractCellHelper::mapping_q_query_fe_values<2>(unsigned int, unsigned int, dealii::MappingQ<2, 2> const&, dealii::Triangulation<2, 2> const&, std::vector<std::pair<unsigned int, unsigned int>, std::allocator<std::pair<unsigned int, unsigned int> > > const&, double, std::vector<dealii::internal::MatrixFreeFunctions::GeometryType, std::allocator<dealii::internal::MatrixFreeFunctions::GeometryType> >&, dealii::AlignedVector<double>&, dealii::AlignedVector<std::array<dealii::Tensor<2, 2, double>, (2)+(1)> >&)>) at ../include/deal.II/base/thread_management.h:1434
#23 0x00007ffff108cf76 in dealii::internal::MatrixFreeFunctions::MappingInfo<2, double, dealii::VectorizedArray<double, 2ul> >::compute_mapping_q (
    this=0x7fffffffbec0, tria=..., cell_array=std::vector of length 20, capacity 20 = {...}, faces=std::vector of length 0, capacity 0)
    at ../include/deal.II/matrix_free/mapping_info.templates.h:2818
#24 0x00007ffff108c324 in dealii::internal::MatrixFreeFunctions::MappingInfo<2, double, dealii::VectorizedArray<double, 2ul> >::initialize (
    this=0x7fffffffbec0, tria=..., cells=std::vector of length 20, capacity 20 = {...}, face_info=..., active_fe_index=std::vector of length 0, capacity 0, 
    mapping=std::shared_ptr<dealii::hp::MappingCollection<2, 2>> (use count 2, weak count 0) = {...}, quad=std::vector of length 1, capacity 1 = {...}, 
    update_flags_cells=99, update_flags_boundary_faces=dealii::update_default, update_flags_inner_faces=dealii::update_default, 
    update_flags_faces_by_cells=dealii::update_default) at ../include/deal.II/matrix_free/mapping_info.templates.h:451
#25 0x00007ffff15f3c6c in dealii::MatrixFree<2, double, dealii::VectorizedArray<double, 2ul> >::internal_reinit<double, 2> (this=0x7fffffffbe00, 
    mapping=std::shared_ptr<dealii::hp::MappingCollection<2, 2>> (use count 2, weak count 0) = {...}, 
    dof_handler=std::vector of length 1, capacity 1 = {...}, constraint=std::vector of length 1, capacity 1 = {...}, 
    locally_owned_dofs=std::vector of length 1, capacity 1 = {...}, quad=std::vector of length 1, capacity 1 = {...}, additional_data=...)
    at ../include/deal.II/matrix_free/matrix_free.templates.h:774
#26 0x00005555556408a9 in dealii::MatrixFree<2, double, dealii::VectorizedArray<double, 2ul> >::reinit<dealii::QGauss<1>, double, dealii::MappingQEulerian<2, dealii::LinearAlgebra::distributed::Vector<double, dealii::MemorySpace::Host>, 2> > (this=0x7fffffffbe00, mapping=..., dof_handler=..., constraints_in=..., 
    quad=..., additional_data=...) at /ssd/deal-git3/include/deal.II/matrix_free/matrix_free.h:2995
#27 0x000055555563996b in test<2, 2, 3, double, double> (n_ref=0) at /ssd/deal-git3/tests/mappings/mapping_q_eulerian_14.cc:313
#28 0x000055555562628e in main (argc=1, argv=0x7fffffffdae8) at /ssd/deal-git3/tests/mappings/mapping_q_eulerian_14.cc:368

Just beautiful!

@tjhei
Copy link
Member Author

tjhei commented Oct 5, 2021

My interpretation is that we are missing some ghost indices but I have no idea how to determine what I might need. Any ideas? @kronbichler ?

Comment on lines 247 to 251
MGLevelObject<LinearAlgebra::distributed::Vector<LevelNumberType>>
displacement_level(min_level, max_level);
mg_transfer_euler.interpolate_to_mg(dof_handler_euler,
displacement_level,
displacement);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "bug" is here, and is fixed by adding this code before the interpolate_to_mg call:

  for (unsigned int level = 0; level <= max_level; ++level)
    {
      IndexSet relevant_mg_dofs;
      DoFTools::extract_locally_relevant_level_dofs(dof_handler_euler,
                                                    level,
                                                    relevant_mg_dofs);
      displacement_level[level].reinit(dof_handler_euler.locally_owned_mg_dofs(
                                         level),
                                       relevant_mg_dofs,
                                       mpi_communicator);
    }

This situation reveals an oversight in our multigrid classes, though: We somewhat naively assume that the interpolate_to_mg would need the same indices as the restrict and prolongate functions (implied by copy_to_mg). However, one might want to have more ghosts especially in the present context of MappingQEulerian, as we also want to be able to fill the information on all ghost cells. Or, as in the case here, it might even fail on just the locally owned cells when some of the cells get handled by another MPI process in the transfer setting.

In our CFD application code, we use a similar strategy, namely to copy to a vector to more ghosts after the transfer has been execute (in order not to rely on the fact that copy_to_mg preserves the ghost state on the level vectors), namely: https://github.com/exadg/exadg/blob/2eabd006f482001b22251632b3c97644a9918706/include/exadg/grid/mapping_dof_vector.h#L306-L322
The main question is whether we should always create vectors with all ghosts for the interpolate_to_mg functions, i.e., here

for (unsigned int level = min_level; level <= max_level; ++level)
if (dst[level].size() != dof_handler.n_dofs(level) ||
dst[level].locally_owned_size() !=
dof_handler.locally_owned_mg_dofs(level).n_elements())
dst[level].reinit(this->vector_partitioners[level]);
// copy fine level vector to active cells in MG hierarchy
this->copy_to_mg(dof_handler, dst, src, true);
and
for (unsigned int level = min_level; level <= max_level; ++level)
initialize_dof_vector(level, dst[level]);
would need to be adjusted accordingly. Or we need to document this more clearly. Opinions?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This situation reveals an oversight in our multigrid classes, though: We somewhat naively assume that the interpolate_to_mg would need the same indices as the restrict and prolongate functions

That makes sense and I can confirm that this fixes the issue. I was mislead because I used tests/mappings/mapping_q_eulerian_08|09 as a basis for the code.

In our CFD application code, we use a similar strategy, namely to copy to a vector to more ghosts after the transfer has been execute

Is there an advantage to doing it afterwards? Is this because you use the transfer for something else?

The main question is whether we should always create vectors with all ghosts for the interpolate_to_mg functions

Do you think this would incur a significant cost? We probably only need this change when using MappingQEulerian...

Copy link
Member

@kronbichler kronbichler Oct 6, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason why we do it afterwards is that we do not rely on the fact that the copy_to_mg does not change the ghost layer. I do not think we make a guarantee here (or do we @peterrum, you have been working a lot with MG recently), so that was merely for being on the conservative side. But yes I agree, doing before the operation is in line with the original intent of these functions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I think what would be consistent to cover the following three cases:

  1. input MG-vectors have the correct ghost DoFs: nothing to do
  2. ... have size 0: resize so that it matches with 1)
  3. ... have different ghost DoFs: create temporal mg vectors, proceed with 1) and finally copy the results to the actual output mg vector

What do you think?

I really don't like if the function change somewhere the ghost state of the vector, which might lead to unexpected behaviors. This reminds me that there is still an open PR I need to finalize (copy_to_mg - #11871)...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with this concept of the three cases, our transfer classes should not change the ghost state, and working towards eliminating the need for things like adjust_ghost_range_if_necessary, by making the vectors we have inside the multigrid hierarchy compatible with the matrix-vector product (no copy) and transfer (try to avoid deep copy on as many fine levels as possible). For the transfer, we need to enable user-provided initialization of vectors (either a partitioner or a lambda, as discussed in the other PR) and in case we find them match (as done in MGTransferMatrixFree), we use that ghost approach, and if not, we create a copy.

The main point of action would now be to see over our interfaces. The code I linked to above is already problematic because we check

for (unsigned int level = min_level; level <= max_level; ++level)
if (dst[level].size() != dof_handler.n_dofs(level) ||
dst[level].locally_owned_size() !=
dof_handler.locally_owned_mg_dofs(level).n_elements())
dst[level].reinit(this->vector_partitioners[level]);

I think we should change that to if (dst[level].size() == 0) in this code above, and add an assert in the else case checking AssertDimension(dst[level].locally_owned_size(), dof_handler.dof_handler.locally_owned_mg_dofs(level).n_elements()). We then later check for the ghost state in
if (dst[level].get_partitioner().get() ==
this->vector_partitioners[level].get())
input = &dst[level];
else
{
ghosted_fine.reinit(this->vector_partitioners[level]);
ghosted_fine.copy_locally_owned_data_from(dst[level]);
ghosted_fine.update_ghost_values();
input = &ghosted_fine;
}
- this seems to be the most obvious design. And then document these three points in the two main transfer classes that are sensitive to these settings, MGTransferGlobalCoarsening and MGTransferMatrixFree.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course, our documentation would need to be very clear on the error observed by @tjhei in the original post, that one cannot and should not rely the ghosts to be set adequately, and to ideally present the function with a vector including the relevant ghosts that get filled (either in the same vector, or by a copy inside our functions).

@tjhei
Copy link
Member Author

tjhei commented Oct 6, 2021

Oh, and thank you.

@tjhei
Copy link
Member Author

tjhei commented Oct 6, 2021

I added the code block to _08 as well. Would it make sense to merge this PR?

Copy link
Member

@kronbichler kronbichler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is very good now and indeed good to have the check.

@kronbichler kronbichler merged commit 37d6046 into dealii:master Oct 7, 2021
@tjhei tjhei deleted the bug-eulerian-gmg branch October 7, 2021 16:29
@tjhei
Copy link
Member Author

tjhei commented Oct 7, 2021

I thought about this some more @peterrum and @kronbichler . I think interpolate_to_mg is a different use case compared to copy_to_mg. I can think of two situations for using interpolate_to_mg: MappingQEulerian and using a solution vector for a nonlinear problem or as a coefficient. In both cases, relevant dofs is the correct ghost set.
My suggestion would be:
if (dst[level].size()==0) allocate_with_relevant_dofs; else do nothing and keep input;

Is that an acceptable change?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants