Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[Core] Remove push_back from ModelPart #12903

Open
wants to merge 34 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
dd02fc8
add additional insert and typecheck to PVS
sunethwarna Dec 5, 2024
2bf1e96
remove push_backs from ModelPart
sunethwarna Dec 5, 2024
2c5d23f
include reduction_utils
sunethwarna Dec 5, 2024
d5521ba
minor
sunethwarna Dec 5, 2024
2f72e0c
func name change
sunethwarna Dec 5, 2024
1e98ea3
fix embedded_skin_utility for Id Change
sunethwarna Dec 10, 2024
9c05b31
allow insertion from const vectors
sunethwarna Dec 10, 2024
abbce15
allow insertion from parent entity ranges
sunethwarna Dec 10, 2024
71ae35f
fix for iterator invalidation
sunethwarna Dec 10, 2024
6d34602
add comments
sunethwarna Dec 10, 2024
f1c4a6f
minor signature modification
sunethwarna Dec 10, 2024
35c1874
fix assign_ms_const_to_neigh_utils
sunethwarna Dec 11, 2024
13e87e7
fix error msg tests
sunethwarna Dec 11, 2024
a50fd2b
fix hdf5
sunethwarna Dec 11, 2024
8a42759
generalize addition from ids
sunethwarna Dec 11, 2024
93293e0
simplify
sunethwarna Dec 11, 2024
6adabf4
fix ranged id addition
sunethwarna Dec 11, 2024
9082f96
Merge remote-tracking branch 'origin/master' into pvs/remove_push_bac…
sunethwarna Dec 12, 2024
d7d4734
fix parmmg
sunethwarna Dec 16, 2024
d718a9a
add benchmarks for modelpart
sunethwarna Dec 16, 2024
4430f5e
minor for comparison
sunethwarna Dec 16, 2024
d7e05de
add type_traits
sunethwarna Dec 18, 2024
fedc6b3
allow rvalue containers in pvs insert
sunethwarna Dec 18, 2024
d642295
allow rvalue containers in model part
sunethwarna Dec 18, 2024
ee29e90
tests for rvalue containers in PVS
sunethwarna Dec 18, 2024
71e3074
more performance improvements
sunethwarna Dec 18, 2024
1024156
fix tests
sunethwarna Dec 18, 2024
a9e8219
minor
sunethwarna Dec 18, 2024
abfdcab
bug fix
sunethwarna Dec 18, 2024
a00437f
fix sub range addition
sunethwarna Jan 2, 2025
36fd864
add a test to subrange addition
sunethwarna Jan 2, 2025
88a5f51
fix test
sunethwarna Jan 2, 2025
22ff3e1
expose shrink_to_fit
sunethwarna Jan 2, 2025
99aca1d
change test
sunethwarna Jan 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Copy link
Member

Choose a reason for hiding this comment

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

this is not efficient as you may be creating it out of order

Copy link
Member Author

Choose a reason for hiding this comment

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

It cannot be out of order, because we assume HDF5 files are written by the HDF5Application, hence when we write, we always write in the sorted order.

Copy link
Member

Choose a reason for hiding this comment

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

then i do not understand the role of mIds ...

Copy link
Member

Choose a reason for hiding this comment

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

wait, mIds is ordered?

Copy link
Member

Choose a reason for hiding this comment

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

i'll accept the comment, but ... what if somone writes the hdf5 "by hand"? in that case it will be enormously expensive, correct?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, it will be expensive, but on the other hand, do we expect them to create HDF5 files by their own?, it needs to have a special structure. It it not impossible, but tedious.

If they are creating, then they can create them in sorted manner as well.

Copy link
Member

@roigcarlo roigcarlo Jan 14, 2025

Choose a reason for hiding this comment

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

I tend to lean with @sunethwarna point of view here. We just assume that id's are ordered here, or at least provide a mechanism to sort them before reaching this points. Not only the code is easier to read and do not need sorts in between, when we move to C++20 we can replace this with something like:

...
auto entities = std::views::iota(0,num_new_elems) | std::views::transform([&](int i) -> ElementType::Pointer {
    for (unsigned j = 0; j < geometry_size; j++) {
        const int node_id = mConnectivities(i, j);
        nodes(j) = rNodes(node_id);
    }
    return p_elem = r_elem.Create(mIds[i], nodes, rProperties(mPropertiesIds[i]));
});

rElements.insert(entities.begin(), entities.end(), entities.end())

Which ensures that the view is inserted directly into rElements and remove the need for a temporal container with random access or the need to insert 1 by 1, which means that will be faster.

}
KRATOS_CATCH("");
}
Expand Down Expand Up @@ -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);
RiccardoRossi marked this conversation as resolved.
Show resolved Hide resolved
}
KRATOS_CATCH("");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ void PointsData::CreateNodes(NodesContainerType& rNodes)
const array_1d<double, 3>& r_coord = mCoords[i];
NodeType::Pointer p_node = Kratos::make_intrusive<NodeType>(
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);
Copy link
Member

Choose a reason for hiding this comment

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

same

Copy link
Member Author

Choose a reason for hiding this comment

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

Same comment as before

}
KRATOS_CATCH("");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1184,9 +1184,9 @@ void ParMmgUtilities<TPMMGLibrary>::WriteMeshDataToModelPart(
std::unordered_map<IndexType, IndexVectorType> 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<Condition::Pointer> created_conditions_vector;
std::vector<Element::Pointer> created_elements_vector;
std::vector<Node::Pointer> created_nodes_vector;

// Auxiliar values
int ref, is_required;
Expand Down
274 changes: 274 additions & 0 deletions kratos/benchmarks/model_part_benchmark.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
// | / |
// ' / __| _` | __| _ \ __|
// . \ | ( | | ( |\__ `
// _|\_\_| \__,_|\__|\___/ ____/
// Multi-Physics
//
// License: BSD License
// Kratos default license: kratos/license.txt
//
// Main authors: Suneth Warnakulasuriya
//

// System includes
#include <vector>

// External includes
#include <benchmark/benchmark.h>

// 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<ModelPart::NodeType::Pointer> 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<NodeType>(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<ModelPart::NodeType, IndexedObject> 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<NodeType>(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<ModelPart::NodeType::Pointer> 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<NodeType>(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<ModelPart::NodeType::Pointer> 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<NodeType>(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<ModelPart::NodeType::Pointer> 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<NodeType>(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<ModelPart::NodeType, IndexedObject> nodes;
std::vector<IndexType> 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<NodeType>(i + 1, 0.0, 0.0, 0.0));
RiccardoRossi marked this conversation as resolved.
Show resolved Hide resolved
}

// 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();
Loading
Loading