OGS
HeatTransportBHE/BHE/MeshUtils.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
4#include "MeshUtils.h"
5
6#include <algorithm>
7#include <set>
8#include <unordered_map>
9#include <utility>
10
11#include "BaseLib/Algorithm.h"
12#include "BaseLib/Error.h"
14#include "MeshLib/Mesh.h"
16#include "MeshLib/Node.h"
17
18namespace
19{
20std::vector<MeshLib::Element*> extractOneDimensionalElements(
21 std::vector<MeshLib::Element*> const& elements)
22{
23 std::vector<MeshLib::Element*> one_dimensional_elements;
24
25 copy_if(
26 begin(elements), end(elements), back_inserter(one_dimensional_elements),
27 [](MeshLib::Element const* const e) { return e->getDimension() == 1; });
28
29 return one_dimensional_elements;
30}
31
32std::vector<int> getUniqueMaterialIds(
33 MeshLib::PropertyVector<int> const& material_ids,
34 std::vector<MeshLib::Element*> const& elements)
35{
36 std::set<int> unique_material_ids;
37 std::transform(begin(elements), end(elements),
38 inserter(unique_material_ids, end(unique_material_ids)),
39 [&material_ids](MeshLib::Element const* const e)
40 { return material_ids[e->getID()]; });
41 return {begin(unique_material_ids), end(unique_material_ids)};
42}
43} // namespace
44
45namespace ProcessLib
46{
47namespace HeatTransportBHE
48{
50{
51 std::vector<MeshLib::Element*> const all_bhe_elements =
52 extractOneDimensionalElements(mesh.getElements());
53
54 // finally counting two types of elements
55 // They are (i) soil, and (ii) BHE type of elements
56 DBUG("-> found total {:d} soil elements and {:d} BHE elements",
57 mesh.getNumberOfElements() - all_bhe_elements.size(),
58 all_bhe_elements.size());
59
60 // get BHE material IDs
61 auto const* const opt_material_ids = MeshLib::materialIDs(mesh);
62 if (opt_material_ids == nullptr)
63 {
64 OGS_FATAL("Not able to get material IDs! ");
65 }
66 auto const& material_ids = *opt_material_ids;
67
68 auto const& bhe_material_ids =
69 getUniqueMaterialIds(material_ids, all_bhe_elements);
70 DBUG("-> found {:d} BHE material groups", bhe_material_ids.size());
71
72 // create a vector of BHE elements for each group
73 std::vector<std::vector<MeshLib::Element*>> bhe_elements;
74 bhe_elements.resize(bhe_material_ids.size());
75 for (unsigned bhe_id = 0; bhe_id < bhe_material_ids.size(); bhe_id++)
76 {
77 const auto bhe_mat_id = bhe_material_ids[bhe_id];
78 std::vector<MeshLib::Element*>& vec_elements = bhe_elements[bhe_id];
79 copy_if(begin(all_bhe_elements), end(all_bhe_elements),
80 back_inserter(vec_elements),
81 [&](MeshLib::Element const* const e)
82 { return material_ids[e->getID()] == bhe_mat_id; });
83 DBUG("-> found {:d} elements on the BHE_{:d}", vec_elements.size(),
84 bhe_id);
85 }
86
87 // get a vector of BHE nodes
88 std::vector<std::vector<MeshLib::Node*>> bhe_nodes;
89 bhe_nodes.resize(bhe_material_ids.size());
90 for (unsigned bhe_id = 0; bhe_id < bhe_material_ids.size(); bhe_id++)
91 {
92 std::vector<MeshLib::Node*>& vec_nodes = bhe_nodes[bhe_id];
93 for (MeshLib::Element* e : bhe_elements[bhe_id])
94 {
95 for (unsigned i = 0; i < e->getNumberOfNodes(); i++)
96 {
97 vec_nodes.push_back(const_cast<MeshLib::Node*>(e->getNode(i)));
98 }
99 }
102
103 DBUG("-> found {:d} nodes on the BHE_{:d}", vec_nodes.size(), bhe_id);
104 }
105
106 return {bhe_material_ids, bhe_elements, bhe_nodes};
107}
108
110 std::vector<MeshLib::Element*> const& bhe_elements)
111{
112 if (bhe_elements.empty())
113 {
114 OGS_FATAL(
115 "findBHEEndpointsFromElementOrdering called with an empty BHE "
116 "element list.");
117 }
118
119 // Adjacency: node id -> list of (element index, local-node-index 0 or 1)
120 // that reference it. For a well-formed BHE chain every node appears in
121 // either 1 (endpoint) or 2 (interior) such records.
122 std::unordered_map<std::size_t, std::vector<std::pair<std::size_t, int>>>
123 adjacency;
124 for (std::size_t ei = 0; ei < bhe_elements.size(); ++ei)
125 {
126 auto const* e = bhe_elements[ei];
127 if (e->getNumberOfNodes() != 2)
128 {
129 OGS_FATAL(
130 "BHE element {:d} has {:d} nodes; expected a 2-node line "
131 "element.",
132 e->getID(), e->getNumberOfNodes());
133 }
134 for (int local = 0; local < 2; ++local)
135 {
136 adjacency[e->getNode(local)->getID()].emplace_back(ei, local);
137 }
138 }
139
140 // Endpoints are nodes connected to exactly one BHE line element. Any
141 // node connected to more than 2 elements means the BHE is branching and
142 // is not a simple chain.
143 std::vector<std::size_t> endpoint_node_ids;
144 for (auto const& [nid, refs] : adjacency)
145 {
146 if (refs.size() == 1)
147 {
148 endpoint_node_ids.push_back(nid);
149 }
150 else if (refs.size() > 2)
151 {
152 OGS_FATAL(
153 "BHE element chain has a branching junction at node {:d} "
154 "(connected to {:d} elements). Expected a simple line chain.",
155 nid, refs.size());
156 }
157 }
158
159 if (endpoint_node_ids.size() != 2)
160 {
161 OGS_FATAL(
162 "BHE elements do not form a single connected line chain (found "
163 "{:d} endpoints; expected 2). Check that all line elements with "
164 "the same BHE material ID share endpoints node-to-node.",
165 endpoint_node_ids.size());
166 }
167
168 // The inlet is the endpoint that appears as local node 0 of its element;
169 // the outlet is the endpoint that appears as local node 1 of its
170 // element. If neither endpoint is at local node 0 (or both are), the
171 // chain-start or chain-end element is reversed.
172 MeshLib::Node const* inlet = nullptr;
173 MeshLib::Node const* outlet = nullptr;
174 for (std::size_t const nid : endpoint_node_ids)
175 {
176 auto const& [ei, local] = adjacency[nid].front();
177 if (local == 0)
178 {
179 if (inlet != nullptr)
180 {
181 OGS_FATAL(
182 "Both BHE chain endpoints (nodes {:d} and {:d}) appear "
183 "as local node 0 of their connected element; chain "
184 "orientation is ambiguous. Reorder element nodes so the "
185 "chain runs node 0 -> node 1 from inlet to outlet.",
186 inlet->getID(), nid);
187 }
188 inlet = bhe_elements[ei]->getNode(0);
189 }
190 else
191 {
192 outlet = bhe_elements[ei]->getNode(1);
193 }
194 }
195
196 if (inlet == nullptr || outlet == nullptr)
197 {
198 OGS_FATAL(
199 "BHE chain endpoints are not consistent with the elements' "
200 "node-0 -> node-1 ordering. Reorder element nodes so that the "
201 "intended inlet endpoint is node 0 of the chain-start element "
202 "and the intended outlet endpoint is node 1 of the chain-end "
203 "element.");
204 }
205
206 // Walk the chain from the inlet: at each step, the unused element whose
207 // node 0 equals the current node must exist, otherwise some interior
208 // element is reversed.
209 std::vector<bool> used(bhe_elements.size(), false);
210 MeshLib::Node const* cur = inlet;
211 for (std::size_t step = 0; step < bhe_elements.size(); ++step)
212 {
213 auto const& refs = adjacency[cur->getID()];
214 bool advanced = false;
215 for (auto const& [ei, local] : refs)
216 {
217 if (!used[ei] && local == 0)
218 {
219 used[ei] = true;
220 cur = bhe_elements[ei]->getNode(1);
221 advanced = true;
222 break;
223 }
224 }
225 if (!advanced)
226 {
227 OGS_FATAL(
228 "BHE element chain is inconsistently oriented at node {:d}: "
229 "an interior element does not have this node as its node 0. "
230 "Reorder the element's nodes so the chain runs node 0 -> "
231 "node 1 from inlet to outlet.",
232 cur->getID());
233 }
234 }
235
236 if (cur != outlet)
237 {
238 OGS_FATAL(
239 "BHE chain walk did not terminate at the expected outlet "
240 "endpoint (expected node {:d}, reached node {:d}).",
241 outlet->getID(), cur->getID());
242 }
243
244 return {inlet, outlet};
245}
246
247} // end of namespace HeatTransportBHE
248} // namespace ProcessLib
#define OGS_FATAL(...)
Definition Error.h:10
void DBUG(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:22
std::size_t getID() const
virtual constexpr unsigned getDimension() const =0
Get dimension of the mesh element.
std::size_t getID() const
Returns the ID of the element.
Definition Element.h:80
std::vector< Element * > const & getElements() const
Get the element-vector for the mesh.
Definition Mesh.h:101
std::size_t getNumberOfElements() const
Get the number of elements.
Definition Mesh.h:89
void makeVectorUnique(std::vector< T > &v)
Definition Algorithm.h:198
bool idsComparator(T const a, T const b)
Definition Mesh.h:204
PropertyVector< int > const * materialIDs(Mesh const &mesh)
Definition Mesh.cpp:260
BHEMeshData getBHEDataInMesh(MeshLib::Mesh const &mesh)
BHEEndpoints findBHEEndpointsFromElementOrdering(std::vector< MeshLib::Element * > const &bhe_elements)
std::vector< MeshLib::Element * > extractOneDimensionalElements(std::vector< MeshLib::Element * > const &elements)
std::vector< int > getUniqueMaterialIds(MeshLib::PropertyVector< int > const &material_ids, std::vector< MeshLib::Element * > const &elements)