Path: blob/main/src/t8_cmesh/t8_cmesh_internal/t8_cmesh_offset.c
915 views
/*1This file is part of t8code.2t8code is a C library to manage a collection (a forest) of multiple3connected adaptive space-trees of general element classes in parallel.45Copyright (C) 2015 the developers67t8code is free software; you can redistribute it and/or modify8it under the terms of the GNU General Public License as published by9the Free Software Foundation; either version 2 of the License, or10(at your option) any later version.1112t8code is distributed in the hope that it will be useful,13but WITHOUT ANY WARRANTY; without even the implied warranty of14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the15GNU General Public License for more details.1617You should have received a copy of the GNU General Public License18along with t8code; if not, write to the Free Software Foundation, Inc.,1951 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.20*/2122/** \file t8_cmesh_offset.c23* Implementation of functions that deal with24* the cmesh partition offset.25*/2627#include "t8_cmesh_offset.h"2829/* Return -1 if A is smaller 030* Return 0 if not. */31static int32t8_glo_kl0 (const t8_gloidx_t A)33{34return A < 0 ? -1 : 0;35}3637/* The first tree of a given process in a partition */38t8_gloidx_t39t8_offset_first (const int proc, const t8_gloidx_t *offset)40{41T8_ASSERT (proc >= 0);42T8_ASSERT (offset != NULL);43return T8_GLOIDX_ABS (offset[proc]) + t8_glo_kl0 (offset[proc]);44}4546/* Given the global tree id of the first local tree of a process and47* the flag whether it is shared or not, compute the entry in the offset array. */48/* This entry is the first_tree if it is not shared and49* -first_tree - 1 if it is shared.50*/51t8_gloidx_t52t8_offset_first_tree_to_entry (const t8_gloidx_t first_tree, const int shared)53{54return shared ? -first_tree - 1 : first_tree;55}5657/* The number of trees of a given process in a partition */58t8_gloidx_t59t8_offset_num_trees (const int proc, const t8_gloidx_t *offset)60{61t8_gloidx_t num_global_trees;62T8_ASSERT (proc >= 0);63T8_ASSERT (offset != NULL);6465num_global_trees = T8_GLOIDX_ABS (offset[proc + 1]) - t8_offset_first (proc, offset);6667/* Fails if both offset[proc] and offset[proc+1] are -1 */68T8_ASSERT (num_global_trees >= 0);6970return num_global_trees;71}7273/* The last local tree of a given process in a partition */74t8_gloidx_t75t8_offset_last (const int proc, const t8_gloidx_t *offset)76{77T8_ASSERT (proc >= -1);78T8_ASSERT (offset != NULL);7980return T8_GLOIDX_ABS (offset[proc + 1]) - 1;81}8283#if T8_ENABLE_DEBUG84/* Query whether a given global tree is in a valid range of a partition */85static int86t8_offset_valid_tree (const t8_gloidx_t gtree, const int mpisize, const t8_gloidx_t *offset)87{88T8_ASSERT (offset != NULL);8990return 0 <= gtree && gtree <= t8_offset_last (mpisize - 1, offset);91}92#endif9394/* Return 1 if the process has no trees in the partition.95* Return 0 if the process has at least one tree */96int97t8_offset_empty (const int proc, const t8_gloidx_t *offset)98{99T8_ASSERT (proc >= 0);100T8_ASSERT (offset != NULL);101102if (t8_offset_num_trees (proc, offset) <= 0) {103return 1;104}105return 0;106}107108/* Find the next higher rank that is not empty.109* returns mpisize if this rank does not exist. */110int111t8_offset_next_nonempty_rank (const int rank, const int mpisize, const t8_gloidx_t *offset)112{113int next_nonempty = rank + 1;114while (next_nonempty < mpisize && t8_offset_empty (next_nonempty, offset)) {115next_nonempty++;116}117return next_nonempty;118}119120#if T8_ENABLE_DEBUG121/* Check whether a given offset array represents a valid122* partition. */123int124t8_offset_consistent (const int mpisize, const t8_shmem_array_t offset_shmem, const t8_gloidx_t num_trees)125{126int i, ret = 1;127t8_gloidx_t last_tree;128T8_ASSERT (t8_shmem_array_is_initialized (offset_shmem));129130const t8_gloidx_t *offset = t8_shmem_array_get_gloidx_array (offset_shmem);131132ret = offset[0] == 0;133last_tree = t8_offset_last (0, offset); /* stores the last tree of process i-1 */134for (i = 1; i < mpisize && ret; i++) {135if (t8_offset_empty (i, offset)) {136/* If the process is empty, then its first tree must not be shared */137ret &= offset[i] >= 0;138}139else {140/* If the process is not empty its first local tree must be bigger or141* equal to the last tree of the last nonempty process.142* Equality must only hold, when the first tree is shared, thus offset[i] < 0 */143if (offset[i] < 0) {144ret &= t8_offset_first (i, offset) == last_tree;145}146else {147ret &= t8_offset_first (i, offset) > last_tree;148}149last_tree = t8_offset_last (i, offset);150ret &= (last_tree <= num_trees);151}152}153ret &= (offset[mpisize] == num_trees);154t8_debugf ("Offset is %s %i\n", ret ? "consistent." : "not consistent!", i - 1);155return ret;156}157#endif158159/* Determine whether a given global tree id is in the range of a given process */160int161t8_offset_in_range (const t8_gloidx_t tree_id, const int proc, const t8_gloidx_t *offset)162{163return t8_offset_first (proc, offset) <= tree_id && tree_id <= t8_offset_last (proc, offset);164}165166int167t8_offset_any_owner_of_tree_ext (const int mpisize, const int start_proc, const t8_gloidx_t gtree,168const t8_gloidx_t *offset)169{170int proc = start_proc;171int range[2] = { 0, mpisize - 1 };172173range[0] = 0;174range[1] = mpisize - 1;175/* find any process that owns the tree with a binary search */176int found = 0;177while (!found) {178if (t8_offset_in_range (gtree, proc, offset)) {179found = 1;180break;181}182else if (t8_offset_last (proc, offset) < gtree) {183/* look further right */184range[0] = proc + 1;185}186else {187range[1] = proc - 1;188}189proc = (range[0] + range[1]) / 2;190}191return proc;192}193/* Find any owner of a given tree.194*/195int196t8_offset_any_owner_of_tree (const int mpisize, const t8_gloidx_t gtree, const t8_gloidx_t *offset)197{198return t8_offset_any_owner_of_tree_ext (mpisize, (mpisize - 1) / 2, gtree, offset);199}200201/* Find the smallest process that owns a given tree.202* To increase the runtime, some_owner can be a process that203* already owns the tree. Otherwise (some_owner < 0), the function will compute one. */204int205t8_offset_first_owner_of_tree (const int mpisize, const t8_gloidx_t gtree, const t8_gloidx_t *offset, int *some_owner)206{207int proc, proc_temp;208209T8_ASSERT (t8_offset_valid_tree (gtree, mpisize, offset));210if (*some_owner < 0) {211*some_owner = t8_offset_any_owner_of_tree (mpisize, gtree, offset);212}213T8_ASSERT (*some_owner < mpisize);214proc = *some_owner;215/* Here, proc stores an arbitrary owner of gtree */216/* Now we find the smallest process that owns the tree */217proc_temp = proc;218while (proc_temp >= 0 && t8_offset_in_range (gtree, proc_temp, offset)) {219/* decrement temp as long as it holds the tree */220proc_temp--;221while (0 <= proc_temp && t8_offset_empty (proc_temp, offset)) {222/* Skip empty processes */223proc_temp--;224}225}226/* We are now one nonempty process below the smallest process having the tree */227if (proc_temp >= -1) {228proc_temp++;229while (t8_offset_empty (proc_temp, offset)) {230/* Skip empty processes */231proc_temp++;232}233T8_ASSERT (t8_offset_in_range (gtree, proc_temp, offset));234}235else {236/* This should never happen */237SC_ABORT ("ERROR: proc_temp ran out of bounds");238}239proc = proc_temp;240return proc;241}242243static int244t8_offset_next_prev_owner_of_tree (const int mpisize, const t8_gloidx_t gtree, const t8_gloidx_t *offset,245const int current_owner, const int search_dir)246{247int proc;248249int search_left_or_right = search_dir > 0 ? 1 : -1; /* Adjust search dir. Positive means next process,250negative previous. */251proc = current_owner + search_left_or_right;252while (proc >= 0 && proc < mpisize && t8_offset_empty (proc, offset)) {253/* Skip empty processes */254proc += search_left_or_right;255}256if (proc >= 0 && proc < mpisize && t8_offset_in_range (gtree, proc, offset)) {257/* proc is still in the range and it owns gtree */258return proc;259}260/* Either proc is greater than mpisize or smaller 0 or it does not261* own gtree any more. */262return -1;263}264265/* Given a process current_owner that has the local tree gtree,266* find the next bigger rank that also has this tree.267* If none is found, we return -1.268*/269int270t8_offset_next_owner_of_tree (const int mpisize, const t8_gloidx_t gtree, const t8_gloidx_t *offset,271const int current_owner)272{273return t8_offset_next_prev_owner_of_tree (mpisize, gtree, offset, current_owner, +1);274}275276/* Given a process current_owner that has the local tree gtree,277* find the next smaller rank that also has this tree.278* If none is found, we return -1.279*/280int281t8_offset_prev_owner_of_tree (const int mpisize, const t8_gloidx_t gtree, const t8_gloidx_t *offset,282const int current_owner)283{284return t8_offset_next_prev_owner_of_tree (mpisize, gtree, offset, current_owner, -1);285}286287/* Find the biggest process that owns a given tree.288* To increase the runtime, some_owner can be a process that289* already owns the tree. Otherwise (some_owner < 0), the function will compute one. */290int291t8_offset_last_owner_of_tree (const int mpisize, const t8_gloidx_t gtree, const t8_gloidx_t *offset, int *some_owner)292{293int proc, proc_temp;294295T8_ASSERT (t8_offset_valid_tree (gtree, mpisize, offset));296if (*some_owner < 0) {297*some_owner = t8_offset_any_owner_of_tree (mpisize, gtree, offset);298}299T8_ASSERT (*some_owner < mpisize);300proc = *some_owner;301/* Here, proc stores an arbitrary owner of gtree */302/* Now we find the smallest process that owns the tree */303proc_temp = proc;304while (proc_temp < mpisize && t8_offset_in_range (gtree, proc_temp, offset)) {305/* increment temp as long as it holds the tree */306proc_temp++;307while (proc_temp < mpisize && t8_offset_empty (proc_temp, offset)) {308/* Skip empty processes */309proc_temp++;310}311}312/* We are now one nonempty process above the biggest process having the tree */313if (proc_temp <= mpisize) {314proc_temp--;315while (t8_offset_empty (proc_temp, offset)) {316/* Skip empty processes */317proc_temp--;318}319T8_ASSERT (t8_offset_in_range (gtree, proc_temp, offset));320}321else {322/* This should never happen */323SC_ABORT ("ERROR: proc_temp ran out of bounds");324}325proc = proc_temp;326return proc;327}328329/* Compute a list of all processes that own a specific tree */330/* Owners must be an initialized sc_array with int elements and331* element count 0 */332void333t8_offset_all_owners_of_tree (const int mpisize, const t8_gloidx_t gtree, const t8_gloidx_t *offset, sc_array_t *owners)334{335int proc;336int *entry;337int some_owner = -1;338T8_ASSERT (owners != NULL);339T8_ASSERT (owners->elem_count == 0);340T8_ASSERT (owners->elem_size == sizeof (int));341T8_ASSERT (t8_offset_valid_tree (gtree, mpisize, offset));342343/* Find the smallest process that has the tree */344proc = t8_offset_first_owner_of_tree (mpisize, gtree, offset, &some_owner);345/* Add the first process to the array */346entry = (int *) sc_array_push (owners);347*entry = proc;348/* Now we parse through all processes bigger than the first one until349* they do not have the tree anymore. */350while (proc >= 0) {351/* Find the next owner of gtree */352proc = t8_offset_next_owner_of_tree (mpisize, gtree, offset, proc);353if (proc >= 0) {354/* If we found one, we add it to the array */355/* Otherwise we found all owners */356entry = (int *) sc_array_push (owners);357*entry = proc;358}359}360}361362/* Return 1 if the process will not send any trees, that is if it is363* empty or has only one shared tree. */364int365t8_offset_nosend (const int proc, const int mpisize, const t8_gloidx_t *offset_from, const t8_gloidx_t *offset_to)366{367t8_gloidx_t num_trees;368369num_trees = t8_offset_num_trees (proc, offset_from);370if (t8_offset_empty (proc, offset_from)) {371return 1;372}373else if (num_trees <= 2) {374int first_not_send, last_not_send = 0;375/* We only have one or two trees */376/* The first tree is not send if it is shared and will not be on377* this process in the new partition */378first_not_send379= offset_from[proc] < 0 && !t8_offset_in_range (t8_offset_first (proc, offset_from), proc, offset_to);380381if ((first_not_send && num_trees == 2) || (!first_not_send && num_trees == 1)) {382int temp_proc;383t8_gloidx_t last_tree = t8_offset_last (proc, offset_from);384sc_array_t receivers;385/* It could be that our last tree is not send either, this is the case386* if all processes in the new partition that have it, already have it shared387* in the current partition. */388/* At first we find out if last tree is shared by anybody, since if not389* we do not need to find all receivers. */390if (t8_offset_in_range (last_tree, proc, offset_to)) {391/* We keep our last tree, thus it is send by us */392return 0;393}394395temp_proc = proc + 1;396/* Find next nonempty process */397while (temp_proc < mpisize && t8_offset_empty (temp_proc, offset_from)) {398temp_proc++;399}400if (temp_proc >= mpisize || t8_offset_first (temp_proc, offset_from) != last_tree) {401/* Our last tree is unique and thus we need to send it */402return 0;403}404sc_array_init (&receivers, sizeof (int));405/* Now we need to find out all process in the new partition that have last_tree406* and check if any of them did not have it before. */407t8_offset_all_owners_of_tree (mpisize, last_tree, offset_to, &receivers);408for (size_t ireceiver = 0; ireceiver < receivers.elem_count; ireceiver++) {409temp_proc = *(int *) sc_array_index (&receivers, ireceiver);410if (!t8_offset_in_range (last_tree, temp_proc, offset_from)) {411/* We found at least one process that needs our last tree */412sc_array_reset (&receivers);413return 0;414}415}416sc_array_reset (&receivers);417last_not_send = 1;418}419if (num_trees - first_not_send - last_not_send <= 0) {420/* We only have one tree, it is shared and we do not keep it.421* Or we have only two trees and both are also on other procs and only422* persist on these procs */423return 1;424}425}426return 0;427}428429/* Return one if proca sends trees to procb when partitioning from430* offset_from to offset_to */431int432t8_offset_sendsto (const int proca, const int procb, const t8_gloidx_t *t8_offset_from, const t8_gloidx_t *t8_offset_to)433{434t8_gloidx_t proca_first, proca_last;435t8_gloidx_t procb_first, procb_last;436int keeps_first;437438T8_ASSERT (t8_offset_from != NULL && t8_offset_to != NULL);439/* proca sends to procb if proca's first tree (plus 1 if it is shared)440* is smaller than procb's last tree and441* proca's last tree is bigger than procb's first tree442* and proca has trees to send */443/* true if procb will keep its first tree and it is shared */444if (t8_offset_empty (proca, t8_offset_from) || t8_offset_empty (procb, t8_offset_to)) {445/* If the sender or the receiver is empty we do not send anything. */446return 0;447}448keeps_first = t8_offset_from[procb] < 0449&& t8_offset_in_range (t8_offset_first (procb, t8_offset_from), procb, t8_offset_to)450&& !t8_offset_empty (procb, t8_offset_from);451if (proca == procb && keeps_first) {452/* If proca = procb and the first tree is kept, we definitely send */453return 1;454}455proca_first = t8_offset_first (proca, t8_offset_from) + (t8_offset_from[proca] < 0);456proca_last = t8_offset_last (proca, t8_offset_from);457procb_first = t8_offset_first (procb, t8_offset_to);458procb_last = t8_offset_last (procb, t8_offset_to);459if (keeps_first && proca_last == t8_offset_first (procb, t8_offset_from)) {460proca_last--;461}462if (proca_first <= proca_last && /* There are trees to send and... */463proca_first <= procb_last /* The first tree on a before is smaller than464* the last on b after partitioning and... */465&& proca_last >= procb_first /* The last tree on a before is bigger than */466+ (keeps_first /* the first on b after partitioning */467&& procb_first == t8_offset_first (procb, t8_offset_from))) {468return 1;469}470return 0;471}472473/* Determine whether a process A will send a tree T to a process B.474*/475int476t8_offset_sendstree (const int proc_send, const int proc_to, const t8_gloidx_t gtree, const t8_gloidx_t *offset_from,477const t8_gloidx_t *offset_to)478{479/* If the tree is not the last tree on proc_send it is send if480* first_tree <= tree <= last_tree on the new partition for process proc_to.481* If the tree is the last tree is is send if the same condition holds and482* it is not already the first tree of proc_to in the old partition */483if (!t8_offset_in_range (gtree, proc_send, offset_from)) {484/* The tree is not in proc_send's part of the partition */485return 0;486}487488if (!t8_offset_in_range (gtree, proc_to, offset_to)) {489/* The tree will not be in proc_to's part of the new partition */490return 0;491}492if (!t8_offset_empty (proc_to, offset_from) && gtree == t8_offset_first (proc_to, offset_from)493&& proc_send != proc_to) {494/* The tree is already a tree of proc_to. This catches the case where495* tree is shared between proc_send and proc_to. */496return 0;497}498if (proc_send != proc_to && gtree == t8_offset_first (proc_send, offset_from) && offset_from[proc_send] < 0) {499/* proc_send is not proc_to and tree is the first of proc send and shared500* then a process smaller then proc_send will send tree */501return 0;502}503/* The tree is on proc_send and will be on proc_to.504* If proc_send is not proc_to then505* tree is not shared by a smaller process than proc_send. */506return 1;507}508509/* Count the number of sending procs from start to end sending to mpirank510* A process counts as sending if it has at least one non-shared local tree */511int512t8_offset_range_send (const int start, const int end, const int mpirank, const t8_gloidx_t *offset_from,513const t8_gloidx_t *offset_to)514{515int count = 0, i;516517for (i = start; i <= end; i++) {518if (t8_offset_sendsto (i, mpirank, offset_from, offset_to)) {519count++;520}521}522return count;523}524525void526t8_offset_print (__attribute__ ((unused)) const t8_shmem_array_t offset, __attribute__ ((unused)) sc_MPI_Comm comm)527{528#if T8_ENABLE_DEBUG529char buf[BUFSIZ] = "| ";530int i, mpiret, mpisize;531532if (offset == NULL) {533t8_debugf ("Offsets = NULL\n");534return;535}536537mpiret = sc_MPI_Comm_size (comm, &mpisize);538SC_CHECK_MPI (mpiret);539for (i = 0; i <= mpisize; i++) {540snprintf (buf + strlen (buf), BUFSIZ - strlen (buf), " % lli |", (long long) t8_shmem_array_get_gloidx (offset, i));541}542t8_debugf ("Offsets = %s\n", buf);543#endif544}545546547