Commit 81368def authored by GILLES Sebastien's avatar GILLES Sebastien
Browse files

#1529 Rewrite Mesh::SetReducedCoordsList() to make it much clearer (and more...

#1529 Rewrite Mesh::SetReducedCoordsList() to make it much clearer (and more efficient) - partly due to the shared_ptr now used in Coords.
parent 1ede21b0
......@@ -149,7 +149,7 @@ namespace MoReFEM
#endif // NDEBUG
void Coords::SetLowestProcessor(bool value)
void Coords::SetIsLowestProcessor(bool value)
{
is_lowest_processor_ = value;
}
......
......@@ -346,7 +346,7 @@ namespace MoReFEM
* \internal Despite its public status, this should not be used as it is for low-level usage.
* \endinternal
*
* \return True if no lowest rank consider current \a Coords as a processor-wise one.
* \return True if no lowest rank consider current \a Coords as a processor-wise one. False if the \a Coords is related only to ghosts.
*/
bool IsLowestProcessorRank() const noexcept;
......@@ -384,7 +384,7 @@ namespace MoReFEM
//! Internal mutator to the lowest processor that features this \a Coords as processor-wise.
//! \param[in] value Whether current processor is the lowest one or not.
void SetLowestProcessor(bool value);
void SetIsLowestProcessor(bool value);
......@@ -421,7 +421,6 @@ namespace MoReFEM
* 'Lowest' here refer to the integer rank; so if a \a Coords is both on processor 3 and 7, both of them
* should set this value to 3.
*
* \attention Due to its role, it is filled only for processor-wise \a Coords, not for ghosted ones.
*/
bool is_lowest_processor_ = true;
......
......@@ -350,105 +350,34 @@ namespace MoReFEM
namespace // anonymous
{
/*!
* \brief Return list of the indexes of the \a Coords in the list.
*/
std::vector<unsigned int> GenerateIndexList(const Coords::vector_shared_ptr& coords_list)
{
std::vector<unsigned int> ret(coords_list.size());
const auto end_coords_list = coords_list.cend();
std::transform(coords_list.cbegin(),
end_coords_list,
ret.begin(),
[](const auto& coords_ptr)
{
assert(!(!coords_ptr));
return coords_ptr->GetIndexFromMeshFile();
});
assert(ret.size() == coords_list.size());
Utilities::EliminateDuplicate(ret); // also sort the list
assert(ret.size() == coords_list.size()
&& "Input list should not include duplicate elements.");
return ret;
}
/*!
* \brief Create the new unique_ptr list given the indexes to cherry-pick in the original one.
* \brief Determine the \a Coords that are already present in lower ranks (only counting the processor-wise ones - ghost are discarded here).
*
* (so the list is empty for rank 0...)
*/
Coords::vector_shared_ptr GenerateReducedList(const std::vector<unsigned int>& index_list,
Coords::vector_shared_ptr& list_before_reduction)
std::vector<unsigned int>
ComputeAlreadyFoundOnLowerRanks(const Wrappers::Mpi& mpi,
unsigned int Nprogram_wise_coords,
const Coords::vector_shared_ptr& a_processor_wise_coords_list)
{
Coords::vector_shared_ptr ret;
const auto begin_index_list = index_list.cbegin();
const auto end_index_list = index_list.cend();
const auto size = list_before_reduction.size();
for (auto i = 0ul; i < size; ++i)
{
auto& coords_ptr = list_before_reduction[i];
if (!coords_ptr)
continue;
const auto coords_id = coords_ptr->GetIndexFromMeshFile();
if (std::binary_search(begin_index_list,
end_index_list,
coords_id))
{
ret.emplace_back(std::move(coords_ptr));
list_before_reduction[i] = nullptr;
}
}
assert(ret.size() == index_list.size() && "That's the whole point of the function!");
assert(std::none_of(ret.cbegin(),
ret.cend(),
Utilities::IsNullptr<Coords::shared_ptr>));
return ret;
}
assert(mpi.Nprocessor<int>() > 1);
const auto rank = mpi.GetRank<unsigned int>();
const auto Nproc = mpi.Nprocessor<unsigned int>();
} // namespace anonymous
std::vector<unsigned int> processor_wise_coords_id_list(a_processor_wise_coords_list.size());
std::transform(a_processor_wise_coords_list.cbegin(),
a_processor_wise_coords_list.cend(),
processor_wise_coords_id_list.begin(),
[](const auto& coords_ptr)
{
assert(!(!coords_ptr));
return coords_ptr->GetIndexFromMeshFile();
});
std::vector<unsigned int> ret;
// \todo #1529 shared_ptr should simplify implementation of this one!
void Mesh::SetReducedCoordsList(const Wrappers::Mpi& mpi,
Coords::vector_shared_ptr&& processor_wise_coords_list,
Coords::vector_shared_ptr&& ghosted_coords_list)
{
auto& program_wise_coords_list = GetNonCstProcessorWiseCoordsList(); // still encompasses all program-wise
// \a Coords at this stage.
const auto Nprogram_wise_coords = static_cast<unsigned int>(program_wise_coords_list.size());
// Here I need to keep only the \a Coords related to the processor; but there are hoops to jump to take
// care or the nature of the pointer (raw or unique).
// It's not very efficient, but done only once in the code.
auto processor_wise_coords_id_list = GenerateIndexList(processor_wise_coords_list);
const auto Nproc = mpi.Nprocessor<unsigned int>();
// Prepare the list of \a Coords already considered processor-wise on lower ranks; this will be helpful
// whenever we want to count how many \a Coords are in a \a Domain without counting some twice.
std::vector<unsigned int> already_found_on_lower_ranks;
if (Nproc > 1u)
{
const auto rank = mpi.GetRank<unsigned int>();
if (rank == 0)
{
mpi.SendContainer(1,
......@@ -456,80 +385,129 @@ namespace MoReFEM
}
else if (rank < Nproc - 1u)
{
already_found_on_lower_ranks = mpi.Receive<unsigned int>(rank - 1u, Nprogram_wise_coords);
std::vector<unsigned int> to_send = already_found_on_lower_ranks;
ret = mpi.Receive<unsigned int>(rank - 1u, Nprogram_wise_coords);
std::vector<unsigned int> to_send = ret;
to_send.insert(to_send.end(),
processor_wise_coords_id_list.begin(),
processor_wise_coords_id_list.end());
Utilities::EliminateDuplicate(to_send);
mpi.SendContainer(rank + 1u,
to_send);
}
else
already_found_on_lower_ranks = mpi.Receive<unsigned int>(rank - 1u, Nprogram_wise_coords);
ret = mpi.Receive<unsigned int>(rank - 1u, Nprogram_wise_coords);
assert(std::is_sorted(ret.cbegin(), ret.cend()));
return ret;
}
auto ghosted_coords_id_list = GenerateIndexList(ghosted_coords_list);
} // namespace anonymous
void Mesh::SetIsLowestProcessor(const Wrappers::Mpi& mpi,
unsigned int Nprogram_wise_coords,
const Coords::vector_shared_ptr& a_processor_wise_coords_list) const
{
const auto already_on_lower_ranks = ComputeAlreadyFoundOnLowerRanks(mpi,
Nprogram_wise_coords,
a_processor_wise_coords_list);
const auto begin_already_on_lower_ranks = already_on_lower_ranks.cbegin();
const auto end_already_on_lower_ranks = already_on_lower_ranks.cend();
for (const auto& coords_ptr : a_processor_wise_coords_list)
{
assert(!(!coords_ptr));
const auto id = coords_ptr->GetIndexFromMeshFile();
const bool already_known = std::binary_search(begin_already_on_lower_ranks,
end_already_on_lower_ranks,
id);
coords_ptr->SetIsLowestProcessor(!already_known);
}
}
void Mesh::SetReducedCoordsList(const Wrappers::Mpi& mpi,
Coords::vector_shared_ptr&& a_processor_wise_coords_list,
Coords::vector_shared_ptr&& a_ghosted_coords_list)
{
#ifndef NDEBUG
{
std::vector<unsigned int> intersection;
std::set_intersection(processor_wise_coords_id_list.begin(),
processor_wise_coords_id_list.end(),
ghosted_coords_id_list.begin(),
ghosted_coords_id_list.end(),
std::back_inserter(intersection));
Coords::vector_shared_ptr intersection;
std::set_intersection(a_processor_wise_coords_list.begin(),
a_processor_wise_coords_list.end(),
a_ghosted_coords_list.begin(),
a_ghosted_coords_list.end(),
std::back_inserter(intersection),
[](const auto& lhs, const auto& rhs)
{
assert(!(!lhs));
assert(!(!rhs));
return lhs->template GetPositionInCoordsListInMesh<MpiScale::program_wise>()
< rhs->template GetPositionInCoordsListInMesh<MpiScale::program_wise>();
});
assert(intersection.empty() && "Ghosted coords list is assumed not to encompass Coords already handled "
"by processor-wise list.");
}
#endif // NDEBUG
auto processor_wise_coords_unique_ptr_list =
GenerateReducedList(processor_wise_coords_id_list, program_wise_coords_list);
const auto Nprogram_wise_coords = static_cast<unsigned int>(GetProcessorWiseCoordsList().size());
// < At this stage GetProcessorWiseCoordsList() still encompasses all program-wise Coords
// (this will be wrong at the very next line...)
SetGhostedCoordsList(GenerateReducedList(ghosted_coords_id_list, program_wise_coords_list));
const auto Nprocessor_wise_coords = processor_wise_coords_unique_ptr_list.size();
const auto begin_already_found_on_lower_ranks = already_found_on_lower_ranks.cbegin();
const auto end_already_found_on_lower_ranks = already_found_on_lower_ranks.cend();
auto index = 0ul;
for (; index < Nprocessor_wise_coords; ++index)
SetProcessorWiseCoordsList(std::move(a_processor_wise_coords_list));
SetGhostedCoordsList(std::move(a_ghosted_coords_list));
decltype(auto) processor_wise_coords_list = GetProcessorWiseCoordsList();
decltype(auto) ghost_coords_list = GetGhostedCoordsList();
// Set whether it is the lowest processor the \a Coords is given in processor-wise list.
// As default is false, no operation needed for ghost \a Coords.
{
auto& coords_ptr = processor_wise_coords_unique_ptr_list[static_cast<std::size_t>(index)];
assert(!(!coords_ptr));
auto& coords = *coords_ptr;
coords.SetPositionInCoordsListInMesh<MpiScale::processor_wise>(static_cast<unsigned int>(index));
const auto current_coords_index = coords.GetIndexFromMeshFile();
const auto it_lowest_rank =
std::find(begin_already_found_on_lower_ranks,
end_already_found_on_lower_ranks,
current_coords_index);
coords.SetLowestProcessor(it_lowest_rank == end_already_found_on_lower_ranks);
SetIsLowestProcessor(mpi,
Nprogram_wise_coords,
processor_wise_coords_list);
// For safety mostly - the field shouldn't be called for for a ghost \a Coords.
// (it is required to compute the number of \a Coords in a given \a Domain, which is done only on processor-
// wise ones).
for (const auto& coords_ptr: ghost_coords_list)
{
assert(!(!coords_ptr));
coords_ptr->SetIsLowestProcessor(false);
}
}
// By convention, ghost coords indexing follow the same indexing (i.e. first ghosted Coords gets
// Nprocessor_wise_coords index).
decltype(auto) ghosted_coords_unique_ptr_list = GetGhostedCoordsList();
for (const auto& coords_ptr : ghosted_coords_unique_ptr_list)
// Finally set the index which gives the position in the \a Mesh.
{
assert(!(!coords_ptr));
// index is the same as in previous loop!
coords_ptr->SetPositionInCoordsListInMesh<MpiScale::processor_wise>(static_cast<unsigned int>(index++));
auto index = 0u;
for (const auto& coords_ptr : processor_wise_coords_list)
{
assert(!(!coords_ptr));
coords_ptr->SetPositionInCoordsListInMesh<MpiScale::processor_wise>(index++);
}
// By convention, ghost coords indexing follow the same indexing (i.e. first ghosted Coords gets
// Nprocessor_wise_coords index).
for (const auto& coords_ptr : ghost_coords_list)
{
assert(!(!coords_ptr));
coords_ptr->SetPositionInCoordsListInMesh<MpiScale::processor_wise>(index++);
}
}
std::swap(program_wise_coords_list, processor_wise_coords_unique_ptr_list);
}
......@@ -1023,6 +1001,12 @@ namespace MoReFEM
}
void Mesh::SetProcessorWiseCoordsList(Coords::vector_shared_ptr&& list)
{
processor_wise_coords_list_ = std::move(list);
}
void Mesh::SortPrepartitionedCoords(std::size_t Nprocessor_wise)
{
auto& full_coords_list = GetNonCstProcessorWiseCoordsList();
......
......@@ -616,6 +616,8 @@ namespace MoReFEM
* with ghosted \a NodeBearer. If a \a Coords is related to both a processor-wise and a ghost \a NodeBearer,
* it won't appear in this list (put differently, intersection of processor_wise_coords_list and
* ghosted_coords_list is the empty set).
*
* The internal index and the lowest processor for each \a Coords aren't yet computed; they will be only after the call to \a SetReducedCoordsList.
*/
/*!
......@@ -655,6 +657,23 @@ namespace MoReFEM
Coords::vector_shared_ptr&& ghosted_coords_list);
/*!
* \brief For all processor-wise \a Coords, determine which is the lowest rank in which they appear (not counting ghosts).
*
* \internal This is an internal method that is meant to be called by SetReducedCoordsList() only.
*
* The point is to be able to count without duplicating the number of \a Coords inside a given \a Domain.
*
* \copydoc doxygen_hide_mpi_param
* \param[in] Nprogram_wise_coords Number of program-wise coords. This is required for internal Mpi computations,
* \param[in,out] a_processor_wise_coords_list The processor-wise list of \a Coords. The list itself is not modified, but the point
* of the method is to set properly the internal field \a is_lowest_processor_, hence the 'out' status.
*/
void SetIsLowestProcessor(const Wrappers::Mpi& mpi,
unsigned int Nprogram_wise_coords,
const Coords::vector_shared_ptr& a_processor_wise_coords_list) const;
private:
/*!
......@@ -751,6 +770,11 @@ namespace MoReFEM
//! Non constant access to the list of all \a Coords on the processor.
Coords::vector_shared_ptr& GetNonCstProcessorWiseCoordsList() noexcept;
//! Mutator the list of the \a Coords related to processor-wise \a NodeBearer.
//! \param[in] list The list of ghosted \a Coords computed outside the \a Mesh.
void SetProcessorWiseCoordsList(Coords::vector_shared_ptr&& list);
//! Mutator the list of the \a Coords related ONLY to ghosted \a NodeBearer.
//! \param[in] list The list of ghosted \a Coords computed outside the \a Mesh.
void SetGhostedCoordsList(Coords::vector_shared_ptr&& list);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment