OpenRadioss 2025.1.11
OpenRadioss project
Loading...
Searching...
No Matches
cpp_reorder_elements.cpp
Go to the documentation of this file.
1//Copyright> OpenRadioss
2//Copyright> Copyright (C) 1986-2025 Altair Engineering Inc.
3//Copyright>
4//Copyright> This program is free software: you can redistribute it and/or modify
5//Copyright> it under the terms of the GNU Affero General Public License as published by
6//Copyright> the Free Software Foundation, either version 3 of the License, or
7//Copyright> (at your option) any later version.
8//Copyright>
9//Copyright> This program is distributed in the hope that it will be useful,
10//Copyright> but WITHOUT ANY WARRANTY; without even the implied warranty of
11//Copyright> MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12//Copyright> GNU Affero General Public License for more details.
13//Copyright>
14//Copyright> You should have received a copy of the GNU Affero General Public License
15//Copyright> along with this program. If not, see <https://www.gnu.org/licenses/>.
16//Copyright>
17//Copyright>
18//Copyright> Commercial Alternative: Altair Radioss Software
19//Copyright>
20//Copyright> As an alternative to this open-source version, Altair also offers Altair Radioss
21//Copyright> software under a commercial license. Contact Altair to discuss further if the
22//Copyright> commercial version may interest you: https://www.altair.com/radioss/.
23#include <string>
24#include <iostream>
25#include <sstream>
26#include <fstream>
27#include <vector>
28#include <set>
29#include <unordered_map>
30#include <algorithm>
31#include <map>
32#include <set>
33#include <deque>
34#include <list>
35//#define NDEBUG
36//#include <cassert>
37
38using namespace std;
39
40#define _FCALL
41
42typedef vector<int> Vint;
43
44// Elements are partitioned: one element belongs to only one domain
46{
47 Vint localId; // localId[global id] = local id on the domain
48 vector<Vint> globalId; // globalId[domain p][global id] = local id on domain P
49};
50
51// Nodes are mapped to domains: one node can belong to many domains
53{
54 unordered_map<int, int> toCompact; // toCompact[global id] = compact id
55 Vint toGlobal; // toGlobal[compact id] = global id
56
57public:
58 int size() { return toGlobal.size(); };
59 void addGlobalId(const int &i)
60 {
61 if (toCompact.find(i) == toCompact.end())
62 {
63 const int nb = toCompact.size();
64 toCompact[i] = nb;
65 toGlobal.push_back(i);
66 }
67 };
68 int getCompactId(const int &i) const
69 {
70 auto got = toCompact.find(i);
71 //assert(got != toCompact.end());
72 return got->second;
73 };
74 int getGlobalId(const int &i) const
75 {
76 //assert(i >= 0);
77 //assert(((size_t)i) < toGlobal.size());
78 return toGlobal[i];
79 }
80};
81
82typedef vector<NodeMapping> Vorder;
83
85{
86
87 vector<Vint> _Adj;
88 size_t _N;
89
90 // degree = number of neighbors
91 size_t degree(const size_t &i) const
92 {
93 return _Adj[i].size();
94 }
95
96
97 //find a pseudo-peripheral root
98 // [in] mask: mask[i] == -1 => i is not numbered yet
99 // [out] lower_bound: starting point for looking for a root
100 // [return] id of the next root: id such that mask[id] == -1 and degree(id) is minimal
101 size_t pickRoot(const Vint &mask, size_t &lower_bound) const
102 {
103 size_t root = 0;
104
105 // pick next available root
106 for (root = lower_bound; root < _N; root++)
107 {
108 if (mask[root] == -1)
109 {
110 break;
111 }
112 }
113
114 int maxLevel = -1;
115 bool continueLoop = true;
116 size_t iter = 0;
117
118 while(continueLoop && iter < _N)
119 {
120 iter++;
121 size_t currentDist = 0;
122 Vint level(_N, -1); // level[i] = distance to root or -1 if not visited yet
123 deque<int> currentVertices(1, root);
124 level[root] = 0;
125 while(currentVertices.size() > 0)
126 {
127
128 const int current = currentVertices.front();
129 if (level[current] > level[root] || (level[current] == level[root] && degree(current) > degree(root)))
130 {
131 //the new root is the vertex with the highest level
132 // with level > -1 (i.e. reachable from the current root)
133 root = current;
134 } else if(level[current] == level[root] && degree(current) == degree(root))
135 {
136 if(current < root)
137 {
138 root = current;
139 }
140 }
141
142 currentVertices.pop_front();
143 currentDist = level[current] + 1;
144 for(const auto &next : _Adj[current])
145 {
146 if (level[next] == -1)
147 {
148 level[next] = currentDist;
149 currentVertices.push_back(next);
150 }
151 }
152 }
153
154 if(maxLevel < level[root])
155 { // continue if we have found a new root
156 maxLevel = level[root];
157 }else
158 { // stop if we have not found a new root
159 continueLoop = false;
160 }
161 }
162
163 //assert(mask[root] == -1);
164 return root;
165 }
166
167public:
168 void setNbVertices(const size_t &s)
169 {
170 _N = s;
171 _Adj.resize(_N);
172 }
173 void reserve(const size_t &s)
174 {
175 for (auto &a : _Adj)
176 {
177 a.reserve(s);
178 }
179 }
180
181 inline void addEdge(const int &a, const int &b)
182 {
183 _Adj[a].push_back(b);
184 }
185
186
187 // [out] order, permutation of indexes to minimize the bandwidth
189 {
190 Vint dist(_N,-1);
191 Vint order(_N,-1);
192 size_t lowerBound = 0;
193 size_t nextIndex = 0;
194
195 while (nextIndex < _N)
196 {
197 const size_t root = pickRoot(order, lowerBound);
198 //assert(dist[root] == -1);
199 //assert(order[root] == -1);
200 dist[root] = 0;
201 order[root] = nextIndex++;
202 size_t currentDist = 0;
203 deque<int> currentVertices(1, root);
204 while(currentVertices.size() > 0)
205 {
206 const int current = currentVertices.front();
207 currentVertices.pop_front();
208 currentDist = dist[current] + 1;
209 multiset<pair<int, int>> nextVertices;
210 for (const auto &next : _Adj[current])
211 {
212 if (dist[next] == -1)
213 {
214 dist[next] = currentDist;
215 nextVertices.insert(make_pair(degree(next), next));
216 }
217 }
218 for(const auto &next : nextVertices)
219 {
220 order[next.second] = nextIndex++;
221 currentVertices.push_back(next.second);
222 }
223 }
224 }
225 // order["old"] = "new"
226 return order;
227 }
228
229 void printStats(const Vint &perm) const
230 {
231 int newMaxBandwidth = 0;
232 long long int newSumBandwidth = 0;
233 int oldMaxBandwidth = 0;
234 long long int oldSumBandwidth = 0;
235 Vint iperm(perm.size());
236 for (size_t i = 0; i < perm.size(); ++i)
237 {
238 iperm[perm[i]] = i;
239 }
240 for (size_t i = 0; i < _N; ++i)
241 {
242 for (const auto &j : _Adj[i])
243 {
244 const int newBandwidth = abs(perm[i] - perm[j]);
245 newMaxBandwidth = max(newMaxBandwidth, newBandwidth);
246 newSumBandwidth += newBandwidth;
247 const int oldBandwidth = abs(((int)i) - j);
248 oldMaxBandwidth = max(oldMaxBandwidth, oldBandwidth);
249 oldSumBandwidth += oldBandwidth;
250 }
251 }
252 if (_N > 1)
253 {
254 cout << "n:" << _N << " Old/New Max: " << ((double)oldMaxBandwidth)<<"/"<<((double)newMaxBandwidth);
255 cout << " Sum: " << ((double)oldSumBandwidth)<<"/"<<((double)newSumBandwidth) << endl;
256 }
257 }
258 void printPermutation(const vector<int> &perm) const
259 {
260 ofstream outfile;
261 outfile.open("spy_matrices.m");
262 outfile << "clear all; \n";
263 outfile << " A = sparse(" << _N << "," << _N << "); \n";
264 outfile << " P = zeros(" << _N << ",1); \n";
265 for (size_t i = 0; i < _N; ++i)
266 {
267 outfile << "P(" << perm[i] + 1 << ")=" << i + 1 << "; \n";
268 }
269 for (size_t i = 0; i < _N; ++i)
270 {
271 for (const auto &j : _Adj[i])
272 {
273 outfile << "A(" << i + 1 << "," << j + 1 << ") = 1; \n";
274 }
275 }
276 outfile.close();
277 }
278};
279// checks that v is a permutation of 0:v.size()-1
280bool is_permutation(const Vint &v)
281{
282 Vint r(v.size(), 0);
283 for (const auto &i : v)
284 {
285 if (i < 0 || ((size_t)i) > v.size())
286 {
287 return false;
288 }
289 r[i] = 1;
290 }
291 size_t sum = 0;
292 for (auto &n : r)
293 {
294 sum += n;
295 }
296 return sum == v.size();
297}
298void splitPerDomain(int nel, int nodesPerElt, size_t nspmd, int *domain, int lda, int offset, int *elt2Nodes,
299 Vorder &nodes, ElementPartition &elts)
300{ // Distribute the elements according to domain[i = 0:nspmd-1]
301 // defines local-to-domain ids of elements and nodes
302 elts.globalId = vector<Vint>(nspmd);
303 elts.localId = Vint(nel, -1);
304 for (int i = 0; i < nel; i++)
305 {
306 const int p = domain[i];
307 elts.localId[i] = elts.globalId[p].size();
308 elts.globalId[p].push_back(i);
309 for (int j = 0; j < nodesPerElt; j++)
310 {
311 nodes[p].addGlobalId(elt2Nodes[i * lda + j + offset]);
312 }
313 }
314}
315extern "C"
316{
317 // called from Fortran
318 void cpp_reorder_elements(int *NEL, int *NSPMD, int *NODES_PER_ELT, int *OFFSET, int *LDA, int *domain, int *elt2Nodes, int *permutation)
319 {
320 const int nel = *NEL; // number of elements
321 const size_t nspmd = (size_t)*NSPMD; // number of subdomains
322 const int nodesPerElt = *NODES_PER_ELT; // number of nodes per element
323 const int offset = *OFFSET; // padding size of array elt2Nodes (see cgrtails.F)
324 const int lda = *LDA; // leading dimension of elt2Nodes array
325 // elt2Nodes is a 2D array of size nel x nodesPerElt such as elt2Nodes[i*lda + j + offset] is the global id of the jth node of the ith element
326
327 Vorder nodesMapping(nspmd);
328 ElementPartition eltsParition;
329 vector<Adjacency> adjacencies(nspmd);
330 vector<vector<Vint>> node2Elts(nspmd);
331 vector<Vint> elt2LocalNodes(nspmd);
332
333 splitPerDomain(nel, nodesPerElt, nspmd, domain, lda, offset, elt2Nodes, nodesMapping, eltsParition);
334
335 for (size_t p = 0; p < nspmd; p++)
336 {
337 const int nb_local_elts = eltsParition.globalId[p].size();
338 const int nb_local_nodes = nodesMapping[p].size();
339 node2Elts[p].resize(nb_local_nodes);
340 adjacencies[p].setNbVertices(nb_local_elts);
341 adjacencies[p].reserve(30); // estimation of the maximum number of neighbor per element
342 elt2LocalNodes[p].resize(nb_local_elts * nodesPerElt);
343 }
344
345 // Fill node2Elts[domain][local_node_id] = set of local element id
346 // Fill elt2LocalNodes[domain][position in connectivity] = local_node_id
347 for (int i = 0; i < nel; i++)
348 {
349 const int p = domain[i];
350 // local id of the element
351 const int e = eltsParition.localId[i];
352 for (int j = 0; j < nodesPerElt; j++)
353 {
354 // local id of the neighbouring node
355 const int local_node_id = nodesMapping[p].getCompactId(elt2Nodes[i * lda + j + offset]);
356 elt2LocalNodes[p][nodesPerElt * e + j] = local_node_id;
357 node2Elts[p][local_node_id].push_back(e);
358 }
359 }
360
361 // Fill Adjacency
362 for(size_t p = 0; p < nspmd; p++) //for each domain
363 {
364 const size_t nel_local = eltsParition.globalId[p].size();
365 Vint last(nel_local, -1);
366 for(size_t e1 = 0 ; e1 < nel_local ; e1++) //for each element e1
367 {
368 for(size_t j = 0 ; j < nodesPerElt ; j++) //for each node n of e1
369 {
370 const int n = elt2LocalNodes[p][nodesPerElt * e1 + j];
371 for(auto & e2 : node2Elts[p][n]) //for each element e2 connected to n
372 {
373 if(last[e2] != e1)
374 {
375 last[e2] = e1;
376 adjacencies[p].addEdge(e1, e2);
377 }
378 }
379 }
380 }
381 }
382
383 // for each domain, compute the local permutation (index)
384 // and fill the global permutation
385 int start = 0;
386 for (size_t p = 0; p < nspmd; ++p)
387 {
388 Vint index = adjacencies[p].CuthillMckee();
389 //adjacencies[p].printStats(index);
390 for (size_t i = 0; i < index.size(); ++i)
391 {
392 // permutation["new"] = "old"
393 permutation[(size_t)(start + index[i])] = 1 + eltsParition.globalId[p][i];
394 }
395 start += index.size();
396 }
397 };
398
399 // Fortran to C++ API
400 void CPP_REORDER_ELEMENTS_(int *NEL, int *NSPMD, int *NODES_PER_ELT, int *OFFSET, int *LDA, int *domain, int *elt2Nodes, int *permutation)
401 {
402 cpp_reorder_elements(NEL, NSPMD, NODES_PER_ELT, OFFSET, LDA, domain, elt2Nodes, permutation);
403 }
404 void CPP_REORDER_ELEMENTS__(int *NEL, int *NSPMD, int *NODES_PER_ELT, int *OFFSET, int *LDA, int *domain, int *elt2Nodes, int *permutation)
405 {
406 cpp_reorder_elements(NEL, NSPMD, NODES_PER_ELT, OFFSET, LDA, domain, elt2Nodes, permutation);
407 }
408 void _FCALL CPP_REORDER_ELEMENTS(int *NEL, int *NSPMD, int *NODES_PER_ELT, int *OFFSET, int *LDA, int *domain, int *elt2Nodes, int *permutation)
409 {
410 cpp_reorder_elements(NEL, NSPMD, NODES_PER_ELT, OFFSET, LDA, domain, elt2Nodes, permutation);
411 }
412 void cpp_reorder_elements_(int *NEL, int *NSPMD, int *NODES_PER_ELT, int *OFFSET, int *LDA, int *domain, int *elt2Nodes, int *permutation)
413 {
414 cpp_reorder_elements(NEL, NSPMD, NODES_PER_ELT, OFFSET, LDA, domain, elt2Nodes, permutation);
415 }
416 void cpp_reorder_elements__(int *NEL, int *NSPMD, int *NODES_PER_ELT, int *OFFSET, int *LDA, int *domain, int *elt2Nodes, int *permutation)
417 {
418 cpp_reorder_elements(NEL, NSPMD, NODES_PER_ELT, OFFSET, LDA, domain, elt2Nodes, permutation);
419 }
420}
vector< Vint > _Adj
void printStats(const Vint &perm) const
size_t pickRoot(const Vint &mask, size_t &lower_bound) const
void setNbVertices(const size_t &s)
Vint CuthillMckee() const
void printPermutation(const vector< int > &perm) const
void reserve(const size_t &s)
void addEdge(const int &a, const int &b)
size_t degree(const size_t &i) const
int getCompactId(const int &i) const
int getGlobalId(const int &i) const
unordered_map< int, int > toCompact
void addGlobalId(const int &i)
bool is_permutation(const Vint &v)
void cpp_reorder_elements__(int *NEL, int *NSPMD, int *NODES_PER_ELT, int *OFFSET, int *LDA, int *domain, int *elt2Nodes, int *permutation)
void CPP_REORDER_ELEMENTS_(int *NEL, int *NSPMD, int *NODES_PER_ELT, int *OFFSET, int *LDA, int *domain, int *elt2Nodes, int *permutation)
void splitPerDomain(int nel, int nodesPerElt, size_t nspmd, int *domain, int lda, int offset, int *elt2Nodes, Vorder &nodes, ElementPartition &elts)
void _FCALL CPP_REORDER_ELEMENTS(int *NEL, int *NSPMD, int *NODES_PER_ELT, int *OFFSET, int *LDA, int *domain, int *elt2Nodes, int *permutation)
vector< int > Vint
void CPP_REORDER_ELEMENTS__(int *NEL, int *NSPMD, int *NODES_PER_ELT, int *OFFSET, int *LDA, int *domain, int *elt2Nodes, int *permutation)
void cpp_reorder_elements_(int *NEL, int *NSPMD, int *NODES_PER_ELT, int *OFFSET, int *LDA, int *domain, int *elt2Nodes, int *permutation)
vector< NodeMapping > Vorder
void cpp_reorder_elements(int *NEL, int *NSPMD, int *NODES_PER_ELT, int *OFFSET, int *LDA, int *domain, int *elt2Nodes, int *permutation)
#define LDA
#define max(a, b)
Definition macros.h:21
#define _FCALL
static int nspmd
Definition rad2rad_c.c:126
char root[ROOTLEN]
Definition rad2rad_c.c:124
n
FILE * outfile[100]