Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
DLR-AMR
GitHub Repository: DLR-AMR/t8code
Path: blob/main/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_offset.c
915 views
1
/*
2
This file is part of t8code.
3
t8code is a C library to manage a collection (a forest) of multiple
4
connected adaptive space-trees of general element classes in parallel.
5
6
Copyright (C) 2015 the developers
7
8
t8code is free software; you can redistribute it and/or modify
9
it under the terms of the GNU General Public License as published by
10
the Free Software Foundation; either version 2 of the License, or
11
(at your option) any later version.
12
13
t8code is distributed in the hope that it will be useful,
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
GNU General Public License for more details.
17
18
You should have received a copy of the GNU General Public License
19
along with t8code; if not, write to the Free Software Foundation, Inc.,
20
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21
*/
22
23
/** \file t8_cmesh_offset.c
24
* Implementation of functions that deal with
25
* the cmesh partition offset.
26
*/
27
28
#include "t8_cmesh_offset.h"
29
30
/* Return -1 if A is smaller 0
31
* Return 0 if not. */
32
static int
33
t8_glo_kl0 (const t8_gloidx_t A)
34
{
35
return A < 0 ? -1 : 0;
36
}
37
38
/* The first tree of a given process in a partition */
39
t8_gloidx_t
40
t8_offset_first (const int proc, const t8_gloidx_t *offset)
41
{
42
T8_ASSERT (proc >= 0);
43
T8_ASSERT (offset != NULL);
44
return T8_GLOIDX_ABS (offset[proc]) + t8_glo_kl0 (offset[proc]);
45
}
46
47
/* Given the global tree id of the first local tree of a process and
48
* the flag whether it is shared or not, compute the entry in the offset array. */
49
/* This entry is the first_tree if it is not shared and
50
* -first_tree - 1 if it is shared.
51
*/
52
t8_gloidx_t
53
t8_offset_first_tree_to_entry (const t8_gloidx_t first_tree, const int shared)
54
{
55
return shared ? -first_tree - 1 : first_tree;
56
}
57
58
/* The number of trees of a given process in a partition */
59
t8_gloidx_t
60
t8_offset_num_trees (const int proc, const t8_gloidx_t *offset)
61
{
62
t8_gloidx_t num_global_trees;
63
T8_ASSERT (proc >= 0);
64
T8_ASSERT (offset != NULL);
65
66
num_global_trees = T8_GLOIDX_ABS (offset[proc + 1]) - t8_offset_first (proc, offset);
67
68
/* Fails if both offset[proc] and offset[proc+1] are -1 */
69
T8_ASSERT (num_global_trees >= 0);
70
71
return num_global_trees;
72
}
73
74
/* The last local tree of a given process in a partition */
75
t8_gloidx_t
76
t8_offset_last (const int proc, const t8_gloidx_t *offset)
77
{
78
T8_ASSERT (proc >= -1);
79
T8_ASSERT (offset != NULL);
80
81
return T8_GLOIDX_ABS (offset[proc + 1]) - 1;
82
}
83
84
#if T8_ENABLE_DEBUG
85
/* Query whether a given global tree is in a valid range of a partition */
86
static int
87
t8_offset_valid_tree (const t8_gloidx_t gtree, const int mpisize, const t8_gloidx_t *offset)
88
{
89
T8_ASSERT (offset != NULL);
90
91
return 0 <= gtree && gtree <= t8_offset_last (mpisize - 1, offset);
92
}
93
#endif
94
95
/* Return 1 if the process has no trees in the partition.
96
* Return 0 if the process has at least one tree */
97
int
98
t8_offset_empty (const int proc, const t8_gloidx_t *offset)
99
{
100
T8_ASSERT (proc >= 0);
101
T8_ASSERT (offset != NULL);
102
103
if (t8_offset_num_trees (proc, offset) <= 0) {
104
return 1;
105
}
106
return 0;
107
}
108
109
/* Find the next higher rank that is not empty.
110
* returns mpisize if this rank does not exist. */
111
int
112
t8_offset_next_nonempty_rank (const int rank, const int mpisize, const t8_gloidx_t *offset)
113
{
114
int next_nonempty = rank + 1;
115
while (next_nonempty < mpisize && t8_offset_empty (next_nonempty, offset)) {
116
next_nonempty++;
117
}
118
return next_nonempty;
119
}
120
121
#if T8_ENABLE_DEBUG
122
/* Check whether a given offset array represents a valid
123
* partition. */
124
int
125
t8_offset_consistent (const int mpisize, const t8_shmem_array_t offset_shmem, const t8_gloidx_t num_trees)
126
{
127
int i, ret = 1;
128
t8_gloidx_t last_tree;
129
T8_ASSERT (t8_shmem_array_is_initialized (offset_shmem));
130
131
const t8_gloidx_t *offset = t8_shmem_array_get_gloidx_array (offset_shmem);
132
133
ret = offset[0] == 0;
134
last_tree = t8_offset_last (0, offset); /* stores the last tree of process i-1 */
135
for (i = 1; i < mpisize && ret; i++) {
136
if (t8_offset_empty (i, offset)) {
137
/* If the process is empty, then its first tree must not be shared */
138
ret &= offset[i] >= 0;
139
}
140
else {
141
/* If the process is not empty its first local tree must be bigger or
142
* equal to the last tree of the last nonempty process.
143
* Equality must only hold, when the first tree is shared, thus offset[i] < 0 */
144
if (offset[i] < 0) {
145
ret &= t8_offset_first (i, offset) == last_tree;
146
}
147
else {
148
ret &= t8_offset_first (i, offset) > last_tree;
149
}
150
last_tree = t8_offset_last (i, offset);
151
ret &= (last_tree <= num_trees);
152
}
153
}
154
ret &= (offset[mpisize] == num_trees);
155
t8_debugf ("Offset is %s %i\n", ret ? "consistent." : "not consistent!", i - 1);
156
return ret;
157
}
158
#endif
159
160
/* Determine whether a given global tree id is in the range of a given process */
161
int
162
t8_offset_in_range (const t8_gloidx_t tree_id, const int proc, const t8_gloidx_t *offset)
163
{
164
return t8_offset_first (proc, offset) <= tree_id && tree_id <= t8_offset_last (proc, offset);
165
}
166
167
int
168
t8_offset_any_owner_of_tree_ext (const int mpisize, const int start_proc, const t8_gloidx_t gtree,
169
const t8_gloidx_t *offset)
170
{
171
int proc = start_proc;
172
int range[2] = { 0, mpisize - 1 };
173
174
range[0] = 0;
175
range[1] = mpisize - 1;
176
/* find any process that owns the tree with a binary search */
177
int found = 0;
178
while (!found) {
179
if (t8_offset_in_range (gtree, proc, offset)) {
180
found = 1;
181
break;
182
}
183
else if (t8_offset_last (proc, offset) < gtree) {
184
/* look further right */
185
range[0] = proc + 1;
186
}
187
else {
188
range[1] = proc - 1;
189
}
190
proc = (range[0] + range[1]) / 2;
191
}
192
return proc;
193
}
194
/* Find any owner of a given tree.
195
*/
196
int
197
t8_offset_any_owner_of_tree (const int mpisize, const t8_gloidx_t gtree, const t8_gloidx_t *offset)
198
{
199
return t8_offset_any_owner_of_tree_ext (mpisize, (mpisize - 1) / 2, gtree, offset);
200
}
201
202
/* Find the smallest process that owns a given tree.
203
* To increase the runtime, some_owner can be a process that
204
* already owns the tree. Otherwise (some_owner < 0), the function will compute one. */
205
int
206
t8_offset_first_owner_of_tree (const int mpisize, const t8_gloidx_t gtree, const t8_gloidx_t *offset, int *some_owner)
207
{
208
int proc, proc_temp;
209
210
T8_ASSERT (t8_offset_valid_tree (gtree, mpisize, offset));
211
if (*some_owner < 0) {
212
*some_owner = t8_offset_any_owner_of_tree (mpisize, gtree, offset);
213
}
214
T8_ASSERT (*some_owner < mpisize);
215
proc = *some_owner;
216
/* Here, proc stores an arbitrary owner of gtree */
217
/* Now we find the smallest process that owns the tree */
218
proc_temp = proc;
219
while (proc_temp >= 0 && t8_offset_in_range (gtree, proc_temp, offset)) {
220
/* decrement temp as long as it holds the tree */
221
proc_temp--;
222
while (0 <= proc_temp && t8_offset_empty (proc_temp, offset)) {
223
/* Skip empty processes */
224
proc_temp--;
225
}
226
}
227
/* We are now one nonempty process below the smallest process having the tree */
228
if (proc_temp >= -1) {
229
proc_temp++;
230
while (t8_offset_empty (proc_temp, offset)) {
231
/* Skip empty processes */
232
proc_temp++;
233
}
234
T8_ASSERT (t8_offset_in_range (gtree, proc_temp, offset));
235
}
236
else {
237
/* This should never happen */
238
SC_ABORT ("ERROR: proc_temp ran out of bounds");
239
}
240
proc = proc_temp;
241
return proc;
242
}
243
244
static int
245
t8_offset_next_prev_owner_of_tree (const int mpisize, const t8_gloidx_t gtree, const t8_gloidx_t *offset,
246
const int current_owner, const int search_dir)
247
{
248
int proc;
249
250
int search_left_or_right = search_dir > 0 ? 1 : -1; /* Adjust search dir. Positive means next process,
251
negative previous. */
252
proc = current_owner + search_left_or_right;
253
while (proc >= 0 && proc < mpisize && t8_offset_empty (proc, offset)) {
254
/* Skip empty processes */
255
proc += search_left_or_right;
256
}
257
if (proc >= 0 && proc < mpisize && t8_offset_in_range (gtree, proc, offset)) {
258
/* proc is still in the range and it owns gtree */
259
return proc;
260
}
261
/* Either proc is greater than mpisize or smaller 0 or it does not
262
* own gtree any more. */
263
return -1;
264
}
265
266
/* Given a process current_owner that has the local tree gtree,
267
* find the next bigger rank that also has this tree.
268
* If none is found, we return -1.
269
*/
270
int
271
t8_offset_next_owner_of_tree (const int mpisize, const t8_gloidx_t gtree, const t8_gloidx_t *offset,
272
const int current_owner)
273
{
274
return t8_offset_next_prev_owner_of_tree (mpisize, gtree, offset, current_owner, +1);
275
}
276
277
/* Given a process current_owner that has the local tree gtree,
278
* find the next smaller rank that also has this tree.
279
* If none is found, we return -1.
280
*/
281
int
282
t8_offset_prev_owner_of_tree (const int mpisize, const t8_gloidx_t gtree, const t8_gloidx_t *offset,
283
const int current_owner)
284
{
285
return t8_offset_next_prev_owner_of_tree (mpisize, gtree, offset, current_owner, -1);
286
}
287
288
/* Find the biggest process that owns a given tree.
289
* To increase the runtime, some_owner can be a process that
290
* already owns the tree. Otherwise (some_owner < 0), the function will compute one. */
291
int
292
t8_offset_last_owner_of_tree (const int mpisize, const t8_gloidx_t gtree, const t8_gloidx_t *offset, int *some_owner)
293
{
294
int proc, proc_temp;
295
296
T8_ASSERT (t8_offset_valid_tree (gtree, mpisize, offset));
297
if (*some_owner < 0) {
298
*some_owner = t8_offset_any_owner_of_tree (mpisize, gtree, offset);
299
}
300
T8_ASSERT (*some_owner < mpisize);
301
proc = *some_owner;
302
/* Here, proc stores an arbitrary owner of gtree */
303
/* Now we find the smallest process that owns the tree */
304
proc_temp = proc;
305
while (proc_temp < mpisize && t8_offset_in_range (gtree, proc_temp, offset)) {
306
/* increment temp as long as it holds the tree */
307
proc_temp++;
308
while (proc_temp < mpisize && t8_offset_empty (proc_temp, offset)) {
309
/* Skip empty processes */
310
proc_temp++;
311
}
312
}
313
/* We are now one nonempty process above the biggest process having the tree */
314
if (proc_temp <= mpisize) {
315
proc_temp--;
316
while (t8_offset_empty (proc_temp, offset)) {
317
/* Skip empty processes */
318
proc_temp--;
319
}
320
T8_ASSERT (t8_offset_in_range (gtree, proc_temp, offset));
321
}
322
else {
323
/* This should never happen */
324
SC_ABORT ("ERROR: proc_temp ran out of bounds");
325
}
326
proc = proc_temp;
327
return proc;
328
}
329
330
/* Compute a list of all processes that own a specific tree */
331
/* Owners must be an initialized sc_array with int elements and
332
* element count 0 */
333
void
334
t8_offset_all_owners_of_tree (const int mpisize, const t8_gloidx_t gtree, const t8_gloidx_t *offset, sc_array_t *owners)
335
{
336
int proc;
337
int *entry;
338
int some_owner = -1;
339
T8_ASSERT (owners != NULL);
340
T8_ASSERT (owners->elem_count == 0);
341
T8_ASSERT (owners->elem_size == sizeof (int));
342
T8_ASSERT (t8_offset_valid_tree (gtree, mpisize, offset));
343
344
/* Find the smallest process that has the tree */
345
proc = t8_offset_first_owner_of_tree (mpisize, gtree, offset, &some_owner);
346
/* Add the first process to the array */
347
entry = (int *) sc_array_push (owners);
348
*entry = proc;
349
/* Now we parse through all processes bigger than the first one until
350
* they do not have the tree anymore. */
351
while (proc >= 0) {
352
/* Find the next owner of gtree */
353
proc = t8_offset_next_owner_of_tree (mpisize, gtree, offset, proc);
354
if (proc >= 0) {
355
/* If we found one, we add it to the array */
356
/* Otherwise we found all owners */
357
entry = (int *) sc_array_push (owners);
358
*entry = proc;
359
}
360
}
361
}
362
363
/* Return 1 if the process will not send any trees, that is if it is
364
* empty or has only one shared tree. */
365
int
366
t8_offset_nosend (const int proc, const int mpisize, const t8_gloidx_t *offset_from, const t8_gloidx_t *offset_to)
367
{
368
t8_gloidx_t num_trees;
369
370
num_trees = t8_offset_num_trees (proc, offset_from);
371
if (t8_offset_empty (proc, offset_from)) {
372
return 1;
373
}
374
else if (num_trees <= 2) {
375
int first_not_send, last_not_send = 0;
376
/* We only have one or two trees */
377
/* The first tree is not send if it is shared and will not be on
378
* this process in the new partition */
379
first_not_send
380
= offset_from[proc] < 0 && !t8_offset_in_range (t8_offset_first (proc, offset_from), proc, offset_to);
381
382
if ((first_not_send && num_trees == 2) || (!first_not_send && num_trees == 1)) {
383
int temp_proc;
384
t8_gloidx_t last_tree = t8_offset_last (proc, offset_from);
385
sc_array_t receivers;
386
/* It could be that our last tree is not send either, this is the case
387
* if all processes in the new partition that have it, already have it shared
388
* in the current partition. */
389
/* At first we find out if last tree is shared by anybody, since if not
390
* we do not need to find all receivers. */
391
if (t8_offset_in_range (last_tree, proc, offset_to)) {
392
/* We keep our last tree, thus it is send by us */
393
return 0;
394
}
395
396
temp_proc = proc + 1;
397
/* Find next nonempty process */
398
while (temp_proc < mpisize && t8_offset_empty (temp_proc, offset_from)) {
399
temp_proc++;
400
}
401
if (temp_proc >= mpisize || t8_offset_first (temp_proc, offset_from) != last_tree) {
402
/* Our last tree is unique and thus we need to send it */
403
return 0;
404
}
405
sc_array_init (&receivers, sizeof (int));
406
/* Now we need to find out all process in the new partition that have last_tree
407
* and check if any of them did not have it before. */
408
t8_offset_all_owners_of_tree (mpisize, last_tree, offset_to, &receivers);
409
for (size_t ireceiver = 0; ireceiver < receivers.elem_count; ireceiver++) {
410
temp_proc = *(int *) sc_array_index (&receivers, ireceiver);
411
if (!t8_offset_in_range (last_tree, temp_proc, offset_from)) {
412
/* We found at least one process that needs our last tree */
413
sc_array_reset (&receivers);
414
return 0;
415
}
416
}
417
sc_array_reset (&receivers);
418
last_not_send = 1;
419
}
420
if (num_trees - first_not_send - last_not_send <= 0) {
421
/* We only have one tree, it is shared and we do not keep it.
422
* Or we have only two trees and both are also on other procs and only
423
* persist on these procs */
424
return 1;
425
}
426
}
427
return 0;
428
}
429
430
/* Return one if proca sends trees to procb when partitioning from
431
* offset_from to offset_to */
432
int
433
t8_offset_sendsto (const int proca, const int procb, const t8_gloidx_t *t8_offset_from, const t8_gloidx_t *t8_offset_to)
434
{
435
t8_gloidx_t proca_first, proca_last;
436
t8_gloidx_t procb_first, procb_last;
437
int keeps_first;
438
439
T8_ASSERT (t8_offset_from != NULL && t8_offset_to != NULL);
440
/* proca sends to procb if proca's first tree (plus 1 if it is shared)
441
* is smaller than procb's last tree and
442
* proca's last tree is bigger than procb's first tree
443
* and proca has trees to send */
444
/* true if procb will keep its first tree and it is shared */
445
if (t8_offset_empty (proca, t8_offset_from) || t8_offset_empty (procb, t8_offset_to)) {
446
/* If the sender or the receiver is empty we do not send anything. */
447
return 0;
448
}
449
keeps_first = t8_offset_from[procb] < 0
450
&& t8_offset_in_range (t8_offset_first (procb, t8_offset_from), procb, t8_offset_to)
451
&& !t8_offset_empty (procb, t8_offset_from);
452
if (proca == procb && keeps_first) {
453
/* If proca = procb and the first tree is kept, we definitely send */
454
return 1;
455
}
456
proca_first = t8_offset_first (proca, t8_offset_from) + (t8_offset_from[proca] < 0);
457
proca_last = t8_offset_last (proca, t8_offset_from);
458
procb_first = t8_offset_first (procb, t8_offset_to);
459
procb_last = t8_offset_last (procb, t8_offset_to);
460
if (keeps_first && proca_last == t8_offset_first (procb, t8_offset_from)) {
461
proca_last--;
462
}
463
if (proca_first <= proca_last && /* There are trees to send and... */
464
proca_first <= procb_last /* The first tree on a before is smaller than
465
* the last on b after partitioning and... */
466
&& proca_last >= procb_first /* The last tree on a before is bigger than */
467
+ (keeps_first /* the first on b after partitioning */
468
&& procb_first == t8_offset_first (procb, t8_offset_from))) {
469
return 1;
470
}
471
return 0;
472
}
473
474
/* Determine whether a process A will send a tree T to a process B.
475
*/
476
int
477
t8_offset_sendstree (const int proc_send, const int proc_to, const t8_gloidx_t gtree, const t8_gloidx_t *offset_from,
478
const t8_gloidx_t *offset_to)
479
{
480
/* If the tree is not the last tree on proc_send it is send if
481
* first_tree <= tree <= last_tree on the new partition for process proc_to.
482
* If the tree is the last tree is is send if the same condition holds and
483
* it is not already the first tree of proc_to in the old partition */
484
if (!t8_offset_in_range (gtree, proc_send, offset_from)) {
485
/* The tree is not in proc_send's part of the partition */
486
return 0;
487
}
488
489
if (!t8_offset_in_range (gtree, proc_to, offset_to)) {
490
/* The tree will not be in proc_to's part of the new partition */
491
return 0;
492
}
493
if (!t8_offset_empty (proc_to, offset_from) && gtree == t8_offset_first (proc_to, offset_from)
494
&& proc_send != proc_to) {
495
/* The tree is already a tree of proc_to. This catches the case where
496
* tree is shared between proc_send and proc_to. */
497
return 0;
498
}
499
if (proc_send != proc_to && gtree == t8_offset_first (proc_send, offset_from) && offset_from[proc_send] < 0) {
500
/* proc_send is not proc_to and tree is the first of proc send and shared
501
* then a process smaller then proc_send will send tree */
502
return 0;
503
}
504
/* The tree is on proc_send and will be on proc_to.
505
* If proc_send is not proc_to then
506
* tree is not shared by a smaller process than proc_send. */
507
return 1;
508
}
509
510
/* Count the number of sending procs from start to end sending to mpirank
511
* A process counts as sending if it has at least one non-shared local tree */
512
int
513
t8_offset_range_send (const int start, const int end, const int mpirank, const t8_gloidx_t *offset_from,
514
const t8_gloidx_t *offset_to)
515
{
516
int count = 0, i;
517
518
for (i = start; i <= end; i++) {
519
if (t8_offset_sendsto (i, mpirank, offset_from, offset_to)) {
520
count++;
521
}
522
}
523
return count;
524
}
525
526
void
527
t8_offset_print (__attribute__ ((unused)) const t8_shmem_array_t offset, __attribute__ ((unused)) sc_MPI_Comm comm)
528
{
529
#if T8_ENABLE_DEBUG
530
char buf[BUFSIZ] = "| ";
531
int i, mpiret, mpisize;
532
533
if (offset == NULL) {
534
t8_debugf ("Offsets = NULL\n");
535
return;
536
}
537
538
mpiret = sc_MPI_Comm_size (comm, &mpisize);
539
SC_CHECK_MPI (mpiret);
540
for (i = 0; i <= mpisize; i++) {
541
snprintf (buf + strlen (buf), BUFSIZ - strlen (buf), " % lli |", (long long) t8_shmem_array_get_gloidx (offset, i));
542
}
543
t8_debugf ("Offsets = %s\n", buf);
544
#endif
545
}
546
547