diff --git a/applications/HDF5Application/custom_io/hdf5_connectivities_data.cpp b/applications/HDF5Application/custom_io/hdf5_connectivities_data.cpp index 343369247550..b6a621a7a515 100644 --- a/applications/HDF5Application/custom_io/hdf5_connectivities_data.cpp +++ b/applications/HDF5Application/custom_io/hdf5_connectivities_data.cpp @@ -90,7 +90,10 @@ void ConnectivitiesData::CreateEntities(NodesContainerType& rNodes, } ElementType::Pointer p_elem = r_elem.Create(mIds[i], nodes, rProperties(mPropertiesIds[i])); - rElements.push_back(p_elem); + // here we can safely use the insert with the rElements.end() as the hint + // because, when reading, we can assume that it was written in the + // sorted order within HDF5. + rElements.insert(rElements.end(), p_elem); } KRATOS_CATCH(""); } @@ -119,7 +122,10 @@ void ConnectivitiesData::CreateEntities(NodesContainerType& rNodes, } Condition::Pointer p_cond = r_cond.Create(mIds[i], nodes, rProperties(mPropertiesIds[i])); - rConditions.push_back(p_cond); + // here we can safely use the insert with the rConditions.end() as the hint + // because, when reading, we can assume that it was written in the + // sorted order within HDF5. + rConditions.insert(rConditions.end(), p_cond); } KRATOS_CATCH(""); } diff --git a/applications/HDF5Application/custom_io/hdf5_points_data.cpp b/applications/HDF5Application/custom_io/hdf5_points_data.cpp index 8e7c4fa5d5ff..354a0006bb55 100644 --- a/applications/HDF5Application/custom_io/hdf5_points_data.cpp +++ b/applications/HDF5Application/custom_io/hdf5_points_data.cpp @@ -36,7 +36,10 @@ void PointsData::CreateNodes(NodesContainerType& rNodes) const array_1d& r_coord = mCoords[i]; NodeType::Pointer p_node = Kratos::make_intrusive( mIds[i], r_coord[0], r_coord[1], r_coord[2]); - rNodes.push_back(p_node); + // here we can safely use the insert with the rNodes.end() as the hint + // because, when reading, we can assume that it was written in the + // sorted order within HDF5. + rNodes.insert(rNodes.end(), p_node); } KRATOS_CATCH(""); } diff --git a/applications/MeshingApplication/custom_utilities/parmmg/pmmg_utilities.cpp b/applications/MeshingApplication/custom_utilities/parmmg/pmmg_utilities.cpp index 0a4022e0f9c1..b431c8e04dc7 100644 --- a/applications/MeshingApplication/custom_utilities/parmmg/pmmg_utilities.cpp +++ b/applications/MeshingApplication/custom_utilities/parmmg/pmmg_utilities.cpp @@ -1184,9 +1184,9 @@ void ParMmgUtilities::WriteMeshDataToModelPart( std::unordered_map color_nodes, first_color_cond, second_color_cond, first_color_elem, second_color_elem; // The tempotal store of - ConditionsArrayType created_conditions_vector; - ElementsArrayType created_elements_vector; - NodesArrayType created_nodes_vector; + std::vector created_conditions_vector; + std::vector created_elements_vector; + std::vector created_nodes_vector; // Auxiliar values int ref, is_required; diff --git a/kratos/benchmarks/model_part_benchmark.cpp b/kratos/benchmarks/model_part_benchmark.cpp new file mode 100644 index 000000000000..7e5531aa0ebc --- /dev/null +++ b/kratos/benchmarks/model_part_benchmark.cpp @@ -0,0 +1,274 @@ +// | / | +// ' / __| _` | __| _ \ __| +// . \ | ( | | ( |\__ ` +// _|\_\_| \__,_|\__|\___/ ____/ +// Multi-Physics +// +// License: BSD License +// Kratos default license: kratos/license.txt +// +// Main authors: Suneth Warnakulasuriya +// + +// System includes +#include + +// External includes +#include + +// Project includes +#include "containers/model.h" +#include "containers/pointer_vector_set.h" +#include "includes/indexed_object.h" +#include "includes/key_hash.h" +#include "includes/model_part.h" +#include "includes/smart_pointers.h" +#include "utilities/assign_unique_model_part_collection_tag_utility.h" + +namespace Kratos { + +static void BM_ModelPartCreateNewNode(benchmark::State& state) { + for (auto _ : state) { + const IndexType input_size = state.range(0); + + state.PauseTiming(); + + auto model = Model(); + auto& r_model_part = model.CreateModelPart("test"); + + state.ResumeTiming(); + + for (IndexType i = 0; i < input_size; ++i) { + r_model_part.CreateNewNode(i + 1, 0.0, 0.0, 0.0); + } + } +} + + +static void BM_ModelPartAddNodesToRootFromRange1(benchmark::State& state) { + for (auto _ : state) { + const IndexType input_size = state.range(0); + const bool fill_existing = state.range(1) == 1; + + state.PauseTiming(); + + auto model = Model(); + auto& r_model_part = model.CreateModelPart("test"); + + std::vector nodes_to_be_added, existing_nodes; + if (!fill_existing) { + nodes_to_be_added.reserve(input_size); + } else { + nodes_to_be_added.reserve(input_size / 2 + 1); + existing_nodes.reserve(input_size / 2 + 1); + } + + // doing it in reverse to make sure a proper sort is done always + for (IndexType i = input_size; i > 0; --i) { + auto p_node = Kratos::make_intrusive(i, 0.0, 0.0, 0.0); + if (!fill_existing || i % 2 == 0) { + nodes_to_be_added.push_back(p_node); + } else { + existing_nodes.push_back(p_node); + } + } + r_model_part.AddNodes(existing_nodes.begin(), existing_nodes.end()); + state.ResumeTiming(); + + // benchmarking to add nodes for already existing model part with nodes + r_model_part.AddNodes(nodes_to_be_added.begin(), nodes_to_be_added.end()); + } +} + +static void BM_ModelPartAddNodesToRootFromRange2(benchmark::State& state) { + for (auto _ : state) { + const IndexType input_size = state.range(0); + const bool fill_existing = state.range(1) == 1; + + state.PauseTiming(); + + auto model = Model(); + auto& r_model_part = model.CreateModelPart("test"); + + PointerVectorSet nodes_to_be_added, existing_nodes; + if (!fill_existing) { + nodes_to_be_added.reserve(input_size); + } else { + nodes_to_be_added.reserve(input_size / 2 + 1); + existing_nodes.reserve(input_size / 2 + 1); + } + + // doing it in reverse to make sure a proper sort is done always + for (IndexType i = 0; i < input_size; ++i) { + auto p_node = Kratos::make_intrusive(i + 1, 0.0, 0.0, 0.0); + if (!fill_existing || i % 2 == 0) { + nodes_to_be_added.insert(nodes_to_be_added.end(), p_node); + } else { + existing_nodes.insert(existing_nodes.end(), p_node); + } + } + r_model_part.AddNodes(existing_nodes.begin(), existing_nodes.end()); + state.ResumeTiming(); + + // benchmarking to add nodes for already existing model part with nodes + r_model_part.AddNodes(nodes_to_be_added.begin(), nodes_to_be_added.end()); + } +} + +static void BM_ModelPartAddNodesToSubSubFromRange1(benchmark::State& state) { + for (auto _ : state) { + const IndexType input_size = state.range(0); + const bool fill_existing = state.range(1) == 1; + + state.PauseTiming(); + + auto model = Model(); + auto& r_sub_sub_model_part = model.CreateModelPart("test").CreateSubModelPart("sub_test").CreateSubModelPart("sub_sub_test"); + + std::vector nodes_to_be_added, existing_nodes; + if (!fill_existing) { + nodes_to_be_added.reserve(input_size); + } else { + nodes_to_be_added.reserve(input_size / 2 + 1); + existing_nodes.reserve(input_size / 2 + 1); + } + + // doing it in reverse to make sure a proper sort is done always + for (IndexType i = 0; i < input_size; ++i) { + auto p_node = Kratos::make_intrusive(i + 1, 0.0, 0.0, 0.0); + if (!fill_existing || i % 2 == 0) { + nodes_to_be_added.insert(nodes_to_be_added.end(), p_node); + } else { + existing_nodes.insert(existing_nodes.end(), p_node); + } + } + r_sub_sub_model_part.AddNodes(existing_nodes.begin(), existing_nodes.end()); + state.ResumeTiming(); + + r_sub_sub_model_part.AddNodes(nodes_to_be_added.begin(), nodes_to_be_added.end()); + } +} + +static void BM_ModelPartAddNodesToSubSubFromRange2(benchmark::State& state) { + for (auto _ : state) { + const IndexType input_size = state.range(0); + const bool fill_existing = state.range(1) == 1; + + state.PauseTiming(); + + auto model = Model(); + auto& r_sub_sub_model_part = model.CreateModelPart("test").CreateSubModelPart("sub_test").CreateSubModelPart("sub_sub_test"); + + std::vector nodes_to_be_added, existing_nodes; + if (!fill_existing) { + nodes_to_be_added.reserve(input_size); + } else { + nodes_to_be_added.reserve(input_size / 2 + 1); + existing_nodes.reserve(input_size / 2 + 1); + } + + // doing it in reverse to make sure a proper sort is done always + for (IndexType i = 0; i < input_size; ++i) { + auto p_node = Kratos::make_intrusive(i + 1, 0.0, 0.0, 0.0); + if (!fill_existing || i % 2 == 0) { + nodes_to_be_added.insert(nodes_to_be_added.end(), p_node); + } else { + existing_nodes.insert(existing_nodes.end(), p_node); + } + } + r_sub_sub_model_part.GetRootModelPart().AddNodes(nodes_to_be_added.begin(), nodes_to_be_added.end()); + r_sub_sub_model_part.GetRootModelPart().AddNodes(existing_nodes.begin(), existing_nodes.end()); + state.ResumeTiming(); + + r_sub_sub_model_part.AddNodes(nodes_to_be_added.begin(), nodes_to_be_added.end()); + } +} + +static void BM_ModelPartAddNodesToSubSubFromRange3(benchmark::State& state) { + for (auto _ : state) { + const IndexType input_size = state.range(0); + const bool fill_existing = state.range(1) == 1; + + state.PauseTiming(); + + auto model = Model(); + auto& r_sub_sub_model_part = model.CreateModelPart("test").CreateSubModelPart("sub_test").CreateSubModelPart("sub_sub_test"); + + std::vector nodes_to_be_added, existing_nodes; + if (!fill_existing) { + nodes_to_be_added.reserve(input_size); + } else { + nodes_to_be_added.reserve(input_size / 2 + 1); + existing_nodes.reserve(input_size / 2 + 1); + } + + // doing it in reverse to make sure a proper sort is done always + for (IndexType i = 0; i < input_size; ++i) { + auto p_node = Kratos::make_intrusive(i + 1, 0.0, 0.0, 0.0); + if (!fill_existing || i % 2 == 0) { + nodes_to_be_added.insert(nodes_to_be_added.end(), p_node); + } else { + existing_nodes.insert(existing_nodes.end(), p_node); + } + } + r_sub_sub_model_part.GetRootModelPart().AddNodes(nodes_to_be_added.begin(), nodes_to_be_added.end()); + r_sub_sub_model_part.GetRootModelPart().AddNodes(existing_nodes.begin(), existing_nodes.end()); + state.ResumeTiming(); + + r_sub_sub_model_part.AddNodes(r_sub_sub_model_part.GetRootModelPart().NodesBegin(), r_sub_sub_model_part.GetRootModelPart().NodesEnd()); + } +} + +static void BM_ModelPartAddNodesToSubSubFromId1(benchmark::State& state) { + for (auto _ : state) { + const IndexType input_size = state.range(0); + const bool fill_existing = state.range(1) == 1; + + state.PauseTiming(); + + auto model = Model(); + auto& r_root_model_part = model.CreateModelPart("test"); + auto& r_sub_sub_model_part = r_root_model_part.CreateSubModelPart("sub_test").CreateSubModelPart("sub_sub_test"); + + PointerVectorSet nodes; + std::vector node_ids_to_add, existing_node_ids; + if (!fill_existing) { + node_ids_to_add.reserve(input_size); + } else { + node_ids_to_add.reserve(input_size / 2 + 1); + existing_node_ids.reserve(input_size / 2 + 1); + } + + for (IndexType i = 0; i < input_size; ++i) { + nodes.insert(nodes.end(), Kratos::make_intrusive(i + 1, 0.0, 0.0, 0.0)); + } + + // doing it in reverse to make sure a proper sort is done always + for (IndexType i = input_size; i > 0; --i) { + if (!fill_existing || i % 2 == 0) { + node_ids_to_add.push_back(i); + } else { + existing_node_ids.push_back(i); + } + } + r_root_model_part.AddNodes(nodes.begin(), nodes.end()); + r_sub_sub_model_part.AddNodes(existing_node_ids); + state.ResumeTiming(); + + r_sub_sub_model_part.AddNodes(node_ids_to_add); + } +} + + +// Register the function as a benchmark +BENCHMARK(BM_ModelPartCreateNewNode) -> RangeMultiplier(10) -> Range(1e2, 1e+6); +BENCHMARK(BM_ModelPartAddNodesToRootFromRange1) -> ArgsProduct({{100, 1000, 10000, 100000, 1000000}, {0, 1}}); +BENCHMARK(BM_ModelPartAddNodesToRootFromRange2) -> ArgsProduct({{100, 1000, 10000, 100000, 1000000}, {0, 1}}); +BENCHMARK(BM_ModelPartAddNodesToSubSubFromRange1) -> ArgsProduct({{100, 1000, 10000, 100000, 1000000}, {0, 1}}); +BENCHMARK(BM_ModelPartAddNodesToSubSubFromRange2) -> ArgsProduct({{100, 1000, 10000, 100000, 1000000}, {0, 1}}); +BENCHMARK(BM_ModelPartAddNodesToSubSubFromRange3) -> ArgsProduct({{100, 1000, 10000, 100000, 1000000}, {0, 1}}); +BENCHMARK(BM_ModelPartAddNodesToSubSubFromId1) -> ArgsProduct({{100, 1000, 10000, 100000, 1000000}, {0, 1}}); + +} // namespace Kratos + +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/kratos/containers/pointer_vector_set.h b/kratos/containers/pointer_vector_set.h index 7eccc0b0fc78..c3962b970766 100644 --- a/kratos/containers/pointer_vector_set.h +++ b/kratos/containers/pointer_vector_set.h @@ -20,6 +20,7 @@ #include #include #include +#include // External includes #include @@ -28,6 +29,7 @@ #include "includes/define.h" #include "includes/serializer.h" #include "containers/key_generator.h" +#include "utilities/type_traits.h" namespace Kratos { @@ -647,10 +649,45 @@ class PointerVectorSet final template void insert(InputIterator first, InputIterator last) { - // first sorts the input iterators and make the input unique. - std::sort(first, last, CompareKey()); - auto new_last = std::unique(first, last, EqualKeyTo()); - SortedInsert(first, new_last); + // We copy always the input range to a temp not to have the input range mutated. + std::vector temp; + temp.reserve(std::distance(first, last)); + for (auto it = first; it != last; ++it) { + temp.push_back(GetPointer(it)); + } + std::sort(temp.begin(), temp.end(), CompareKey()); + auto new_last = std::unique(temp.begin(), temp.end(), EqualKeyTo()); + SortedInsert(temp.begin(), new_last); + + // KRATOS_ERROR << "HELLO THERE"; + } + + + /** + * @brief Inserts elements from a r_value container. + * @details This method is used to insert all values from an r valued container. + * This mutates the r valued input container to be sorted and unique if it is not the type of PointerVectorSet. + * If it is the type of the PointerVectorSet, then it doesn't mutate the input container. + * + * @warning The move assumes the ownership of the container, hence the the rContainer may be mutated. if the TContainer is of type std::shared_ptr or intrusive_ptr + * then, there will always be a null pointer at the end past position of the unique sorted list since the std::unique uses the move assignment operator + * within its algorithm. Therefore, the new_last created here will have the correct pointer to the object, and the rContainer will have a nullptr in the + * corresponding place. Therefore, if this method is called once, and if it used with a container type which is not PointerVectorSet, please do not use + * the input rContainer anymore. It will segfault unless the PointerVectorSet is created with raw pointers. [This is because since c++11 std::unique uses the move assignment operator] + * @tparam TContainer + * @param rContainer + */ + template::value, bool> = true> + void insert(TContainer&& rContainer) + { + if constexpr(!std::is_same_v, PointerVectorSet>) { + std::sort(rContainer.begin(), rContainer.end(), CompareKey()); + auto new_last = std::unique(rContainer.begin(), rContainer.end(), EqualKeyTo()); + SortedInsert(rContainer.begin(), new_last); + } else { + SortedInsert(rContainer.begin(), rContainer.end()); + } + } /** @@ -667,6 +704,20 @@ class PointerVectorSet final SortedInsert(first, last); } + /** + * @brief Insert elements from another PointerVectorSet range. + * @details This function inserts element pointers from another PointerVectorSet range specified by first and last into the current set. + * Since, PointerVectorSet is assumed to be sorted and unique, the incoming PointerVectorSet is not + * sorted and made unique again. This will not insert any elements in the incoming set, if there exists an element with a key + * which is equal to an element's key in the input range. + * @param first Other PointerVectorSet starting iterator + * @param last Other PointerVectorSet ending iterator + */ + void insert(PointerVectorSet::iterator first, PointerVectorSet::iterator last) + { + SortedInsert(first, last); + } + void insert(const PointerVectorSet& rOther) { insert(rOther.begin(), rOther.end()); @@ -809,6 +860,11 @@ class PointerVectorSet final return mData.capacity(); } + void shrink_to_fit() + { + mData.shrink_to_fit(); + } + /** * @brief Sort the elements in the set. * @details This function sorts the elements in the set using the CompareKey comparison function. After sorting, @@ -1199,7 +1255,10 @@ class PointerVectorSet final // which is harder to guess, and cryptic. Hence, using the decltype. using iterator_value_type = std::decay_t; - if constexpr(std::is_same_v>) { + if constexpr(std::is_same_v || std::is_same_v) { + // if the TIteratorType is of boost::indirect_iterator type, then we can get the pointer by dereferencing. + return *(Iterator.base()); + } else if constexpr(std::is_same_v>) { // this supports any type of pointers return *Iterator; } else if constexpr(std::is_same_v> && std::is_same_v>>) { diff --git a/kratos/includes/model_part.h b/kratos/includes/model_part.h index b1b38b85efb2..3c6b92e18f6e 100644 --- a/kratos/includes/model_part.h +++ b/kratos/includes/model_part.h @@ -18,11 +18,13 @@ #include #include #include +#include // External includes // Project includes #include "includes/define.h" +#include "includes/key_hash.h" #include "includes/serializer.h" #include "includes/process_info.h" #include "containers/data_value_container.h" @@ -39,6 +41,8 @@ #include "includes/master_slave_constraint.h" #include "containers/variable.h" #include "containers/variable_data.h" +#include "utilities/parallel_utilities.h" +#include "utilities/type_traits.h" namespace Kratos { @@ -95,6 +99,15 @@ class KRATOS_API(KRATOS_CORE) ModelPart final Kratos_Ownership_Size }; + ///@} + ///@name Class definitions + ///@{ + + /// @brief Templated class to get corresponding container and its information from a mesh. + /// @tparam TContainerType The type of the container to be retrieved. + template + struct Container {}; + ///@} ///@name Type Definitions ///@{ @@ -356,47 +369,29 @@ class KRATOS_API(KRATOS_CORE) ModelPart final template void AddNodes(TIteratorType nodes_begin, TIteratorType nodes_end, IndexType ThisIndex = 0) { - KRATOS_TRY - ModelPart::NodesContainerType aux; - ModelPart::NodesContainerType aux_root; //they may not exist in the root - ModelPart* root_model_part = &this->GetRootModelPart(); + EntityRangeChecker()(this, nodes_begin, nodes_end); - for(TIteratorType it = nodes_begin; it!=nodes_end; it++) - { - auto it_found = root_model_part->Nodes().find(it->Id()); - if(it_found == root_model_part->NodesEnd()) //node does not exist in the top model part - { - aux_root.push_back( *(it.base()) ); //node does not exist - aux.push_back( *(it.base()) ); - } - else //if it does exist verify it is the same node - { - if(&(*it_found) != &(*it))//check if the pointee coincides - KRATOS_ERROR << "attempting to add a new node with Id :" << it_found->Id() << ", unfortunately a (different) node with the same Id already exists" << std::endl; - else - aux.push_back( *(it.base()) ); - } - } - - //now add to the root model part - for(auto it = aux_root.begin(); it!=aux_root.end(); it++) - root_model_part->Nodes().push_back( *(it.base()) ); - root_model_part->Nodes().Unique(); - - //add to all of the leaves - - ModelPart* current_part = this; - while(current_part->IsSubModelPart()) - { - for(auto it = aux.begin(); it!=aux.end(); it++) - current_part->Nodes().push_back( *(it.base()) ); + InsertEntityRange([](ModelPart* pModelPart) { + return &(pModelPart->GetMesh().Nodes()); + }, nodes_begin, nodes_end); + } - current_part->Nodes().Unique(); + template::value, bool> = true> + void AddNodes(TContainer&& rInputContainer) + { + // This check is required in cases where std::vector is passed as an r value, then it will + // not call the correct addition with the input args of const std::vector&, it will get in to this method. + // this check will redirect it to the std::vector method with the rContainer. + if constexpr(!std::is_same_v, std::vector>) { + EntityRangeChecker()(this, rInputContainer.begin(), rInputContainer.end()); - current_part = &(current_part->GetParentModelPart()); + InsertEntities([](ModelPart* pModelPart) { + return &(pModelPart->GetMesh().Nodes()); + }, std::move(rInputContainer)); + } else { + // call the vector of int insertion. + AddNodes(rInputContainer); } - - KRATOS_CATCH("") } /** Inserts a node in the current mesh. @@ -694,46 +689,29 @@ class KRATOS_API(KRATOS_CORE) ModelPart final template void AddMasterSlaveConstraints(TIteratorType constraints_begin, TIteratorType constraints_end, IndexType ThisIndex = 0) { - KRATOS_TRY - ModelPart::MasterSlaveConstraintContainerType aux; - ModelPart::MasterSlaveConstraintContainerType aux_root; - ModelPart* root_model_part = &this->GetRootModelPart(); - - for(TIteratorType it = constraints_begin; it!=constraints_end; it++) - { - auto it_found = root_model_part->MasterSlaveConstraints().find(it->Id()); - if(it_found == root_model_part->MasterSlaveConstraintsEnd()) //node does not exist in the top model part - { - aux_root.push_back( *(it.base()) ); - aux.push_back( *(it.base()) ); - } - else //if it does exist verify it is the same node - { - if(&(*it_found) != &(*it))//check if the pointee coincides - KRATOS_ERROR << "attempting to add a new master-slave constraint with Id :" << it_found->Id() << ", unfortunately a (different) master-slave constraint with the same Id already exists" << std::endl; - else - aux.push_back( *(it.base()) ); - } - } - - for(auto it = aux_root.begin(); it!=aux_root.end(); it++) - root_model_part->MasterSlaveConstraints().push_back( *(it.base()) ); - root_model_part->MasterSlaveConstraints().Unique(); - - //add to all of the leaves + EntityRangeChecker()(this, constraints_begin, constraints_end); - ModelPart* current_part = this; - while(current_part->IsSubModelPart()) - { - for(auto it = aux.begin(); it!=aux.end(); it++) - current_part->MasterSlaveConstraints().push_back( *(it.base()) ); + InsertEntityRange([](ModelPart* pModelPart) { + return &(pModelPart->GetMesh().MasterSlaveConstraints()); + }, constraints_begin, constraints_end); + } - current_part->MasterSlaveConstraints().Unique(); + template::value, bool> = true> + void AddMasterSlaveConstraints(TContainer&& rInputContainer) + { + // This check is required in cases where std::vector is passed as an r value, then it will + // not call the correct addition with the input args of const std::vector&, it will get in to this method. + // this check will redirect it to the std::vector method with the rContainer. + if constexpr(!std::is_same_v, std::vector>) { + EntityRangeChecker()(this, rInputContainer.begin(), rInputContainer.end()); - current_part = &(current_part->GetParentModelPart()); + InsertEntities([](ModelPart* pModelPart) { + return &(pModelPart->GetMesh().MasterSlaveConstraints()); + }, std::move(rInputContainer)); + } else { + // call the vector of int insertion. + AddMasterSlaveConstraints(rInputContainer); } - - KRATOS_CATCH("") } /** @@ -1042,46 +1020,29 @@ class KRATOS_API(KRATOS_CORE) ModelPart final template void AddElements(TIteratorType elements_begin, TIteratorType elements_end, IndexType ThisIndex = 0) { - KRATOS_TRY - ModelPart::ElementsContainerType aux; - ModelPart::ElementsContainerType aux_root; - ModelPart* root_model_part = &this->GetRootModelPart(); + EntityRangeChecker()(this, elements_begin, elements_end); - for(TIteratorType it = elements_begin; it!=elements_end; it++) - { - auto it_found = root_model_part->Elements().find(it->Id()); - if(it_found == root_model_part->ElementsEnd()) //node does not exist in the top model part - { - aux_root.push_back( *(it.base()) ); - aux.push_back( *(it.base()) ); - } - else //if it does exist verify it is the same node - { - if(&(*it_found) != &(*it))//check if the pointee coincides - KRATOS_ERROR << "attempting to add a new element with Id :" << it_found->Id() << ", unfortunately a (different) element with the same Id already exists" << std::endl; - else - aux.push_back( *(it.base()) ); - } - } - - for(auto it = aux_root.begin(); it!=aux_root.end(); it++) - root_model_part->Elements().push_back( *(it.base()) ); - root_model_part->Elements().Unique(); - - //add to all of the leaves - - ModelPart* current_part = this; - while(current_part->IsSubModelPart()) - { - for(auto it = aux.begin(); it!=aux.end(); it++) - current_part->Elements().push_back( *(it.base()) ); + InsertEntityRange([](ModelPart* pModelPart) { + return &(pModelPart->GetMesh().Elements()); + }, elements_begin, elements_end); + } - current_part->Elements().Unique(); + template::value, bool> = true> + void AddElements(TContainer&& rInputContainer) + { + // This check is required in cases where std::vector is passed as an r value, then it will + // not call the correct addition with the input args of const std::vector&, it will get in to this method. + // this check will redirect it to the std::vector method with the rContainer. + if constexpr(!std::is_same_v, std::vector>) { + EntityRangeChecker()(this, rInputContainer.begin(), rInputContainer.end()); - current_part = &(current_part->GetParentModelPart()); + InsertEntities([](ModelPart* pModelPart) { + return &(pModelPart->GetMesh().Elements()); + }, std::move(rInputContainer)); + } else { + // call the vector of int insertion. + AddElements(rInputContainer); } - - KRATOS_CATCH("") } /// Creates new element with a node ids list. @@ -1228,52 +1189,34 @@ class KRATOS_API(KRATOS_CORE) ModelPart final */ void AddConditions(std::vector const& ConditionIds, IndexType ThisIndex = 0); - /** Inserts a list of pointers to nodes + /** Inserts a list of pointers to conditions */ template void AddConditions(TIteratorType conditions_begin, TIteratorType conditions_end, IndexType ThisIndex = 0) { - KRATOS_TRY - ModelPart::ConditionsContainerType aux; - ModelPart::ConditionsContainerType aux_root; - ModelPart* root_model_part = &this->GetRootModelPart(); - - for(TIteratorType it = conditions_begin; it!=conditions_end; it++) - { - auto it_found = root_model_part->Conditions().find(it->Id()); - if(it_found == root_model_part->ConditionsEnd()) //node does not exist in the top model part - { - aux.push_back( *(it.base()) ); - aux_root.push_back( *(it.base()) ); - } - else //if it does exist verify it is the same node - { - if(&(*it_found) != &(*it))//check if the pointee coincides - KRATOS_ERROR << "attempting to add a new Condition with Id :" << it_found->Id() << ", unfortunately a (different) Condition with the same Id already exists" << std::endl; - else - aux.push_back( *(it.base()) ); - } - } - - //now add to the root model part - for(auto it = aux_root.begin(); it!=aux_root.end(); it++) - root_model_part->Conditions().push_back( *(it.base()) ); - root_model_part->Conditions().Unique(); + EntityRangeChecker()(this, conditions_begin, conditions_end); - //add to all of the leaves - - ModelPart* current_part = this; - while(current_part->IsSubModelPart()) - { - for(auto it = aux.begin(); it!=aux.end(); it++) - current_part->Conditions().push_back( *(it.base()) ); + InsertEntityRange([](ModelPart* pModelPart) { + return &(pModelPart->GetMesh().Conditions()); + }, conditions_begin, conditions_end); + } - current_part->Conditions().Unique(); + template::value, bool> = true> + void AddConditions(TContainer&& rInputContainer) + { + // This check is required in cases where std::vector is passed as an r value, then it will + // not call the correct addition with the input args of const std::vector&, it will get in to this method. + // this check will redirect it to the std::vector method with the rContainer. + if constexpr(!std::is_same_v, std::vector>) { + EntityRangeChecker()(this, rInputContainer.begin(), rInputContainer.end()); - current_part = &(current_part->GetParentModelPart()); + InsertEntities([](ModelPart* pModelPart) { + return &(pModelPart->GetMesh().Conditions()); + }, std::move(rInputContainer)); + } else { + // call the vector of int insertion. + AddConditions(rInputContainer); } - - KRATOS_CATCH("") } /// Creates new condition with a node ids list. @@ -1525,43 +1468,69 @@ class KRATOS_API(KRATOS_CORE) ModelPart final void AddGeometries(TIteratorType GeometryBegin, TIteratorType GeometriesEnd, IndexType ThisIndex = 0) { KRATOS_TRY - std::vector aux, aux_root; + ModelPart* p_root_model_part = &this->GetRootModelPart(); - for(TIteratorType it = GeometryBegin; it!=GeometriesEnd; it++) { - auto it_found = p_root_model_part->Geometries().find(it->Id()); - if(it_found == p_root_model_part->GeometriesEnd()) { // Geometry does not exist in the top model part - aux_root.push_back(*(it.base())); - aux.push_back(*(it.base())); - } else { // If it does exist verify it is the same geometry - if (GeometryType::HasSameGeometryType(*it, *it_found)) { // Check the geometry type and connectivities - for (IndexType i_pt = 0; i_pt < it->PointsNumber(); ++i_pt) { - KRATOS_ERROR_IF((*it)[i_pt].Id() != (*it_found)[i_pt].Id()) << "Attempting to add a new geometry with Id: " << it->Id() << ". A same type geometry with same Id but different connectivities already exists." << std::endl; + block_for_each(GeometryBegin, GeometriesEnd, [p_root_model_part](const auto& prGeometry) { + const auto& r_geometry = ReferenceGetter::Get(prGeometry); + const auto& r_geometries = p_root_model_part->Geometries(); + auto it_found = r_geometries.find(r_geometry.Id()); + if (it_found != p_root_model_part->GeometriesEnd()) { + if (GeometryType::HasSameGeometryType(r_geometry, *it_found)) { // Check the geometry type and connectivities + for (IndexType i_pt = 0; i_pt < r_geometry.PointsNumber(); ++i_pt) { + KRATOS_ERROR_IF((r_geometry)[i_pt].Id() != (*it_found)[i_pt].Id()) << "Attempting to add a new geometry with Id: " << r_geometry.Id() << ". A same type geometry with same Id but different connectivities already exists." << std::endl; } - aux.push_back(*(it_found.base())); // If the Id, type and connectivities are the same add the existing geometry - } else if(&(*it_found) != &(*it)) { // Check if the pointee coincides + } else if(&(*it_found) != &r_geometry) { // Check if the pointee coincides KRATOS_ERROR << "Attempting to add a new geometry with Id: " << it_found->Id() << ". A different geometry with the same Id already exists." << std::endl; - } else { - aux.push_back(*(it.base())); } } - } + }); - // Add to root model part - for(auto& p_geom : aux_root) { - p_root_model_part->AddGeometry(p_geom); - } + InsertEntityRange([](ModelPart* pModelPart) { + return &(pModelPart->Geometries()); + }, GeometryBegin, GeometriesEnd); + + KRATOS_CATCH("") + } - // Add to all of the leaves - ModelPart* p_current_part = this; - while(p_current_part->IsSubModelPart()) { - for(auto& p_geom : aux) { - p_current_part->AddGeometry(p_geom); - } - p_current_part = &(p_current_part->GetParentModelPart()); + /// Inserts a list of geometries to a submodelpart provided their iterators + template::value, bool> = true> + void AddGeometries(TContainer&& rInputContainer) + { + KRATOS_TRY + + // This check is required in cases where std::vector is passed as an r value, then it will + // not call the correct addition with the input args of const std::vector&, it will get in to this method. + // this check will redirect it to the std::vector method with the rContainer. + if constexpr(!std::is_same_v, std::vector>) { + ModelPart* p_root_model_part = &this->GetRootModelPart(); + + block_for_each(rInputContainer.begin(), rInputContainer.end(), [p_root_model_part](const auto& prGeometry) { + const auto& r_geometry = ReferenceGetter::Get(prGeometry); + const auto& r_geometries = p_root_model_part->Geometries(); + auto it_found = r_geometries.find(r_geometry.Id()); + if (it_found != p_root_model_part->GeometriesEnd()) { + if (GeometryType::HasSameGeometryType(r_geometry, *it_found)) { // Check the geometry type and connectivities + for (IndexType i_pt = 0; i_pt < r_geometry.PointsNumber(); ++i_pt) { + KRATOS_ERROR_IF((r_geometry)[i_pt].Id() != (*it_found)[i_pt].Id()) << "Attempting to add a new geometry with Id: " << r_geometry.Id() << ". A same type geometry with same Id but different connectivities already exists." << std::endl; + } + } else if(&(*it_found) != &r_geometry) { // Check if the pointee coincides + KRATOS_ERROR << "Attempting to add a new geometry with Id: " << it_found->Id() << ". A different geometry with the same Id already exists." << std::endl; + } + } + }); + + InsertEntities([](ModelPart* pModelPart) { + return &(pModelPart->Geometries()); + }, std::move(rInputContainer)); + } else { + // call the vector of int insertion. + AddGeometries(rInputContainer); } + + KRATOS_CATCH("") } @@ -1988,6 +1957,193 @@ class KRATOS_API(KRATOS_CORE) ModelPart final ///@name Private Operations ///@{ + /** + * @brief Provides a mechanism to get the reference from given input with some types. + * @details The range addition methods in the model parts are used with following types: + * - std::vector::iterator -> dereference will give the Entity::Pointer + * - PointerVectorSet::iterator -> dereference will give the Entity + * - PointerVectorSet::ptr_iterator -> dereference will give the Entity::Pointer. + * This functor generalizes and gives the reference from a given entity. + * + * @tparam TReturnValueType + */ + template + struct ReferenceGetter + { + template + inline static const TReturnValueType& Get(const TInputValueType& rInputValue) { + if constexpr(std::is_same_v>) { + return rInputValue; + } else if constexpr(std::is_same_v>>) { + return *rInputValue; + } else if constexpr(std::is_same_v>>) { + return **rInputValue; + } else { + static_assert(!std::is_same_v, "Unsupported value type."); + return TReturnValueType{}; + } + } + }; + + /** + * @brief Generalized checker to check whether if an item being inserted is already there, if so whether the item is the same. + * + * @tparam TContainerType + */ + template + struct EntityRangeChecker{ + template + void operator()(ModelPart* pModelPart, TIterator begin, TIterator end) { + KRATOS_TRY + + ModelPart* p_root_model_part = &pModelPart->GetRootModelPart(); + + block_for_each(begin, end, [&](const auto& prEntity) { + const auto& r_entity = ReferenceGetter::Get(prEntity); + const auto& r_entities = Container::GetContainer(p_root_model_part->GetMesh()); // TODO: This is only required to only trigger a find, not a sort. Once the find is fixed, then we can simplify this. + auto it_found = r_entities.find(r_entity.Id()); + KRATOS_ERROR_IF(it_found != r_entities.end() && &r_entity != &*it_found) + << "attempting to add a new " << Container::GetEntityName() << " with Id :" + << r_entity.Id() << " to root model part " << p_root_model_part->FullName() + << ", unfortunately a (different) " << Container::GetEntityName() + << " with the same Id already exists. [ Occurred while adding " << Container::GetEntityName() + << " to " << pModelPart->FullName() << " ]." + << std::endl; + }); + KRATOS_CATCH(""); + } + }; + + template + static bool IsSubSet( + const TContainerType& rContainer, + typename TContainerType::iterator begin, + typename TContainerType::iterator end) + { + // do nothing if the given range is empty. + if (std::distance(begin, end) == 0) { + return true; + } + + // check if the given [begin, end) range is a subset of the given PointerVectorSet. + auto current_pvs_begin_it = rContainer.find(begin->Id()); + if (current_pvs_begin_it != rContainer.end() && &*(current_pvs_begin_it.base()) == &*(begin.base())) { + // memory location pointing to begin is in the rContainer. then check the same for the end. + auto current_pvs_end_it = rContainer.find((end - 1)->Id()); + return current_pvs_end_it != rContainer.end() && &*(current_pvs_end_it.base()) == &*((end - 1).base()); + } + + return false; + } + + /** + * @brief Generalized method to add entities to the current model part and all the parent model parts. + * @details Following cases are considered + * 1. TIterator not being the type of the PointerVectorSet::iterator + * 2. TIterator being the type of PointerVectorSet::iterator + * a. [begin, end) defines a subrange in the considered PointerVectorSet. + * b. [begin, end) does not define a subrange in the considered PointerVectorSet. + * + * - In the case 1: + * - We have to make sure that the range [begin, end) is not always sorted and made unique when inserted to + * current model part and the parent model part. This requires constructing an auxiliary TContainerType container + * to add the items in the range. + * -- This will keep on increasing the capacity of the current PVS as well as all the + * parent PVS containers. + * + * - In the case 2a: + * - If it defines a subrange, we don't have to do anything. We can simply skip the insertion on the current PVS as + * well as in all the parents because they must already have the subrange. + * -- This will keep the same capacity of the PVS. + * + * - In the case 2b: + * - If does not define a subrange and it is of type PVS, hence they are sorted and made unique. So we don't need + * an auxiliary here as well. + * -- This will keep on increasing the capacity of the current PVS as well as all the + * parent PVS containers. + * + * @tparam TContainerGetter + * @tparam TIterator + * @param rContainerGetter + * @param begin + * @param end + */ + template + void InsertEntityRange( + const TContainerGetter& rContainerGetter, + TIterator begin, + TIterator end) + { + KRATOS_TRY + + using container_type = std::remove_pointer_t()(std::declval()))>; + + ModelPart* p_current_part = this; + + if constexpr(!std::is_same_v> && + !std::is_same_v>) { + // this represents the case 1. + container_type aux; + aux.insert(begin, end); + + // we add to all the parents and the current model part + while (p_current_part->IsSubModelPart()) { + rContainerGetter(p_current_part)->insert(aux.begin(), aux.end()); + p_current_part = &(p_current_part->GetParentModelPart()); + } + + // now we add to the root model part + rContainerGetter(p_current_part)->insert(aux.begin(), aux.end()); + } else { + // this represents the case 2. + // now the iterators belong to PVS::iterator, hence no sorting or making them to unique will occur. + bool is_sub_set = IsSubSet(*rContainerGetter(p_current_part), begin, end); + while (p_current_part->IsSubModelPart() && !is_sub_set) { + rContainerGetter(p_current_part)->insert(begin, end); + p_current_part = &(p_current_part->GetParentModelPart()); + is_sub_set = IsSubSet(*rContainerGetter(p_current_part), begin, end); + } + + // now we do the same for the root model part + if (!is_sub_set) { + rContainerGetter(p_current_part)->insert(begin, end); + } + } + + KRATOS_CATCH("") + } + + template::value, bool> = true> + void InsertEntities( + const TContainerGetter& rContainerGetter, + TInputContainer&& rInputContainer) + { + KRATOS_TRY + + using container_type = std::remove_pointer_t()(std::declval()))>; + + ModelPart* p_current_part = this; + + if constexpr(!std::is_same_v, container_type>) { + // this represents the case 1. + container_type aux; + aux.insert(std::move(rInputContainer)); + + // we add to all the parents and the current model part + while (p_current_part->IsSubModelPart()) { + rContainerGetter(p_current_part)->insert(aux.begin(), aux.end()); + p_current_part = &(p_current_part->GetParentModelPart()); + } + + // now we add to the root model part + rContainerGetter(p_current_part)->insert(aux.begin(), aux.end()); + } else { + InsertEntityRange(rContainerGetter, rInputContainer.begin(), rInputContainer.end()); + } + + KRATOS_CATCH("") + } + /** * @brief This method trims a string in the different components to access recursively to any subproperty * @param rStringName The given name to be trimmed @@ -2068,6 +2224,31 @@ class KRATOS_API(KRATOS_CORE) ModelPart final ///@name Type Definitions ///@{ +template <> struct ModelPart::Container { + static std::string GetEntityName() { return "node"; } + static NodesContainerType& GetContainer(ModelPart::MeshType& rMesh) { return rMesh.Nodes(); } +}; + +template <> struct ModelPart::Container { + static std::string GetEntityName() { return "condition"; } + static ConditionsContainerType& GetContainer(ModelPart::MeshType& rMesh) { return rMesh.Conditions(); } +}; + +template <> struct ModelPart::Container { + static std::string GetEntityName() { return "element"; } + static ElementsContainerType& GetContainer(ModelPart::MeshType& rMesh) { return rMesh.Elements(); } +}; + +template <> struct ModelPart::Container { + static std::string GetEntityName() { return "master-slave-constraint"; } + static MasterSlaveConstraintContainerType& GetContainer(ModelPart::MeshType& rMesh) { return rMesh.MasterSlaveConstraints(); } +}; + +template <> struct ModelPart::Container { + static std::string GetEntityName() { return "geometry"; } + // TODO: Can be used once we move the geometries container to meshes. + // static GeometriesMapType& GetContainer(ModelPart::MeshType& rMesh) { return rMesh.Geometries(); } +}; ///@} ///@name Input and output diff --git a/kratos/modeler/create_entities_from_geometries_modeler.cpp b/kratos/modeler/create_entities_from_geometries_modeler.cpp index 4326f6dd1434..bd0922128d67 100644 --- a/kratos/modeler/create_entities_from_geometries_modeler.cpp +++ b/kratos/modeler/create_entities_from_geometries_modeler.cpp @@ -36,7 +36,7 @@ void CreateEntitiesFromGeometries( ModelPart& rModelPart) { // Create the entities container and allocate space - TEntitiesContainerType entities_to_add; + std::vector entities_to_add; entities_to_add.reserve(rModelPart.NumberOfGeometries()); // Get current max element id diff --git a/kratos/sources/model_part.cpp b/kratos/sources/model_part.cpp index db3f4db5e0bc..9ea3d91daf51 100644 --- a/kratos/sources/model_part.cpp +++ b/kratos/sources/model_part.cpp @@ -13,6 +13,7 @@ // System includes #include +#include // External includes @@ -20,7 +21,7 @@ #include "includes/define.h" #include "includes/model_part.h" #include "includes/exception.h" -#include "utilities/parallel_utilities.h" +#include "utilities/reduction_utilities.h" namespace Kratos { @@ -28,6 +29,91 @@ namespace Kratos KRATOS_CREATE_LOCAL_FLAG(ModelPart, ALL_ENTITIES, 0); KRATOS_CREATE_LOCAL_FLAG(ModelPart, OVERWRITE_ENTITIES, 1); +namespace ModelPartHelperUtilities +{ + +template +void AddEntitiesFromIds( + TContainerGetter&& rContainerGetter, + ModelPart* pModelPart, + const std::vector& rEntityIds) +{ + KRATOS_TRY + + using container_type = std::remove_pointer_t()(std::declval()))>; + + if(pModelPart->IsSubModelPart()) { //does nothing if we are on the top model part + //obtain from the root model part the corresponding list of nodes + ModelPart* root_model_part = &pModelPart->GetRootModelPart(); + const auto& r_container = *rContainerGetter(root_model_part); + + // we first sort and unique the given entity ids. + std::vector entities; + entities.resize(rEntityIds.size()); + + // we are doing this in parallel because the most complex one is the finding of the entity for given id. + IndexPartition(rEntityIds.size()).for_each([pModelPart, &r_container, &entities, &rEntityIds](const auto Index) { + const auto entity_id = rEntityIds[Index]; + + auto it = r_container.find(entity_id); + + KRATOS_ERROR_IF(it == r_container.end()) + << "while adding " << ModelPart::Container::GetEntityName() << "s to submodelpart " + << pModelPart->FullName() << ", the " + << ModelPart::Container::GetEntityName() + << " with Id " << entity_id << " does not exist in the root model part"; + + entities[Index] = *(it.base()); + }); + + // aux is created to avoid doing sort and unique for all the parent model parts and current model part + // when insertion is done. since we dont use the entities anymore we can use the move operator here. + // since aux is an empty container. it will call push_back in the insert. + container_type aux; + aux.insert(std::move(entities)); + + ModelPart* current_part = pModelPart; + while(current_part->IsSubModelPart()) { + // this will directly call the PVS::insert overloaded for the PVS::iterator type. + rContainerGetter(current_part)->insert(aux.begin(), aux.end()); + current_part = &(current_part->GetParentModelPart()); + } + } + + KRATOS_CATCH(""); +} + +template +void RemoveEntities( + ModelPart::MeshType& rMesh, + const Flags& rIdentifierFlag) +{ + KRATOS_TRY + + auto& r_container = ModelPart::Container::GetContainer(rMesh); + + //count the nodes to be erase + const auto erase_count = block_for_each>(r_container, [&rIdentifierFlag](const auto& rEntity) -> unsigned int { + return rEntity.Is(rIdentifierFlag); + }); + + TContainerType temp_entities; + temp_entities.reserve(r_container.size() - erase_count); + temp_entities.swap(r_container); + + for(auto i_entity = temp_entities.begin() ; i_entity != temp_entities.end() ; ++i_entity) { + if (i_entity->IsNot(rIdentifierFlag)) { + // we can safely insert them at the end with the correct hint here since, the original r_mesh + // is sorted and unique. + r_container.insert(r_container.end(), std::move(*(i_entity.base()))); + } + } + + KRATOS_CATCH(""); +} + +} // namespace ModelPartHelperUtilities + /// Default constructor. ModelPart::ModelPart(VariablesList::Pointer pVariablesList, Model& rOwnerModel) : ModelPart("Default", pVariablesList, rOwnerModel) { } @@ -232,37 +318,9 @@ void ModelPart::AddNode(ModelPart::NodeType::Pointer pNewNode, ModelPart::IndexT /** Inserts a list of nodes in a submodelpart provided their Id. Does nothing if applied to the top model part */ -void ModelPart::AddNodes(std::vector const& NodeIds, IndexType ThisIndex) +void ModelPart::AddNodes(std::vector const& rNodeIds, IndexType ThisIndex) { - KRATOS_TRY - if(IsSubModelPart()) //does nothing if we are on the top model part - { - //obtain from the root model part the corresponding list of nodes - ModelPart* root_model_part = &this->GetRootModelPart(); - ModelPart::NodesContainerType aux; - aux.reserve(NodeIds.size()); - for(unsigned int i=0; iNodes().find(NodeIds[i]); - if(it!=root_model_part->NodesEnd()) - aux.push_back(*(it.base())); - else - KRATOS_ERROR << "while adding nodes to submodelpart, the node with Id " << NodeIds[i] << " does not exist in the root model part"; - } - - ModelPart* current_part = this; - while(current_part->IsSubModelPart()) - { - for(auto it = aux.begin(); it!=aux.end(); it++) - current_part->Nodes().push_back( *(it.base()) ); - - current_part->Nodes().Unique(); - - current_part = &(current_part->GetParentModelPart()); - } - } - - KRATOS_CATCH(""); + ModelPartHelperUtilities::AddEntitiesFromIds([](ModelPart* pModelPart) { return &pModelPart->Nodes(); }, this, rNodeIds); } /** Inserts a node in the mesh with ThisIndex. @@ -444,34 +502,10 @@ void ModelPart::RemoveNodeFromAllLevels(ModelPart::NodeType::Pointer pThisNode, void ModelPart::RemoveNodes(Flags IdentifierFlag) { - // Lambda to remove nodes from a mesh - auto remove_nodes_from_mesh = [&](ModelPart::MeshType& r_mesh) { - //count the nodes to be erase - const unsigned int nnodes = r_mesh.Nodes().size(); - unsigned int erase_count = 0; - #pragma omp parallel for reduction(+:erase_count) - for(int i=0; i(nnodes); ++i) { - ModelPart::NodesContainerType::iterator i_node = r_mesh.NodesBegin() + i; - - if( i_node->IsNot(IdentifierFlag) ) - erase_count++; - } - - ModelPart::NodesContainerType temp_nodes_container; - temp_nodes_container.reserve(r_mesh.Nodes().size() - erase_count); - - temp_nodes_container.swap(r_mesh.Nodes()); - - for(ModelPart::NodesContainerType::iterator i_node = temp_nodes_container.begin() ; i_node != temp_nodes_container.end() ; ++i_node) { - if( i_node->IsNot(IdentifierFlag) ) - (r_mesh.Nodes()).push_back(std::move(*(i_node.base()))); - } - }; - // This method is optimized to free the memory // Loop over all the local meshes (Is this still necessary with Submodelparts?) for(auto& r_mesh: this->GetMeshes()) { - remove_nodes_from_mesh(r_mesh); + ModelPartHelperUtilities::RemoveEntities(r_mesh, IdentifierFlag); } if (IsDistributed()) { @@ -479,19 +513,19 @@ void ModelPart::RemoveNodes(Flags IdentifierFlag) this->GetCommunicator().SynchronizeOrNodalFlags(IdentifierFlag); // Remove the nodes from the mpi-interfaces in case there is any - remove_nodes_from_mesh(this->GetCommunicator().LocalMesh()); + ModelPartHelperUtilities::RemoveEntities(this->GetCommunicator().LocalMesh(), IdentifierFlag); for(auto& r_mesh: this->GetCommunicator().LocalMeshes()) { - remove_nodes_from_mesh(r_mesh); + ModelPartHelperUtilities::RemoveEntities(r_mesh, IdentifierFlag); } - remove_nodes_from_mesh(this->GetCommunicator().GhostMesh()); + ModelPartHelperUtilities::RemoveEntities(this->GetCommunicator().GhostMesh(), IdentifierFlag); for(auto& r_mesh: this->GetCommunicator().GhostMeshes()) { - remove_nodes_from_mesh(r_mesh); + ModelPartHelperUtilities::RemoveEntities(r_mesh, IdentifierFlag); } - remove_nodes_from_mesh(this->GetCommunicator().InterfaceMesh()); + ModelPartHelperUtilities::RemoveEntities(this->GetCommunicator().InterfaceMesh(), IdentifierFlag); for(auto& r_mesh: this->GetCommunicator().InterfaceMeshes()) { - remove_nodes_from_mesh(r_mesh); + ModelPartHelperUtilities::RemoveEntities(r_mesh, IdentifierFlag); } } @@ -940,34 +974,7 @@ void ModelPart::AddElement(ModelPart::ElementType::Pointer pNewElement, ModelPar */ void ModelPart::AddElements(std::vector const& ElementIds, IndexType ThisIndex) { - KRATOS_TRY - if(IsSubModelPart()) //does nothing if we are on the top model part - { - //obtain from the root model part the corresponding list of nodes - ModelPart* root_model_part = &this->GetRootModelPart(); - ModelPart::ElementsContainerType aux; - aux.reserve(ElementIds.size()); - for(unsigned int i=0; iElements().find(ElementIds[i]); - if(it!=root_model_part->ElementsEnd()) - aux.push_back(*(it.base())); - else - KRATOS_ERROR << "the element with Id " << ElementIds[i] << " does not exist in the root model part"; - } - - ModelPart* current_part = this; - while(current_part->IsSubModelPart()) - { - for(auto it = aux.begin(); it!=aux.end(); it++) - current_part->Elements().push_back( *(it.base()) ); - - current_part->Elements().Unique(); - - current_part = &(current_part->GetParentModelPart()); - } - } - KRATOS_CATCH(""); + ModelPartHelperUtilities::AddEntitiesFromIds([](ModelPart* pModelPart) { return &pModelPart->Elements(); }, this, ElementIds); } /** Inserts an element in the mesh with ThisIndex. @@ -1128,36 +1135,14 @@ void ModelPart::RemoveElements(Flags IdentifierFlag) { // This method is optimized to free the memory //loop over all the meshes - auto& meshes = this->GetMeshes(); - for(ModelPart::MeshesContainerType::iterator i_mesh = meshes.begin() ; i_mesh != meshes.end() ; i_mesh++) - { - //count the elements to be erase - const unsigned int nelements = i_mesh->Elements().size(); - unsigned int erase_count = 0; - #pragma omp parallel for reduction(+:erase_count) - for(int i=0; i(nelements); ++i) - { - auto i_elem = i_mesh->ElementsBegin() + i; - - if( i_elem->IsNot(IdentifierFlag) ) - erase_count++; - } - - ModelPart::ElementsContainerType temp_elements_container; - temp_elements_container.reserve(i_mesh->Elements().size() - erase_count); - - temp_elements_container.swap(i_mesh->Elements()); - - for(ModelPart::ElementsContainerType::iterator i_elem = temp_elements_container.begin() ; i_elem != temp_elements_container.end() ; i_elem++) - { - if( i_elem->IsNot(IdentifierFlag) ) - (i_mesh->Elements()).push_back(std::move(*(i_elem.base()))); - } + for(auto& r_mesh : this->GetMeshes()) { + ModelPartHelperUtilities::RemoveEntities(r_mesh, IdentifierFlag); } //now recursively remove the elements in the submodelparts - for (SubModelPartIterator i_sub_model_part = SubModelPartsBegin(); i_sub_model_part != SubModelPartsEnd(); i_sub_model_part++) - i_sub_model_part->RemoveElements(IdentifierFlag); + for (auto& r_sub_model_part : this->SubModelParts()) { + r_sub_model_part.RemoveElements(IdentifierFlag); + } } void ModelPart::RemoveElementsFromAllLevels(Flags IdentifierFlag) @@ -1199,34 +1184,7 @@ void ModelPart::AddMasterSlaveConstraint(ModelPart::MasterSlaveConstraintType::P */ void ModelPart::AddMasterSlaveConstraints(std::vector const& MasterSlaveConstraintIds, IndexType ThisIndex) { - KRATOS_TRY - if(IsSubModelPart()) //does nothing if we are on the top model part - { - //obtain from the root model part the corresponding list of constraints - ModelPart* root_model_part = &this->GetRootModelPart(); - ModelPart::MasterSlaveConstraintContainerType aux; - aux.reserve(MasterSlaveConstraintIds.size()); - for(unsigned int i=0; iMasterSlaveConstraints().find(MasterSlaveConstraintIds[i]); - if(it!=root_model_part->MasterSlaveConstraintsEnd()) - aux.push_back(*(it.base())); - else - KRATOS_ERROR << "the master-slave constraint with Id " << MasterSlaveConstraintIds[i] << " does not exist in the root model part"; - } - - ModelPart* current_part = this; - while(current_part->IsSubModelPart()) - { - for(auto it = aux.begin(); it!=aux.end(); it++) - current_part->MasterSlaveConstraints().push_back( *(it.base()) ); - - current_part->MasterSlaveConstraints().Unique(); - - current_part = &(current_part->GetParentModelPart()); - } - } - KRATOS_CATCH(""); + ModelPartHelperUtilities::AddEntitiesFromIds([](ModelPart* pModelPart) { return &pModelPart->MasterSlaveConstraints(); }, this, MasterSlaveConstraintIds); } /// @brief Construct a new @ref MasterSlaveConstraint and insert it into the specified @ref Mesh. @@ -1394,33 +1352,14 @@ void ModelPart::RemoveMasterSlaveConstraintFromAllLevels(ModelPart::MasterSlaveC void ModelPart::RemoveMasterSlaveConstraints(Flags IdentifierFlag) { // This method is optimized to free the memory loop over all the meshes - auto& meshes = this->GetMeshes(); - for(auto it_mesh = meshes.begin() ; it_mesh != meshes.end() ; it_mesh++) { - // Count the constraints to be erase - const SizeType nconstraints = it_mesh->MasterSlaveConstraints().size(); - SizeType erase_count = 0; - #pragma omp parallel for reduction(+:erase_count) - for(int i=0; i(nconstraints); ++i) { - auto it_const = it_mesh->MasterSlaveConstraintsBegin() + i; - - if( it_const->IsNot(IdentifierFlag) ) - erase_count++; - } - - ModelPart::MasterSlaveConstraintContainerType temp_constraints_container; - temp_constraints_container.reserve(it_mesh->MasterSlaveConstraints().size() - erase_count); - - temp_constraints_container.swap(it_mesh->MasterSlaveConstraints()); - - for(auto it_const = temp_constraints_container.begin() ; it_const != temp_constraints_container.end(); it_const++) { - if( it_const->IsNot(IdentifierFlag) ) - (it_mesh->MasterSlaveConstraints()).push_back(std::move(*(it_const.base()))); - } + for(auto& r_mesh : this->GetMeshes()) { + ModelPartHelperUtilities::RemoveEntities(r_mesh, IdentifierFlag); } // Now recursively remove the constraints in the submodelparts - for (SubModelPartIterator i_sub_model_part = SubModelPartsBegin(); i_sub_model_part != SubModelPartsEnd(); i_sub_model_part++) - i_sub_model_part->RemoveMasterSlaveConstraints(IdentifierFlag); + for (auto& r_sub_model_part : this->SubModelParts()) { + r_sub_model_part.RemoveMasterSlaveConstraints(IdentifierFlag); + } } /***********************************************************************************/ @@ -1477,34 +1416,7 @@ void ModelPart::AddCondition(ModelPart::ConditionType::Pointer pNewCondition, Mo */ void ModelPart::AddConditions(std::vector const& ConditionIds, IndexType ThisIndex) { - KRATOS_TRY - if(IsSubModelPart()) //does nothing if we are on the top model part - { - //obtain from the root model part the corresponding list of nodes - ModelPart* root_model_part = &this->GetRootModelPart(); - ModelPart::ConditionsContainerType aux; - aux.reserve(ConditionIds.size()); - for(unsigned int i=0; iConditions().find(ConditionIds[i]); - if(it!=root_model_part->ConditionsEnd()) - aux.push_back(*(it.base())); - else - KRATOS_ERROR << "the condition with Id " << ConditionIds[i] << " does not exist in the root model part"; - } - - ModelPart* current_part = this; - while(current_part->IsSubModelPart()) - { - for(auto it = aux.begin(); it!=aux.end(); it++) - current_part->Conditions().push_back( *(it.base()) ); - - current_part->Conditions().Unique(); - - current_part = &(current_part->GetParentModelPart()); - } - } - KRATOS_CATCH(""); + ModelPartHelperUtilities::AddEntitiesFromIds([](ModelPart* pModelPart) { return &pModelPart->Conditions(); }, this, ConditionIds); } /** Inserts a condition in the mesh with ThisIndex. @@ -1663,36 +1575,14 @@ void ModelPart::RemoveConditions(Flags IdentifierFlag) { // This method is optimized to free the memory //loop over all the meshes - auto& meshes = this->GetMeshes(); - for(ModelPart::MeshesContainerType::iterator i_mesh = meshes.begin() ; i_mesh != meshes.end() ; i_mesh++) - { - //count the conditions to be erase - const unsigned int nconditions = i_mesh->Conditions().size(); - unsigned int erase_count = 0; - #pragma omp parallel for reduction(+:erase_count) - for(int i=0; i(nconditions); ++i) - { - auto i_cond = i_mesh->ConditionsBegin() + i; - - if( i_cond->IsNot(IdentifierFlag) ) - erase_count++; - } - - ModelPart::ConditionsContainerType temp_conditions_container; - temp_conditions_container.reserve(i_mesh->Conditions().size() - erase_count); - - temp_conditions_container.swap(i_mesh->Conditions()); - - for(ModelPart::ConditionsContainerType::iterator i_cond = temp_conditions_container.begin() ; i_cond != temp_conditions_container.end() ; i_cond++) - { - if( i_cond->IsNot(IdentifierFlag) ) - (i_mesh->Conditions()).push_back(std::move(*(i_cond.base()))); - } + for(auto& r_mesh : this->GetMeshes()) { + ModelPartHelperUtilities::RemoveEntities(r_mesh, IdentifierFlag); } //now recursively remove the conditions in the submodelparts - for (SubModelPartIterator i_sub_model_part = SubModelPartsBegin(); i_sub_model_part != SubModelPartsEnd(); i_sub_model_part++) - i_sub_model_part->RemoveConditions(IdentifierFlag); + for (auto& r_sub_model_part : this->SubModelParts()) { + r_sub_model_part.RemoveConditions(IdentifierFlag); + } } void ModelPart::RemoveConditionsFromAllLevels(Flags IdentifierFlag) @@ -2013,31 +1903,7 @@ void ModelPart::AddGeometry( */ void ModelPart::AddGeometries(std::vector const& GeometriesIds) { - KRATOS_TRY - if(IsSubModelPart()) { // Does nothing if we are on the top model part - // Obtain from the root model part the corresponding list of geometries - ModelPart* p_root_model_part = &this->GetRootModelPart(); - std::vector aux; - aux.reserve(GeometriesIds.size()); - for(auto& r_id : GeometriesIds) { - auto it_found = p_root_model_part->Geometries().find(r_id); - if(it_found != p_root_model_part->GeometriesEnd()) { - aux.push_back(*(it_found.base())); - } else { - KRATOS_ERROR << "The geometry with Id " << r_id << " does not exist in the root model part" << std::endl; - } - } - - ModelPart* p_current_part = this; - while(p_current_part->IsSubModelPart()) { - for(auto& p_geom : aux) { - p_current_part->AddGeometry(p_geom); - } - - p_current_part = &(p_current_part->GetParentModelPart()); - } - } - KRATOS_CATCH(""); + ModelPartHelperUtilities::AddEntitiesFromIds([](ModelPart* pModelPart) { return &pModelPart->Geometries(); }, this, GeometriesIds); } /// Removes a geometry by id. diff --git a/kratos/tests/cpp_tests/containers/test_pointer_vector_set.cpp b/kratos/tests/cpp_tests/containers/test_pointer_vector_set.cpp index 74ce8ffc56eb..76f9c91f7c82 100644 --- a/kratos/tests/cpp_tests/containers/test_pointer_vector_set.cpp +++ b/kratos/tests/cpp_tests/containers/test_pointer_vector_set.cpp @@ -150,6 +150,18 @@ KRATOS_TEST_CASE_IN_SUITE(PointerVectorSetInsert4, KratosCoreFastSuite) tmp.push_back(p_element_3_copy); test_container.insert(tmp.begin(), tmp.end()); + // check whether the original tmp is not touched. + auto temp_itr = tmp.begin(); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_2); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_1); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_4); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_3); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_4); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_6); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_1); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_5); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_3_copy); + KRATOS_EXPECT_EQ(test_container.size(), 6); auto itr = test_container.begin(); @@ -181,6 +193,13 @@ KRATOS_TEST_CASE_IN_SUITE(PointerVectorSetInsert5, KratosCoreFastSuite) tmp.push_back(p_element_8); test_container.insert(tmp.begin(), tmp.end()); + // check whether the original tmp is not touched. + auto temp_itr = tmp.begin(); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_2); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_1); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_5); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_8); + KRATOS_EXPECT_EQ(test_container.size(), 4); auto itr = test_container.begin(); for (; itr != test_container.end() - 1; ++itr) { @@ -198,6 +217,17 @@ KRATOS_TEST_CASE_IN_SUITE(PointerVectorSetInsert5, KratosCoreFastSuite) tmp.push_back(p_element_6); test_container.insert(tmp.begin(), tmp.end()); + // check whether the original tmp is not touched. + temp_itr = tmp.begin(); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_3); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_4); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_5); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_3); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_3_ptr_copy); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_1); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_7); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_6); + KRATOS_EXPECT_EQ(test_container.size(), 8); itr = test_container.begin(); for (; itr != test_container.end() - 1; ++itr) { @@ -225,6 +255,13 @@ KRATOS_TEST_CASE_IN_SUITE(PointerVectorSetInsert6, KratosCoreFastSuite) tmp.push_back(p_element_8); test_container.insert(tmp.begin(), tmp.end()); + // check whether the original tmp is not touched. + auto temp_itr = tmp.begin(); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_2); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_1); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_5); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_8); + KRATOS_EXPECT_EQ(test_container.size(), 4); auto itr = test_container.begin(); KRATOS_EXPECT_EQ(&*(itr++), &*p_element_1); @@ -243,6 +280,17 @@ KRATOS_TEST_CASE_IN_SUITE(PointerVectorSetInsert6, KratosCoreFastSuite) tmp.push_back(p_element_6); test_container.insert(tmp.begin(), tmp.end()); + // check whether the original tmp is not touched. + temp_itr = tmp.begin(); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_3); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_4); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_5); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_3); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_3_copy); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_1); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_7); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_6); + KRATOS_EXPECT_EQ(test_container.size(), 8); itr = test_container.begin(); KRATOS_EXPECT_EQ(&*(itr++), &*p_element_1); @@ -275,6 +323,14 @@ KRATOS_TEST_CASE_IN_SUITE(PointerVectorSetInsert7, KratosCoreFastSuite) tmp.push_back(p_element_8); test_container_1.insert(tmp.begin(), tmp.end()); + + // check whether the original tmp is not touched. + auto temp_itr = tmp.begin(); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_2); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_1); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_5); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_8); + KRATOS_EXPECT_EQ(test_container_1.size(), 4); auto itr = test_container_1.begin(); KRATOS_EXPECT_EQ(&*(itr++), &*p_element_1); @@ -301,6 +357,17 @@ KRATOS_TEST_CASE_IN_SUITE(PointerVectorSetInsert7, KratosCoreFastSuite) tmp.push_back(p_element_6); test_container_3.insert(tmp.begin(), tmp.end()); + // check whether the original tmp is not touched. + temp_itr = tmp.begin(); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_3); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_4); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_5); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_3); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_3_copy); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_1); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_7); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_6); + test_container_2.insert(test_container_3); KRATOS_EXPECT_EQ(test_container_2.size(), 8); itr = test_container_2.begin(); @@ -331,6 +398,13 @@ KRATOS_TEST_CASE_IN_SUITE(PointerVectorSetInsert8, KratosCoreFastSuite) tmp.push_back(elements[28]); test_container_1.insert(tmp.begin(), tmp.end()); + // check whether the original tmp is not touched. + auto temp_itr = tmp.begin(); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*elements[2]); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*elements[1]); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*elements[25]); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*elements[28]); + KRATOS_EXPECT_EQ(test_container_1.size(), 4); auto itr = test_container_1.begin(); KRATOS_EXPECT_EQ(&*(itr++), &*elements[1]); @@ -357,6 +431,16 @@ KRATOS_TEST_CASE_IN_SUITE(PointerVectorSetInsert8, KratosCoreFastSuite) tmp.push_back(elements[6]); test_container_3.insert(tmp.begin(), tmp.end()); + temp_itr = tmp.begin(); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*elements[3]); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*elements[4]); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*elements[5]); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*elements[3]); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_4_copy); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*p_element_10_copy); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*elements[7]); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*elements[6]); + test_container_2.insert(test_container_3); KRATOS_EXPECT_EQ(test_container_2.size(), 10); itr = test_container_2.begin(); @@ -377,6 +461,11 @@ KRATOS_TEST_CASE_IN_SUITE(PointerVectorSetInsert8, KratosCoreFastSuite) tmp.push_back(elements[48]); test_container_2.insert(tmp.begin(), tmp.end()); + temp_itr = tmp.begin(); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*elements[39]); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*elements[29]); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*elements[48]); + KRATOS_EXPECT_EQ(test_container_2.size(), 13); itr = test_container_2.begin(); KRATOS_EXPECT_EQ(&*(itr++), &*elements[1]); @@ -399,6 +488,11 @@ KRATOS_TEST_CASE_IN_SUITE(PointerVectorSetInsert8, KratosCoreFastSuite) tmp.push_back(elements[45]); test_container_2.insert(tmp.begin(), tmp.end()); + temp_itr = tmp.begin(); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*elements[28]); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*elements[31]); + KRATOS_EXPECT_EQ(&**(temp_itr++), &*elements[45]); + KRATOS_EXPECT_EQ(test_container_2.size(), 15); itr = test_container_2.begin(); KRATOS_EXPECT_EQ(&*(itr++), &*elements[1]); @@ -443,6 +537,46 @@ KRATOS_TEST_CASE_IN_SUITE(TestPointerVectorSetInsert9, KratosCoreFastSuite) KRATOS_EXPECT_EQ(&*(itr++), &*elements[28]); } +KRATOS_TEST_CASE_IN_SUITE(TestPointerVectorSetInsert10, KratosCoreFastSuite) +{ + PointerVectorSet, std::equal_to, std::shared_ptr> test_container; + + std::vector> elements; + for (IndexType i = 0; i < 50; ++i) { + elements.push_back(Kratos::make_shared(i + 1)); + } + + std::vector> tmp; + tmp.push_back(elements[5]); + tmp.push_back(elements[28]); + tmp.push_back(elements[2]); + tmp.push_back(elements[5]); + tmp.push_back(elements[28]); + tmp.push_back(elements[1]); + tmp.push_back(elements[25]); + + // following will change the original tmp + test_container.insert(std::move(tmp)); + + // now we check for the order since above should be changing the temp + // since it was moved. + auto itr_temp = tmp.begin(); + KRATOS_EXPECT_EQ(tmp.size(), 7); + KRATOS_EXPECT_EQ((**(itr_temp++)).Id(), 2); + KRATOS_EXPECT_EQ((**(itr_temp++)).Id(), 3); + KRATOS_EXPECT_EQ((**(itr_temp++)).Id(), 6); + KRATOS_EXPECT_EQ((**(itr_temp++)).Id(), 26); + KRATOS_EXPECT_EQ((**(itr_temp++)).Id(), 29); + + KRATOS_EXPECT_EQ(test_container.size(), 5); + auto itr = test_container.begin(); + KRATOS_EXPECT_EQ(&*(itr++), &*elements[1]); + KRATOS_EXPECT_EQ(&*(itr++), &*elements[2]); + KRATOS_EXPECT_EQ(&*(itr++), &*elements[5]); + KRATOS_EXPECT_EQ(&*(itr++), &*elements[25]); + KRATOS_EXPECT_EQ(&*(itr++), &*elements[28]); +} + KRATOS_TEST_CASE_IN_SUITE(TestPointerVectorSet, KratosCoreFastSuite) { // create model and model part diff --git a/kratos/tests/cpp_tests/sources/test_model_part.cpp b/kratos/tests/cpp_tests/sources/test_model_part.cpp index bff6ee5bc618..36f69b996300 100644 --- a/kratos/tests/cpp_tests/sources/test_model_part.cpp +++ b/kratos/tests/cpp_tests/sources/test_model_part.cpp @@ -12,6 +12,9 @@ // Vicente Mataix Ferrandiz // +// System includes +#include + // Project includes #include "containers/model.h" #include "testing/testing.h" @@ -580,4 +583,79 @@ namespace Kratos::Testing { model_part.RemoveSubModelPart("Inlet1.sub_inlet.sub_sub_inlet.test"), "Error: There is no sub model part with name \"sub_sub_inlet\" in model part \"Main.Inlet1.sub_inlet\""); } + + KRATOS_TEST_CASE_IN_SUITE(ModelPartSubRangeAddition, KratosCoreFastSuite) + { + Model model; + auto& r_model_part = model.CreateModelPart("test"); + Properties::Pointer p_elem_prop = r_model_part.CreateNewProperties(0); + + for (IndexType i = 0; i < 16; ++i) { + r_model_part.CreateNewNode(i + 1, 0.0, 0.0, 0.0); + } + + KRATOS_EXPECT_EQ(r_model_part.Nodes().size(), 16); + r_model_part.Nodes().shrink_to_fit(); + KRATOS_EXPECT_EQ(r_model_part.Nodes().capacity(), 16); + + auto& r_sub_model_part_1 = r_model_part.CreateSubModelPart("sub1"); + + r_sub_model_part_1.AddNodes(r_model_part.Nodes().begin(), r_model_part.Nodes().end()); + // above should not do anything to the r_model_part, hence capacity should be the same. + KRATOS_EXPECT_EQ(r_model_part.Nodes().capacity(), 16); + + KRATOS_EXPECT_EQ(r_sub_model_part_1.Nodes().size(), 16); + r_sub_model_part_1.Nodes().shrink_to_fit(); + KRATOS_EXPECT_EQ(r_sub_model_part_1.Nodes().capacity(), 16); + + auto& r_sub_model_part_2 = r_sub_model_part_1.CreateSubModelPart("sub2"); + r_sub_model_part_2.AddNodes(r_model_part.Nodes().begin() + 8, r_model_part.Nodes().end()); + // above should not do anything to the r_model_part, hence capacity should be the same. + KRATOS_EXPECT_EQ(r_model_part.Nodes().size(), 16); + KRATOS_EXPECT_EQ(r_model_part.Nodes().capacity(), 16); + KRATOS_EXPECT_EQ(r_sub_model_part_1.Nodes().size(), 16); + // the capacity of the r_sub_model_part_1 may be changed because, the r_sub_model_part_2 + // used the iterators of the r_model_part. Then r_sub_model_part_1 is not a subset of r_model_part + // because even though they are pointing to the same memory locations, the intrusive_ptrs memory locations + // are not a subset. Therefore not checking for the capacity. + KRATOS_EXPECT_EQ(r_sub_model_part_2.Nodes().size(), 8); + r_sub_model_part_2.Nodes().shrink_to_fit(); + KRATOS_EXPECT_EQ(r_sub_model_part_2.Nodes().capacity(), 8); + + // now we add using a sub range to sub2 + auto& r_sub_model_part_3 = r_sub_model_part_1.CreateSubModelPart("sub3"); + r_sub_model_part_3.AddNodes(r_model_part.Nodes().begin() + 4, r_model_part.Nodes().end()); + // above should not do anything to the r_model_part, hence capacity should be the same. + KRATOS_EXPECT_EQ(r_model_part.Nodes().size(), 16); + KRATOS_EXPECT_EQ(r_model_part.Nodes().capacity(), 16); + KRATOS_EXPECT_EQ(r_sub_model_part_1.Nodes().size(), 16); + + // again here the capacity changes because not r_sub_model_part_1 contains intrusive_ptrs + // which are not a sub set of the r_model_part + auto r_sub_model_part_1_capacity = r_sub_model_part_1.Nodes().capacity(); + KRATOS_EXPECT_EQ(r_sub_model_part_2.Nodes().size(), 8); + KRATOS_EXPECT_EQ(r_sub_model_part_2.Nodes().capacity(), 8); + KRATOS_EXPECT_EQ(r_sub_model_part_3.Nodes().size(), 12); + r_sub_model_part_3.Nodes().shrink_to_fit(); + KRATOS_EXPECT_EQ(r_sub_model_part_3.Nodes().capacity(), 12); + + auto& r_sub_model_part_4 = r_sub_model_part_3.CreateSubModelPart("sub4"); + r_sub_model_part_4.AddNodes(r_sub_model_part_3.Nodes().begin() + 4, r_sub_model_part_3.Nodes().end() - 1); + + // now there shouldn't be any change in the size or the capacity, because + // now we added r_sub_model_part_3 items to the r_sub_model_part_4. Then it will add them to the + // r_sub_model_part_4, and when it checks IsSubSet for added nodes with r_sub_model_part_3, then it will + // be a subset, hence all the parent model part additions are ignored. + KRATOS_EXPECT_EQ(r_model_part.Nodes().size(), 16); + KRATOS_EXPECT_EQ(r_model_part.Nodes().capacity(), 16); + KRATOS_EXPECT_EQ(r_sub_model_part_1.Nodes().size(), 16); + KRATOS_EXPECT_EQ(r_sub_model_part_1.Nodes().capacity(), r_sub_model_part_1_capacity); + KRATOS_EXPECT_EQ(r_sub_model_part_2.Nodes().size(), 8); + KRATOS_EXPECT_EQ(r_sub_model_part_2.Nodes().capacity(), 8); + KRATOS_EXPECT_EQ(r_sub_model_part_3.Nodes().size(), 12); + KRATOS_EXPECT_EQ(r_sub_model_part_3.Nodes().capacity(), 12); + KRATOS_EXPECT_EQ(r_sub_model_part_4.Nodes().size(), 7); + r_sub_model_part_4.Nodes().shrink_to_fit(); + KRATOS_EXPECT_EQ(r_sub_model_part_4.Nodes().capacity(), 7); + } } // namespace Kratos::Testing. diff --git a/kratos/tests/cpp_tests/utilities/test_model_part_utilities.cpp b/kratos/tests/cpp_tests/utilities/test_model_part_utilities.cpp index 90defa906952..ff9a11eb6b90 100644 --- a/kratos/tests/cpp_tests/utilities/test_model_part_utilities.cpp +++ b/kratos/tests/cpp_tests/utilities/test_model_part_utilities.cpp @@ -31,12 +31,12 @@ namespace Kratos::Testing { for(std::size_t id=1; id<25; id++) { auto p_node = r_model_part.CreateNewNode(id, 0.00,0.00,0.00); if (id%2!=0) { - aux.push_back(p_node); + aux.insert(aux.end(), p_node); } if (id<15) { - aux2.push_back(p_node); + aux2.insert(aux2.end(), p_node); } - aux3.push_back(p_node); + aux3.insert(aux3.end(), p_node); } // We check the first one r_ssmp.AddNodes(aux.begin(), aux.end()); @@ -63,7 +63,7 @@ namespace Kratos::Testing { // Here we can go a bit further. No need to be Unique for(auto it=aux.begin();it!=aux.end(); it++){ - aux3.push_back(*(it.base())); + aux3.insert(aux3.end(), *(it.base())); } r_ssmp.AddNodes(aux3.begin(), aux3.end()); KRATOS_EXPECT_EQ(r_ssmp.NumberOfNodes(), 24); diff --git a/kratos/tests/test_model_part.py b/kratos/tests/test_model_part.py index f1ea24395519..e418d644d8eb 100644 --- a/kratos/tests/test_model_part.py +++ b/kratos/tests/test_model_part.py @@ -734,7 +734,7 @@ def test_add_node(self): ### here we test adding a list of nodes at once #now add node 4 and 5 to the model_part1 by Id - here it fails since we did not yet add node 4 - with self.assertRaisesRegex(RuntimeError, "Error: while adding nodes to submodelpart, the node with Id 4 does not exist in the root model part"): + with self.assertRaisesRegex(RuntimeError, "Error: while adding nodes to submodelpart Main.sub1, the node with Id 4 does not exist in the root model part"): sub1.AddNodes([4,5]) model_part1.AddNode( n4, 0 ) @@ -783,7 +783,7 @@ def test_add_condition(self): ### here we test adding a list of conditions at once #now add node 4 and 5 to the model_part1 by Id - here it fails since we did not yet add node 4 - with self.assertRaisesRegex(RuntimeError, "Error: the condition with Id 4 does not exist in the root model part"): + with self.assertRaisesRegex(RuntimeError, "Error: while adding conditions to submodelpart Main.sub1, the condition with Id 4 does not exist in the root model part"): sub1.AddConditions([4,5]) model_part1.AddCondition( c4, 0 ) @@ -831,7 +831,7 @@ def test_add_element(self): ### here we test adding a list of elements at once #now add node 4 and 5 to the model_part1 by Id - here it fails since we did not yet add node 4 - with self.assertRaisesRegex(RuntimeError, "Error: the element with Id 4 does not exist in the root model part"): + with self.assertRaisesRegex(RuntimeError, "Error: while adding elements to submodelpart Main.sub1, the element with Id 4 does not exist in the root model part"): sub1.AddElements([4,5]) model_part1.AddElement( e4, 0 ) diff --git a/kratos/utilities/assign_master_slave_constraints_to_neighbours_utility.cpp b/kratos/utilities/assign_master_slave_constraints_to_neighbours_utility.cpp index 4bba4689fdd9..9b3169bfa7e5 100644 --- a/kratos/utilities/assign_master_slave_constraints_to_neighbours_utility.cpp +++ b/kratos/utilities/assign_master_slave_constraints_to_neighbours_utility.cpp @@ -222,7 +222,7 @@ void AssignMasterSlaveConstraintsToNeighboursUtility::AssignMasterSlaveConstrain }); // Create a temporary container to store the master-slave constraints - ConstraintContainerType temp_constraints; + std::vector temp_constraints; ModelPart::MasterSlaveConstraintType::Pointer p_constraint; // Dequeue the constraints from the concurrent queue and add them to the temporary container diff --git a/kratos/utilities/embedded_skin_utility.cpp b/kratos/utilities/embedded_skin_utility.cpp index afcc23242c29..bde315fcda2a 100644 --- a/kratos/utilities/embedded_skin_utility.cpp +++ b/kratos/utilities/embedded_skin_utility.cpp @@ -39,8 +39,8 @@ namespace Kratos // Auxiliar vectors to store pointers to the new entities // These vectors will be use when renumbering the entities ids. - ModelPart::NodesContainerType new_nodes_vect; - ModelPart::ConditionsContainerType new_conds_vect; + std::vector new_nodes_vect; + std::vector new_conds_vect; // Set the new condition properties Properties::Pointer p_cond_prop = this->SetSkinEntitiesProperties(); @@ -121,8 +121,8 @@ namespace Kratos unsigned int &rTempNodeId, unsigned int &rTempCondId, Properties::Pointer pCondProp, - ModelPart::NodesContainerType &rNewNodesVect, - ModelPart::ConditionsContainerType &rNewCondsVect) + std::vector &rNewNodesVect, + std::vector &rNewCondsVect) { // Set the split utility and compute the splitting pattern const auto &r_geom = pElement->GetGeometry(); @@ -178,8 +178,8 @@ namespace Kratos template void EmbeddedSkinUtility::RenumberAndAddSkinEntities( - const ModelPart::NodesContainerType &rNewNodesVect, - const ModelPart::ConditionsContainerType &rNewCondsVect) + const std::vector &rNewNodesVect, + const std::vector &rNewCondsVect) { // Once all the entities have been created, renumber the ids. // Created entities local number partial reduction @@ -207,13 +207,13 @@ namespace Kratos for (int i_node = 0; i_node < static_cast(rNewNodesVect.size()); ++i_node){ auto it_node = new_nodes_begin + i_node; const unsigned int new_id = new_node_id + i_node; - it_node->SetId(new_id); + (*it_node)->SetId(new_id); } for (int i_cond = 0; i_cond < static_cast(rNewCondsVect.size()); ++i_cond){ auto it_cond = new_conds_begin + i_cond; const unsigned int new_id = new_cond_id + i_cond; - it_cond->SetId(new_id); + (*it_cond)->SetId(new_id); } // Add the created conditions to the skin model part diff --git a/kratos/utilities/embedded_skin_utility.h b/kratos/utilities/embedded_skin_utility.h index 77ac8eba307f..dd3b09966392 100644 --- a/kratos/utilities/embedded_skin_utility.h +++ b/kratos/utilities/embedded_skin_utility.h @@ -241,8 +241,8 @@ class KRATOS_API(KRATOS_CORE) EmbeddedSkinUtility unsigned int &rTempNodeId, unsigned int &rTempCondId, Properties::Pointer pCondProp, - ModelPart::NodesContainerType &rNewNodesVect, - ModelPart::ConditionsContainerType &rNewCondsVect); + std::vector &rNewNodesVect, + std::vector &rNewCondsVect); /** * @brief Checks if an element is split @@ -329,8 +329,8 @@ class KRATOS_API(KRATOS_CORE) EmbeddedSkinUtility * @param rNewCondsVect vector that stores the new skin conditions */ void RenumberAndAddSkinEntities( - const ModelPart::NodesContainerType &rNewNodesVect, - const ModelPart::ConditionsContainerType &rNewCondsVect); + const std::vector &rNewNodesVect, + const std::vector &rNewCondsVect); /** * @brief Set the Distances Vector object diff --git a/kratos/utilities/type_traits.h b/kratos/utilities/type_traits.h new file mode 100644 index 000000000000..d4485a966cf7 --- /dev/null +++ b/kratos/utilities/type_traits.h @@ -0,0 +1,34 @@ +// | / | +// ' / __| _` | __| _ \ __| +// . \ | ( | | ( |\__ ` +// _|\_\_| \__,_|\__|\___/ ____/ +// Multi-Physics +// +// License: BSD License +// Kratos default license: kratos/license.txt +// +// Main author: Suneth Warnakulasuriya +// + +#pragma once + +#include + +namespace Kratos { + +// TODO: Remove the following when we move to C++20 or higher +template +using BaseType = typename std::remove_cv_t>; + +// Primary template that will be used for the fallback +template +struct HasValueType : std::false_type {}; + +// Specialization for types that have a `value_type` member +template +struct HasValueType> : std::true_type {}; + +template +using IsRValueContainer = std::conjunction, HasValueType>; + +} // namespace Kratos \ No newline at end of file