// Version: $Id: 7224cbf4eb63db8a22aa76381cdf6d06c632d539 $ // // // Commentary: // // // Change Log: // // // Code: #include #include "dtkDistributedGraphTopology.h" #if defined(DTK_HAVE_ZLIB) #include #endif // ///////////////////////////////////////////////////////////////// // Helper function // ///////////////////////////////////////////////////////////////// void dtkDistributedOrderingListInsertion(std::set& l, qlonglong id); void dtkDistributedSortListUsingMap(const std::set& l, const QMap& m, QList& res); bool dtkDistributedGraphTopologyPopulateMap(QMap >& map, qlonglong from, qlonglong to); // ///////////////////////////////////////////////////////////////// // dtkDistributedGraphTopology implementation // ///////////////////////////////////////////////////////////////// dtkDistributedGraphTopology::dtkDistributedGraphTopology(void) : dtkDistributedContainer(), m_is_assembled(false), m_edge_count(NULL), m_neighbour_count(NULL), m_vertex_to_edge(NULL), m_edge_to_vertex(NULL) { m_dd.mapper = NULL; m_dd.positions = NULL; m_comm->barrier(); } dtkDistributedGraphTopology::dtkDistributedGraphTopology(const qlonglong& vertex_count) : dtkDistributedContainer(vertex_count), m_is_assembled(false), m_edge_count(NULL), m_neighbour_count(NULL), m_vertex_to_edge(NULL), m_edge_to_vertex(NULL) { m_dd.mapper = NULL; m_dd.positions = NULL; this->initialize(); m_comm->barrier(); } dtkDistributedGraphTopology::dtkDistributedGraphTopology(const qlonglong& vertex_count, dtkDistributedMapper *mapper) : dtkDistributedContainer(vertex_count, mapper), m_is_assembled(false), m_edge_count(NULL), m_neighbour_count(NULL), m_vertex_to_edge(NULL), m_edge_to_vertex(NULL) { m_dd.mapper = NULL; m_dd.positions = NULL; this->initialize(); m_comm->barrier(); } dtkDistributedGraphTopology::dtkDistributedGraphTopology(const dtkDistributedGraphTopology& o) : dtkDistributedContainer(o.m_size), m_is_assembled(o.m_is_assembled), m_edge_count(NULL), m_neighbour_count(NULL), m_vertex_to_edge(NULL), m_edge_to_vertex(NULL) { m_dd.mapper = NULL; m_dd.positions = NULL; m_comm->barrier(); dtkWarn() << Q_FUNC_INFO << "Copy ctor not implemented. It is not clear it is necessary."; } dtkDistributedGraphTopology::~dtkDistributedGraphTopology(void) { // if (!m_is_assembled && m_edge_count) // m_edge_count->unlock(this->wid()); this->clear(); } dtkDistributedGraphTopology& dtkDistributedGraphTopology::operator = (const dtkDistributedGraphTopology&) { dtkWarn() << Q_FUNC_INFO << "Copy assignment not implemented. It is not clear it is necessary."; return *this; } void dtkDistributedGraphTopology::clear(void) { m_comm->barrier(); m_size = 0LL; m_is_assembled = false; m_mapper->clear(); if (m_neighbour_count) { delete m_neighbour_count; } if (m_edge_count) delete m_edge_count; if (m_vertex_to_edge) delete m_vertex_to_edge; if (m_edge_to_vertex) delete m_edge_to_vertex; if (m_dd.mapper && !m_dd.mapper->deref()) delete m_dd.mapper; if (m_dd.positions) delete m_dd.positions; m_edge_count = NULL; m_neighbour_count = NULL; m_vertex_to_edge = NULL; m_edge_to_vertex = NULL; m_dd.mapper = NULL; m_dd.positions = NULL; m_comm->barrier(); } void dtkDistributedGraphTopology::resize(qlonglong vertex_count) { if (m_size == vertex_count) return; this->clear(); m_size = vertex_count; m_mapper->setMapping(vertexCount(), m_comm->size()); this->initialize(); } void dtkDistributedGraphTopology::initialize(void) { dtkDebug() << "initialize graph with" << vertexCount() << "vertexes, rank =" << this->wid(); dtkDistributedMapper *mapper = new dtkDistributedMapper(*m_mapper); mapper->setMap(vertexCount() + 1, m_comm->size()); m_vertex_to_edge = new dtkDistributedArray(vertexCount() + 1, mapper); m_vertex_to_edge->fill(0); m_neighbour_count = new dtkDistributedArray(vertexCount() + 1, mapper); m_neighbour_count->fill(0); m_edge_count = new dtkDistributedArray(m_comm->size()); m_edge_count->fill(0); } void dtkDistributedGraphTopology::addEdge(qlonglong from, qlonglong to) { qint32 wid = this->wid(); qint32 from_owner = static_cast(m_mapper->owner(from)); if (wid == from_owner) { dtkDistributedGraphTopologyPopulateMap(m_dd.map, from, to); *(m_edge_count->begin()) += 1; } else { qint32 to_owner = static_cast(m_mapper->owner(to)); if (wid == to_owner) { dtkDistributedGraphTopologyPopulateMap(m_dd.map_hybrid, from, to); } else { dtkDistributedGraphTopologyPopulateMap(m_dd.map_remote, from, to); } } } void dtkDistributedGraphTopology::buildDomainDecompositionMaps(void) { if (!m_is_assembled) return; qlonglong wid = this->wid(); m_dd.map.clear(); m_dd.map_hybrid.clear(); m_dd.map_remote.clear(); // List of boundary nodes owned by a lower id partition. std::set be_nodes; qlonglong first_id = this->mapper()->firstIndex(wid); qlonglong last_id = this->mapper()->lastIndex(wid); qlonglong v_count = this->mapper()->count(wid); qlonglong v_id = first_id; auto vit = m_vertex_to_edge->begin(); auto vend = vit + v_count; auto n_count = m_neighbour_count->begin(); auto nit = m_edge_to_vertex->begin(); // Lookup over interior nodes to detect: // - whether the current intern node is on a boundary // - boundary nodes owned by a lower partition // - populate pure intern edges into m_dd.map // - populate hybrid edges into m_dd.map_hybrid for (; vit != vend; ++vit, ++n_count, ++v_id) { auto nbeg = nit; auto nend = (nit + *n_count); bool current_node_is_on_boundary = false; // Lookup over interior nodes to detect whether the current // intern node is on a boundary for (; nit != nend; ++nit) { if (*nit > last_id) { current_node_is_on_boundary = true; break; } } nit = nbeg; // When the current node is on a boundary, all outcoming edges // are hybrid. Moreover, when target node is on a partition // with a lower id, the incoming edge is also hybrid. if (current_node_is_on_boundary) { for (; nit != nend; ++nit) { if (*nit < first_id) { dtkDistributedGraphTopologyPopulateMap(m_dd.map_hybrid, v_id, *nit); dtkDistributedGraphTopologyPopulateMap(m_dd.map_hybrid, *nit, v_id); dtkDistributedOrderingListInsertion(be_nodes, *nit); } else if (*nit <= last_id) { dtkDistributedGraphTopologyPopulateMap(m_dd.map_hybrid, v_id, *nit); } } // When the current node is interior, outcoming edges are // interior. When the target node is on a partition with a // lower id, the incoming edge is hybrid. } else { for (; nit != nend; ++nit) { if (*nit < first_id) { dtkDistributedGraphTopologyPopulateMap(m_dd.map, v_id, *nit); dtkDistributedGraphTopologyPopulateMap(m_dd.map_hybrid, *nit, v_id); dtkDistributedOrderingListInsertion(be_nodes, *nit); } else if (*nit <= last_id) { dtkDistributedGraphTopologyPopulateMap(m_dd.map, v_id, *nit); } } } } // At this stage, it is possible to know which vertex each // partition shares with another one and which interface vertices // each partition is responsible for. This information is gather // into m_dd.map_itf. (Same piece of code in assemble method!!!) { m_dd.local_intern_size = m_dd.map.size(); m_dd.local_itf_size = m_dd.map_hybrid.size(); auto it = m_dd.map_hybrid.cbegin(); auto end = m_dd.map_hybrid.cend(); for (; it != end; ++it) { qlonglong gid = it.key(); qlonglong owner_gid = this->mapper()->owner(gid); if (wid == owner_gid) { dtkDistributedOrderingListInsertion(m_dd.map_itf[owner_gid], gid); } qlonglong j = m_vertex_to_edge->at(gid); qlonglong jend = j + m_neighbour_count->at(gid); for (; j < jend; ++j) { qlonglong jid = m_edge_to_vertex->at(j); qlonglong owner_jid = this->mapper()->owner(jid); if (wid != owner_jid && owner_gid <= owner_jid) { dtkDistributedOrderingListInsertion(m_dd.map_itf[owner_jid], gid); } } } } // Now we populate the m_dd.map_remote that contains all the edges // connecting boundary nodes of lower id partitions. this->rlock(); for (qlonglong ve_id : be_nodes) { qlonglong n_start = m_vertex_to_edge->at(ve_id); qlonglong n_end = n_start + m_neighbour_count->at(ve_id); for (qlonglong ne_id = n_start; ne_id < n_end; ++ne_id) { qlonglong vn_id = m_edge_to_vertex->at(ne_id); if (be_nodes.find(vn_id) != be_nodes.end()) { dtkDistributedGraphTopologyPopulateMap(m_dd.map_remote, ve_id, vn_id); } } } this->unlock(); //qDebug() << wid << mapper()->firstIndex(wid) << mapper()->lastIndex(wid) << m_dd.map;// << m_dd.map_hybrid << m_dd.map_remote; this->m_comm->barrier(); } void dtkDistributedGraphTopology::buildDomainDecompositionData(void) { if (!m_is_assembled) return; qlonglong local_internal_nnz = 0; qlonglong local_hybrid_nnz = 0; qlonglong local_remote_nnz = 0; qlonglong v_count = 0; qlonglong e_count = 0; dtkArray local_neighbour_count; // Build the local CSR structures that organize data according to // domain decomposition paradigm. // First, we clean map_remote to extract vertices that are not // shared with other partitions. It means that vertices taht do // not appear into map_hybrid are removed from the map_remote. { auto it = m_dd.map_remote.begin(); auto ite = m_dd.map_remote.end(); dtkArray vert_to_remove; vert_to_remove.reserve(m_dd.map_remote.size()); for (; it != ite; ++it) { if (!m_dd.map_hybrid.contains(it.key())) { vert_to_remove << it.key(); } else { std::set& l = *it; auto lit = l.begin(); auto lend = l.end(); for (; lit != lend; ++lit) { auto pos = lit; if (!m_dd.map_hybrid.contains(*lit)) { lit = l.erase(pos); --lit; } } l.erase(it.key()); } } for (qlonglong vid : vert_to_remove) { m_dd.map_remote.remove(vid); } } // Fills the local_vertex_to_edge array that maps each vertex to // its first local edge. The vertices are sorted as follows: // - first, the pure interior ones // - second, the boundary vertices owned by the current partition // - third, the boundary vertices owned by lower id partitions m_dd.local_vertex_to_edge.reserve(m_dd.map.size() + m_dd.map_hybrid.size() + m_dd.map_remote.size()); // At the same time, we make the mapping that enable to find local // id to global id and conversely. { auto it = m_dd.map.cbegin(); auto ite = m_dd.map.cend(); for (; it != ite; ++it) { local_internal_nnz += (*it).size(); m_dd.glob_to_loc.insert(it.key(), v_count++); m_dd.loc_to_glob << it.key(); local_neighbour_count << (*it).size(); m_dd.local_vertex_to_edge << e_count; e_count += (*it).size(); } } { auto it = m_dd.map_hybrid.cbegin(); auto ite = m_dd.map_hybrid.cend(); for (; it != ite; ++it) { local_hybrid_nnz += (*it).size(); m_dd.glob_to_loc.insert(it.key(), v_count++); m_dd.loc_to_glob << it.key(); local_neighbour_count << (*it).size(); m_dd.local_vertex_to_edge << e_count; e_count += (*it).size(); } } m_dd.local_vertex_to_edge << e_count; // The remote edges are not handled by the current partition so // they are registered at the end of the local_edge_to_vertex // array and they are not referenced by the local_vertex_to_edge // array. { auto it = m_dd.map_remote.cbegin(); auto ite = m_dd.map_remote.cend(); for (; it != ite; ++it) { local_remote_nnz += (*it).size(); if (!m_dd.glob_to_loc.contains(it.key())) { m_dd.glob_to_loc.insert(it.key(), v_count++); m_dd.loc_to_glob << it.key(); local_neighbour_count << (*it).size(); e_count += (*it).size(); m_dd.local_vertex_to_edge << m_dd.local_vertex_to_edge.last(); } else { local_neighbour_count[m_dd.glob_to_loc[it.key()]] += (*it).size(); e_count += (*it).size(); } } } // Now we can build the mapper that describe how the edges are // distributed according to domain decomposition paradigm. qlonglong local_nnz = local_internal_nnz + local_hybrid_nnz + local_remote_nnz; m_dd.local_edge_to_vertex.resize(local_nnz); qlonglong *local_edge_count = new qlonglong[m_comm->size()]; m_comm->gather(&local_nnz, local_edge_count, 1, 0, true); qlonglong total_local_nnz = 0; for (qlonglong i = 0; i < m_comm->size(); ++i) total_local_nnz += local_edge_count[i]; m_dd.mapper = new dtkDistributedMapper; m_dd.mapper->ref(); m_dd.mapper->initMap(total_local_nnz, m_comm->size()); qlonglong offset = 0; for (qlonglong i = 0; i < m_comm->size(); ++i) { m_dd.mapper->setMap(offset, i); offset += local_edge_count[i]; } delete[] local_edge_count; // We now fill the local CSR edge_to_vertex array. The global ids // are sorted according to their local ids. This operation is // purely local. auto local_it = m_dd.local_edge_to_vertex.begin(); { auto it = m_dd.map.cbegin(); auto ite = m_dd.map.cend(); for (; it != ite; ++it) { for (qlonglong id : *it) { (*local_it) = id; ++local_it; } } } { auto it = m_dd.map_hybrid.cbegin(); auto ite = m_dd.map_hybrid.cend(); for (; it != ite; ++it) { for (qlonglong id : *it) { (*local_it) = id; ++local_it; } } } { auto it = m_dd.map_remote.cbegin(); auto ite = m_dd.map_remote.cend(); for (; it != ite; ++it) { for (qlonglong id : *it) { (*local_it) = id; ++local_it; } } } // Now we fill the m_dd.position DSarray that tells for each edge // in the graph structure where it can be found in the domain // decomposition organization. qlonglong local_pos = m_dd.mapper->firstIndex(this->wid()); if (m_dd.positions) delete m_dd.positions; m_dd.positions = new dtkDistributedArray(m_edge_to_vertex->size(), m_edge_to_vertex->mapper()); m_dd.positions->fill(-1); // We scan the local vertices of the DD structure. this->rlock(); for (qlonglong i = 0; i < m_dd.local_vertex_to_edge.size() - 1; ++i) { qlonglong gi = m_dd.loc_to_glob[i]; qlonglong nnz = m_dd.local_vertex_to_edge[i + 1] - m_dd.local_vertex_to_edge[i]; qlonglong local_start = m_dd.local_vertex_to_edge[i]; qlonglong first_n_pos = m_vertex_to_edge->at(gi); qlonglong n_count = m_neighbour_count->at(gi); // For each local edge we search for its pos into the graph at // the same place we set its local pos into the m_dd.positions // DS array. for (qlonglong j = 0; j < nnz; ++j) { qlonglong gj = m_dd.local_edge_to_vertex[j + local_start]; for (qlonglong k = 0; k < n_count; ++k) { qlonglong global_pos = k + first_n_pos; if (gj == m_edge_to_vertex->at(global_pos)) { m_dd.positions->setAt(global_pos, local_pos); ++local_pos; break; } } } } this->unlock(); // We only keep the remote map for referencing remote edges. m_dd.map.clear(); m_dd.map_hybrid.clear(); this->m_comm->barrier(); } void dtkDistributedGraphTopology::assembleDomainDecompositionFeatures(void) { this->buildDomainDecompositionMaps(); this->buildDomainDecompositionData(); } void dtkDistributedGraphTopology::assemble(void) { this->m_comm->barrier(); // --------------------------- // First stage: interface vertex counting // --------------------------- // The first step of this procedure is to identify the vertices // that stand on an interface. We use the m_neighbour_count DSarray // to distinguish interior vertices from interface vertices. // Resets m_neighbour_count DSarray m_neighbour_count->fill(0); // Now we scan the m_map_hybrid containing the edges with a source // vertex that belongs to another partition. Such source vertices // stand on an interface. For each corresponding entry into the // m_neighbour_count DSarray, we add the number of edges that // connect these vertices to interior vertices. { auto it = m_dd.map_hybrid.cbegin(); auto ite = m_dd.map_hybrid.cend(); qlonglong buffer_array[512]; qlonglong last_key = it.key(); int local_counter = 0; qlonglong buffer_index = it.key(); for (; it != ite; ++it) { if (it.key() < last_key) qDebug() << "PB QMAP not ordered !!!" << it.key() << last_key; qlonglong gap = it.key() - last_key; //buffer is full or there is a gap too large, send it if ((local_counter + gap + 1) >= 511) { m_neighbour_count->addAssign(buffer_index, buffer_array, local_counter); buffer_index = it.key(); last_key = it.key(); local_counter = 0; gap = 0; } //otherwise test if we have to fill buffer_array with 0 for (auto i = 0; i < (gap - 1); ++i) { buffer_array[local_counter++] = 0; } //then had this value to the buffer buffer_array[local_counter++] = (*it).size(); last_key = it.key(); } //finally send the last values m_neighbour_count->addAssign(buffer_index, buffer_array, local_counter); } this->m_comm->barrier(); // After the previous scan, each partition can identify among its // interior vertices those that have connections with vertices on // other partitions. Recognize new interface vertices provided by // other partitions. These vertices were added into the m_map. So, // we can now move them into the m_map_hybrid and increment for // each of them the number of connected edges into the // m_neighbour_count DSarray. // For that, we scan the local part of this latter. For each non // zero entry, we search for the corresponding vertex into the // m_map then, if we find it, we move its edges into the // m_map_hybrid and we increment the value of the current entry of // the m_neighbour_count DSarray. { auto it = m_neighbour_count->begin(); auto ite = m_neighbour_count->end(); qlonglong id = 0; qlonglong gid; for (; it != ite; ++it, ++id) { if (*it != 0) { gid = m_neighbour_count->mapper()->localToGlobal(id, this->wid()); EdgeList l = m_dd.map.take(gid); if (!l.empty()) { m_dd.map_hybrid.insert(gid, l); *it += l.size(); } } } } m_comm->barrier(); m_neighbour_count->clearCache(); // --------------------------- // Second stage: odd edge counting // --------------------------- // At this stage, the local m_map_hybrid contains all the // interface vertices and their connections to interior // ones. However, for given graph topology, it is likely that some // edges connect two interface vertices without being themselves // on the interface. If such edges exist, they have been added // into the m_map_remote that contains all the edges that connect // two interface vertices that belong to another partition. // Identifying such edges requires to assemble a DSarray // containing all the edges from the local m_map_hybrid. Then, // each partition scan the edges of its local m_map_remote and // search for them into the DSarray whether they have been already // treated or not. If not, these edges are moved from the // m_map_remote to the m_map_hybrid. // if (wid() == 3) { // qDebug() << m_dd.map_hybrid << m_dd.map; // auto it = m_neighbour_count->begin(); // auto ite = m_neighbour_count->end(); // for(; it != ite; ++it) { // qDebug() << *it; // } // } // The following step is therefore to assemble into a DSarray all // the edges from the local m_map_hybrid. These edges will be // stored into the CSR format so we can reuse the intrinsic // structures of the graph, namely the m_vertex_to_edge DSarray // and the m_edge_to_vertex one. // We now build the mapper of the m_edge_to_vertex DSarray. For // that, we need to count the local edges that have an interface // vertex as source. We also populate the m_vertex_to_edge DSarray // that will tell us the positions in the m_edge_to_vertex DSarray // of the first neighbours for each interface vertex. qlonglong local_itf_edges; { m_vertex_to_edge->fill(-1); qlonglong first_id = m_vertex_to_edge->mapper()->firstIndex(this->wid()); qlonglong last_id = m_vertex_to_edge->mapper()->lastIndex(this->wid()); qlonglong offset = 0; //calculation of offset //1 each proc sum it's local offset qlonglong local_offset = 0; m_neighbour_count->rlock(this->wid()); for (qlonglong i = first_id; i <= last_id; ++i) { local_offset += m_neighbour_count->at(i); } m_neighbour_count->unlock(); m_comm->barrier(); //2 we do a all gather to avoid reading data of other processes qlonglong *offset_per_proc = new qlonglong[m_comm->size()] ; m_comm->gather(&local_offset, offset_per_proc, 1, 0, true); //3 do a reduction for (qlonglong i = 0; i < this->wid(); ++i) { offset += offset_per_proc[i]; } offset += this->wid(); delete[] offset_per_proc; auto it = m_neighbour_count->begin(); auto ite = m_neighbour_count->end(); auto vit = m_vertex_to_edge->begin(); local_itf_edges = offset; for (; it != ite; ++it, ++vit) { if (*it != 0) { *vit = local_itf_edges; local_itf_edges += *it; } } local_itf_edges -= offset; // In order to avoid zero array allocation which can happen // when a partition does not own any interface vertex (usually // the last partition), we enforce a minimal size of one. local_itf_edges += 1; } m_comm->barrier(); m_vertex_to_edge->clearCache(); // Reduction to compute the global number of interface edges. qlonglong itf_edges_count = 0; m_comm->reduce(&local_itf_edges, &itf_edges_count, 1, dtkDistributedCommunicator::Sum, 0, true); // We update the DSarray m_edge_count so that it contains the // number of interface edges for each partitions. (*m_edge_count->begin()) = local_itf_edges; m_comm->barrier(); m_edge_count->clearCache(); // Builds the mapper for m_edge_to_vertex DSarray. dtkDistributedMapper *itf_edges_mapper = new dtkDistributedMapper(); itf_edges_mapper->initMap(itf_edges_count, m_comm->size()); qlonglong itf_edge_counter = 0; for (int i = 0; i < m_comm->size(); ++i) { itf_edges_mapper->setMap(itf_edge_counter, i); itf_edge_counter += m_edge_count->at(i); } m_comm->barrier(); // Now we can populate the m_edge_to_vertex DSarray if (m_edge_to_vertex) { delete m_edge_to_vertex; } m_edge_to_vertex = new dtkDistributedArray(itf_edges_count, itf_edges_mapper); m_edge_to_vertex->fill(-1); // We scan the interface vertices of the local m_hybrid_map. For // each of them we find using the m_vertex_to_edge DSarray the // position into the m_edge_to_vertex DSarray where we can set the // value of all its neighbours. { auto it = m_dd.map_hybrid.cbegin(); auto ite = m_dd.map_hybrid.cend(); qlonglong minusOne = -1; for (; it != ite; ++it) { qlonglong start_pos = m_vertex_to_edge->at(it.key()); qlonglong end_pos = start_pos + m_neighbour_count->at(it.key()); const EdgeList& l = *it; for (qlonglong nid : l) { for (qlonglong j = start_pos; j < end_pos; ++j) { if (m_edge_to_vertex->compareAndSwap(j, nid, minusOne)) { break; } } } } } this->m_comm->barrier(); // At this stage, it is possible to know which vertex each // partition shares with another one and which interface vertices // each partition is responsible for. This infcormation is gather // into m_dd.map_itf. { m_dd.local_intern_size = m_dd.map.size(); m_dd.local_itf_size = m_dd.map_hybrid.size(); auto it = m_dd.map_hybrid.cbegin(); auto end = m_dd.map_hybrid.cend(); for (; it != end; ++it) { qlonglong gid = it.key(); qlonglong owner_gid = this->mapper()->owner(gid); if (wid() == owner_gid) { dtkDistributedOrderingListInsertion(m_dd.map_itf[owner_gid], gid); } qlonglong j = m_vertex_to_edge->at(gid); qlonglong jend = j + m_neighbour_count->at(gid); for (; j < jend; ++j) { qlonglong jid = m_edge_to_vertex->at(j); qlonglong owner_jid = this->mapper()->owner(jid); if (wid() != owner_jid && owner_gid <= owner_jid) { dtkDistributedOrderingListInsertion(m_dd.map_itf[owner_jid], gid); } } } } // Furthermore, we can now compute the total number of vertices on // all the interfaces. qlonglong local_itf_owned = m_dd.map_itf[wid()].size(); m_comm->reduce(&local_itf_owned, &m_dd.global_itf_size, 1, dtkDistributedCommunicator::Sum, 0, true); // Now, the m_edge_to_vertex DSarray contains all the edges that // have an interface vertex as source. We can perfom an // intersection with the local m_map_remote to find the remaining // edges that connect two interface vertices but that does not // stand on an interface. // Such edges will be moved from m_map_remote to m_map_hybrid and // also stored into a dedicated map. QMap > remote_edge_to_add; // For each edge of the m_dd.map_remote, we search into the // m_edge_to_vertex whether it is found or not. If the edge is not // found, it means that it is an edge that connects two interface // vertices but that it does not stand on an interface. { auto it = m_dd.map_remote.begin(); auto ite = m_dd.map_remote.end(); QList edges_to_move; for (; it != ite; ++it) { qlonglong start_pos = m_vertex_to_edge->at(it.key()); qlonglong end_pos = start_pos + m_neighbour_count->at(it.key()); EdgeList& l = *it; edges_to_move.clear(); auto end_l = l.cend(); for (auto i = l.begin(); i != end_l; ++i) { qlonglong nid = *i; bool found = false; for (qlonglong j = start_pos; j < end_pos; ++j) { if (nid == m_edge_to_vertex->at(j)) { found = true; break; } } if (!found) { edges_to_move << nid; } } if (!edges_to_move.empty()) { EdgeList& rl = remote_edge_to_add[it.key()]; EdgeList& hl = m_dd.map_hybrid[it.key()]; for (int i = 0; i < edges_to_move.size(); ++i) { qlonglong e_id = edges_to_move.at(i); rl.insert(e_id); hl.insert(e_id); } l.clear(); m_neighbour_count->addAssign(it.key(), edges_to_move.size()); } } } m_comm->barrier(); m_neighbour_count->clearCache(); // At the end of this stage, the local hybrid map contains all the // edges that connect an interior vertex to an interface vertex // plus the special edges that connect two interface vertices but // that does not belong to an interface. // Moreover, for all the vertices that stand on an interface, the // m_neighbour_count array contains the right number of edges // connected to these vertices. // --------------------------- // Third stage: graph building // --------------------------- // Firstly, we save the mapping of the vertex to the interface // edges and the local interface edges into pure local arrays. To // preserve a local mapping we need to substract to each value the // first position in the current partition. dtkArray itf_neightbour_count(m_neighbour_count->data(), m_neighbour_count->mapper()->count(this->wid())); dtkArray vertex_to_itf_edges(m_vertex_to_edge->data(), m_vertex_to_edge->mapper()->count(this->wid())); dtkArray itf_edges_to_vertex(m_edge_to_vertex->data(), m_edge_to_vertex->mapper()->count(this->wid()) - 1); { qlonglong offset = m_edge_to_vertex->mapper()->firstIndex(this->wid()); for (qlonglong& i : vertex_to_itf_edges) { i -= offset; } } m_comm->barrier(); // Resets the mapping. m_vertex_to_edge->fill(0); // We now complete the counting of the neighbours for the intern // vertices. This step is purely local. m_neighbour_count->wlock(this->wid()); { auto it = m_dd.map.cbegin(); auto ite = m_dd.map.cend(); for (; it != ite; ++it) { const EdgeList& l = *it; m_neighbour_count->setAt(it.key(), l.size()); } } m_neighbour_count->unlock(this->wid()); // We now build the mapper for the m_edge_to_vertex DSarray. We // count the local edges for each partition and assign this value // into the m_edge_count DSarray m_neighbour_count->rlock(this->wid()); qlonglong local_edge_count = 0; { for (qlonglong count : (*m_neighbour_count)) { local_edge_count += count; } (*(m_edge_count->begin())) = local_edge_count; } m_neighbour_count->unlock(this->wid()); // Reduction to compute global edge count qlonglong edge_count; m_comm->reduce(&local_edge_count, &edge_count, 1, dtkDistributedCommunicator::Sum, 0, true); m_neighbour_count->clearCache(); m_edge_count->clearCache(); // We are now able to populate the mapper. dtkDistributedMapper *e_mapper = new dtkDistributedMapper; e_mapper->initMap(edge_count, m_comm->size()); { qlonglong offset = 0; for (qlonglong i = 0; i < m_comm->size(); ++i) { e_mapper->setMap(offset , i); offset += m_edge_count->at(i); } offset = e_mapper->firstIndex(this->wid()); // We also populate the m_vertex_to_edge DSarray that will // tell us the positions in the m_edge_to_vertex DSarray of // the first neighbours for each vertex. m_vertex_to_edge->wlock(this->wid()); m_neighbour_count->rlock(this->wid()); auto nit = m_neighbour_count->begin(); auto nite = m_neighbour_count->end(); auto it = m_vertex_to_edge->begin(); for (; nit != nite; ++nit, ++it) { *it = offset; offset += *nit; } m_neighbour_count->unlock(this->wid()); m_vertex_to_edge->unlock(this->wid()); } m_comm->barrier(); m_vertex_to_edge->clearCache(); // We destroy the former DSarray to create a new one with the // total number of edges. delete m_edge_to_vertex; m_edge_to_vertex = new dtkDistributedArray(edge_count, e_mapper); m_edge_to_vertex->fill(-1); // Firstly, each partition add the odd edges stored in its // remote_edge_to_add map. { auto it = remote_edge_to_add.cbegin(); auto ite = remote_edge_to_add.cend(); qlonglong minusOne = -1; for (; it != ite; ++it) { qlonglong start_pos = m_vertex_to_edge->at(it.key()); qlonglong end_pos = start_pos + m_neighbour_count->at(it.key()); const EdgeList& l = *it; for (qlonglong nid : l) { for (qlonglong j = start_pos; j < end_pos; ++j) { if (m_edge_to_vertex->compareAndSwap(j, nid, minusOne)) { break; } } } } } this->m_comm->barrier(); m_edge_to_vertex->clearCache(); // Populate the m_edge_to_vertex DSarray m_vertex_to_edge->rlock(this->wid()); m_neighbour_count->rlock(this->wid()); { qlonglong e_offset = m_edge_to_vertex->mapper()->firstIndex(this->wid()); // Firstly, we scan the edges of the interface vertices. For // each of them, we store in a list with increasing order // the id of their neighbours. EdgeList l; for (qlonglong i = 0; i < vertex_to_itf_edges.size(); ++i) { qlonglong start_pos = vertex_to_itf_edges.at(i); if (start_pos < 0) { // We squeeze intern vertices. continue; } qlonglong gid = this->mapper()->localToGlobal(i, this->wid()); // We start by the edges having two vertices on an // interface but that do not belong to an interface. auto e_it = m_edge_to_vertex->begin() + (m_vertex_to_edge->at(gid) - e_offset); auto e_end = e_it + m_neighbour_count->at(gid); for (; e_it != e_end; ++e_it) { if ((*e_it) < 0) break; dtkDistributedOrderingListInsertion(l, *e_it); } // Now we scan the itf_edges_to_vertex array. To get the // right number of edges, we need to substract the number // of edges scanned in the previous step because // itf_neightbour_count array gets the total amount of // edges for each vertex. qlonglong end_pos = start_pos + itf_neightbour_count.at(i) - l.size(); for (qlonglong j = start_pos; j < end_pos; ++j) { dtkDistributedOrderingListInsertion(l, itf_edges_to_vertex.at(j)); } // Now we can use this list to populate m_vertex_to_edge DSarray. e_it = m_edge_to_vertex->begin() + m_vertex_to_edge->at(gid) - e_offset; auto lit = l.cbegin(); for (; e_it != e_end; ++e_it, ++lit) { *e_it = *lit; } // Clear the list before tackling next vertex. l.clear(); } this->m_comm->barrier(); m_edge_to_vertex->clearCache(); // Secondly, we scan the edges of the intern vertices and we // add them directly into the m_edge_to_vertex DSarray at the // right position. auto it = m_dd.map.cbegin(); auto end = m_dd.map.cend(); for (; it != end; ++it) { const EdgeList& edge_list = *it; auto nit = edge_list.begin(); auto nend = edge_list.end(); auto e_it = m_edge_to_vertex->begin() + m_vertex_to_edge->at(it.key()) - e_offset; for (; nit != nend; ++nit, ++e_it) { *e_it = *nit; } } } m_neighbour_count->unlock(this->wid()); m_vertex_to_edge->unlock(this->wid()); m_is_assembled = true; m_comm->barrier(); m_edge_to_vertex->clearCache(); // ------------------------ this->buildDomainDecompositionData(); } // ///////////////////////////////////////////////////////////////// // Helper function // ///////////////////////////////////////////////////////////////// void dtkDistributedOrderingListInsertion(std::set& l, qlonglong id) { l.insert(id); } void dtkDistributedSortListUsingMap(const std::set& l, const QMap& m, QList& res) { res.clear(); res.reserve(l.size()); for (const qlonglong& id : l) { const qlonglong& lid = m[id]; auto it = res.begin(); auto end = res.end(); for (; it != end; ++it) { if (lid < m[*(it)]) { break; } } res.insert(it, id); } } bool dtkDistributedGraphTopologyPopulateMap(QMap >& map, qlonglong from, qlonglong to) { map[from].insert(to); return true; } // // dtkDistributedGraphTopology.cpp ends here