|
Zoltan2
|
00001 // @HEADER 00002 // 00003 // *********************************************************************** 00004 // 00005 // Zoltan2: A package of combinatorial algorithms for scientific computing 00006 // Copyright 2012 Sandia Corporation 00007 // 00008 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, 00009 // the U.S. Government retains certain rights in this software. 00010 // 00011 // Redistribution and use in source and binary forms, with or without 00012 // modification, are permitted provided that the following conditions are 00013 // met: 00014 // 00015 // 1. Redistributions of source code must retain the above copyright 00016 // notice, this list of conditions and the following disclaimer. 00017 // 00018 // 2. Redistributions in binary form must reproduce the above copyright 00019 // notice, this list of conditions and the following disclaimer in the 00020 // documentation and/or other materials provided with the distribution. 00021 // 00022 // 3. Neither the name of the Corporation nor the names of the 00023 // contributors may be used to endorse or promote products derived from 00024 // this software without specific prior written permission. 00025 // 00026 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY 00027 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00028 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 00029 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE 00030 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 00031 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 00032 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 00033 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 00034 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00035 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00036 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00037 // 00038 // Questions? Contact Karen Devine (kddevin@sandia.gov) 00039 // Erik Boman (egboman@sandia.gov) 00040 // Siva Rajamanickam (srajama@sandia.gov) 00041 // 00042 // *********************************************************************** 00043 // 00044 // @HEADER 00045 00050 #ifndef _ZOLTAN2_GRAPHMODEL_HPP_ 00051 #define _ZOLTAN2_GRAPHMODEL_HPP_ 00052 00053 #include <Zoltan2_Model.hpp> 00054 #include <Zoltan2_InputTraits.hpp> 00055 #include <Zoltan2_MatrixAdapter.hpp> 00056 #include <Zoltan2_GraphAdapter.hpp> 00057 #include <Zoltan2_IdentifierAdapter.hpp> 00058 #include <Zoltan2_VectorAdapter.hpp> 00059 #include <Zoltan2_StridedData.hpp> 00060 #include <Zoltan2_MeshAdapter.hpp> 00061 00062 #include <vector> 00063 #include <Teuchos_Hashtable.hpp> 00064 00065 namespace Zoltan2 { 00066 00067 00069 00105 template <typename User> 00106 size_t removeUndesiredEdges( 00107 const RCP<const Environment> &env, 00108 int myRank, 00109 bool removeSelfEdges, 00110 bool removeOffProcessEdges, 00111 bool removeOffGroupEdges, 00112 ArrayView<const typename InputTraits<User>::zgid_t> &gids, 00113 ArrayView<const typename InputTraits<User>::zgid_t> &gidNbors, 00114 ArrayView<const int> &procIds, 00115 ArrayView<StridedData<typename InputTraits<User>::lno_t, 00116 typename InputTraits<User>::scalar_t> > &edgeWeights, 00117 ArrayView<const typename InputTraits<User>::lno_t> &offsets, 00118 ArrayRCP<const typename InputTraits<User>::zgid_t> &newGidNbors, // out 00119 typename InputTraits<User>::scalar_t **&newWeights, // out 00120 ArrayRCP<const typename InputTraits<User>::lno_t> &newOffsets) // out 00121 { 00122 typedef typename InputTraits<User>::zgid_t zgid_t; 00123 typedef typename InputTraits<User>::scalar_t scalar_t; 00124 typedef typename InputTraits<User>::lno_t lno_t; 00125 size_t numKeep = 0; 00126 00127 size_t numVtx = offsets.size() - 1; 00128 size_t numNbors = gidNbors.size(); 00129 00130 env->localInputAssertion(__FILE__, __LINE__, "need more input", 00131 (!removeSelfEdges || 00132 gids.size() >= 00133 static_cast<typename ArrayView<const zgid_t>::size_type>(numVtx)) 00134 && 00135 (!removeOffProcessEdges || 00136 procIds.size() >= 00137 static_cast<typename ArrayView<const int>::size_type>(numNbors)) && 00138 (!removeOffGroupEdges || 00139 procIds.size() >= 00140 static_cast<typename ArrayView<const int>::size_type>(numNbors)), 00141 BASIC_ASSERTION); 00142 00143 // initialize edge weight array 00144 00145 newWeights = NULL; 00146 int eDim = edgeWeights.size(); 00147 00148 // count desired edges 00149 00150 lno_t *offs = new lno_t [numVtx + 1]; 00151 env->localMemoryAssertion(__FILE__, __LINE__, numVtx+1, offs); 00152 for (size_t i = 0; i < numVtx+1; i++) offs[i] = 0; 00153 ArrayRCP<const lno_t> offArray = arcp(offs, 0, numVtx+1, true); 00154 00155 const lno_t *allOffs = offsets.getRawPtr(); 00156 const zgid_t *allIds = gidNbors.getRawPtr(); 00157 00158 const zgid_t *vtx = NULL; 00159 const int *proc = NULL; 00160 00161 if (gids.size() > 0) 00162 vtx = gids.getRawPtr(); 00163 00164 if (procIds.size() > 0) 00165 proc = procIds.getRawPtr(); 00166 00167 offs[0] = 0; 00168 for (size_t i=0; i < numVtx; i++){ 00169 offs[i+1] = 0; 00170 zgid_t vid = vtx ? vtx[i] : zgid_t(0); 00171 for (lno_t j=allOffs[i]; j < allOffs[i+1]; j++){ 00172 int owner = proc ? proc[j] : 0; 00173 bool keep = (!removeSelfEdges || vid != allIds[j]) && 00174 (!removeOffProcessEdges || owner == myRank) && 00175 (!removeOffGroupEdges || owner >= 0); 00176 00177 if (keep) 00178 offs[i+1]++; 00179 } 00180 } 00181 00182 // from counters to offsets 00183 00184 for (size_t i=1; i < numVtx; i++) 00185 offs[i+1] += offs[i]; 00186 00187 numKeep = offs[numVtx]; 00188 00189 // do we need a new neighbor list? 00190 00191 if (numNbors == numKeep){ 00192 newGidNbors = Teuchos::arcpFromArrayView(gidNbors); 00193 newOffsets = Teuchos::arcpFromArrayView(offsets); 00194 return numNbors; 00195 } 00196 else if (numKeep == 0){ 00197 newGidNbors = ArrayRCP<const zgid_t>(Teuchos::null); 00198 newOffsets = offArray; 00199 return 0; 00200 } 00201 00202 // Build the subset neighbor lists (id, weight, and offset). 00203 00204 zgid_t *newGids = new zgid_t [numKeep]; 00205 env->localMemoryAssertion(__FILE__, __LINE__, numKeep, newGids); 00206 00207 newGidNbors = arcp(newGids, 0, numKeep, true); 00208 newOffsets = offArray; 00209 00210 if (eDim > 0){ 00211 newWeights = new scalar_t * [eDim]; 00212 env->localMemoryAssertion(__FILE__, __LINE__, eDim, newWeights); 00213 00214 if (numKeep) { 00215 for (int w=0; w < eDim; w++){ 00216 newWeights[w] = new scalar_t [numKeep]; 00217 env->localMemoryAssertion(__FILE__, __LINE__, numKeep, newWeights[w]); 00218 } 00219 } 00220 else { 00221 for (int w=0; w < eDim; w++) 00222 newWeights[w] = NULL; 00223 } 00224 } 00225 00226 size_t next = 0; 00227 for (size_t i=0; i < numVtx && next < numKeep; i++){ 00228 zgid_t vid = vtx ? vtx[i] : zgid_t(0); 00229 for (lno_t j=allOffs[i]; j < allOffs[i+1]; j++){ 00230 int owner = proc ? proc[j] : 0; 00231 bool keep = (!removeSelfEdges || vid != allIds[j]) && 00232 (!removeOffProcessEdges || owner == myRank) && 00233 (!removeOffGroupEdges || owner >= 0); 00234 00235 if (keep){ 00236 newGids[next] = allIds[j]; 00237 for (int w=0; w < eDim; w++){ 00238 newWeights[w][next] = edgeWeights[w][j]; 00239 } 00240 next++; 00241 if (next == numKeep) 00242 break; 00243 00244 } // if (keep) 00245 } 00246 } 00247 00248 return numKeep; 00249 } 00250 00252 00256 template <typename User> 00257 size_t computeLocalEdgeList( 00258 const RCP<const Environment> &env, const RCP<const Comm<int> > &comm, 00259 size_t numLocalEdges, // local edges 00260 size_t numLocalGraphEdges, // edges in "local" graph 00261 RCP<const IdentifierMap<User> > &idMap, 00262 ArrayRCP<const typename InputTraits<User>::zgid_t> &allEdgeIds, // in 00263 ArrayRCP<const typename InputTraits<User>::gno_t> &allEdgeGnos, // in 00264 ArrayRCP<int> &allProcs, // in 00265 ArrayRCP<const typename InputTraits<User>::lno_t> &allOffs, // in 00266 ArrayRCP<StridedData<typename InputTraits<User>::lno_t, 00267 typename InputTraits<User>::scalar_t> > &allWeights,// in 00268 ArrayRCP<const typename InputTraits<User>::lno_t> &edgeLocalIds, // 00269 ArrayRCP<const typename InputTraits<User>::lno_t> &offsets, // out 00270 ArrayRCP<StridedData<typename InputTraits<User>::lno_t, 00271 typename InputTraits<User>::scalar_t> > &eWeights) // out 00272 { 00273 typedef typename InputTraits<User>::zgid_t zgid_t; 00274 typedef typename InputTraits<User>::gno_t gno_t; 00275 typedef typename InputTraits<User>::scalar_t scalar_t; 00276 typedef typename InputTraits<User>::lno_t lno_t; 00277 typedef StridedData<lno_t, scalar_t> input_t; 00278 int rank = comm->getRank(); 00279 00280 bool gnosAreGids = idMap->gnosAreGids(); 00281 00282 edgeLocalIds = ArrayRCP<const lno_t>(Teuchos::null); 00283 eWeights = ArrayRCP<input_t>(Teuchos::null); 00284 offsets = ArrayRCP<const lno_t>(Teuchos::null); 00285 00286 if (numLocalGraphEdges == 0) { 00287 // Set the offsets array and return 00288 size_t allOffsSize = allOffs.size(); 00289 lno_t *offs = new lno_t [allOffsSize]; 00290 env->localMemoryAssertion(__FILE__, __LINE__, allOffsSize, offs); 00291 for (size_t i = 0; i < allOffsSize; i++) offs[i] = 0; 00292 offsets = arcp(offs, 0, allOffsSize, true); 00293 return 0; 00294 } 00295 00296 if (numLocalGraphEdges == numLocalEdges){ 00297 00298 // Entire graph is local. 00299 00300 lno_t *lnos = new lno_t [numLocalGraphEdges]; 00301 env->localMemoryAssertion(__FILE__, __LINE__, numLocalGraphEdges, lnos); 00302 if (comm->getSize() == 1) { 00303 // With one rank, Can use gnos as local index. 00304 if (gnosAreGids) 00305 for (size_t i=0; i < numLocalEdges; i++) lnos[i] = allEdgeIds[i]; 00306 else 00307 for (size_t i=0; i < numLocalEdges; i++) lnos[i] = allEdgeGnos[i]; 00308 } 00309 else { 00310 ArrayRCP<gno_t> gnoArray; 00311 00312 if (gnosAreGids){ 00313 ArrayRCP<const gno_t> gnosConst = 00314 arcp_reinterpret_cast<const gno_t>(allEdgeIds); 00315 gnoArray = arcp_const_cast<gno_t>(gnosConst); 00316 } 00317 else { 00318 gnoArray = arcp_const_cast<gno_t>(allEdgeGnos); 00319 } 00320 00321 // Need to translate to gnos to local indexing 00322 ArrayView<lno_t> lnoView(lnos, numLocalGraphEdges); 00323 try { 00324 idMap->lnoTranslate(lnoView, 00325 gnoArray.view(0,numLocalGraphEdges), 00326 TRANSLATE_LIB_TO_APP); 00327 } 00328 Z2_FORWARD_EXCEPTIONS; 00329 } 00330 edgeLocalIds = arcp(lnos, 0, numLocalGraphEdges, true); 00331 offsets = allOffs; 00332 eWeights = allWeights; 00333 00334 } 00335 else{ 00336 00337 // Create subset list of local graph edges, offsets and weights. 00338 00339 int nWeightsPerEdge = allWeights.size(); 00340 00341 ArrayRCP<const zgid_t> newEgids; 00342 scalar_t **newWeights = NULL; 00343 00344 ArrayView<const zgid_t> dummyVtx; 00345 ArrayView<const zgid_t> nborView= allEdgeIds.view(0, numLocalEdges); 00346 ArrayView<const int> nborOwner = allProcs.view(0, numLocalEdges); 00347 ArrayView<input_t> eWgts = allWeights.view(0, nWeightsPerEdge); 00348 ArrayView<const lno_t> offView = allOffs.view(0, allOffs.size()); 00349 00350 try{ 00351 numLocalEdges = removeUndesiredEdges<User>(env, rank, false, true, false, 00352 dummyVtx, nborView, nborOwner, 00353 eWgts, offView, newEgids, 00354 newWeights, offsets); 00355 } 00356 Z2_FORWARD_EXCEPTIONS; 00357 00358 env->localBugAssertion(__FILE__, __LINE__, "local graph miscalculation", 00359 numLocalEdges == numLocalGraphEdges, BASIC_ASSERTION); 00360 00361 // offsets array was set by removeUndesiredEdges. Create weight array. 00362 00363 if (nWeightsPerEdge > 0){ 00364 input_t *wgts = new input_t [nWeightsPerEdge]; 00365 for (int w=0; w < nWeightsPerEdge; w++){ 00366 ArrayRCP<const scalar_t> wgtArray(newWeights[w], 0, numLocalGraphEdges,true); 00367 wgts[w] = input_t(wgtArray, 1); 00368 } 00369 eWeights = arcp(wgts, 0, nWeightsPerEdge); 00370 delete [] newWeights; 00371 } 00372 00373 // Create local ID array. First translate gid to gno. 00374 ArrayRCP<gno_t> gnoArray; 00375 00376 if (gnosAreGids){ 00377 ArrayRCP<const gno_t> gnosConst = 00378 arcp_reinterpret_cast<const gno_t>(newEgids); 00379 gnoArray = arcp_const_cast<gno_t>(gnosConst); 00380 } 00381 else{ 00382 00383 ArrayRCP<zgid_t> gidArray = arcp_const_cast<zgid_t>(newEgids); 00384 gno_t *gnoList= new gno_t [numLocalGraphEdges]; 00385 env->localMemoryAssertion(__FILE__, __LINE__, numLocalGraphEdges, 00386 gnoList); 00387 gnoArray = arcp(gnoList, 0, numLocalGraphEdges, true); 00388 00389 try { 00390 idMap->gidTranslate( 00391 gidArray.view(0,numLocalGraphEdges), 00392 gnoArray.view(0,numLocalGraphEdges), 00393 TRANSLATE_APP_TO_LIB); 00394 } 00395 Z2_FORWARD_EXCEPTIONS; 00396 } 00397 00398 // translate gno to lno 00399 00400 lno_t *lnoList = new lno_t [numLocalGraphEdges]; 00401 env->localMemoryAssertion(__FILE__, __LINE__, numLocalGraphEdges, 00402 lnoList); 00403 ArrayView<lno_t> lnoView(lnoList, numLocalGraphEdges); 00404 00405 try { 00406 idMap->lnoTranslate( 00407 lnoView, 00408 gnoArray.view(0,numLocalGraphEdges), 00409 TRANSLATE_LIB_TO_APP); 00410 } 00411 Z2_FORWARD_EXCEPTIONS; 00412 edgeLocalIds = arcp<const lno_t>(lnoList, 0, numLocalGraphEdges, true); 00413 } 00414 00415 return numLocalGraphEdges; 00416 } 00417 00419 00428 template <typename Adapter> 00429 class GraphModel : public Model<Adapter> 00430 { 00431 public: 00432 00433 #ifndef DOXYGEN_SHOULD_SKIP_THIS 00434 typedef typename Adapter::scalar_t scalar_t; 00435 typedef typename Adapter::gno_t gno_t; 00436 typedef typename Adapter::lno_t lno_t; 00437 typedef typename Adapter::zgid_t zgid_t; 00438 typedef typename Adapter::node_t node_t; 00439 typedef typename Adapter::user_t user_t; 00440 typedef typename Adapter::userCoord_t userCoord_t; 00441 typedef IdentifierMap<user_t> idmap_t; 00442 typedef StridedData<lno_t, scalar_t> input_t; 00443 #endif 00444 00446 ~GraphModel() { } 00447 00459 GraphModel(const MatrixAdapter<user_t,userCoord_t> *ia, 00460 const RCP<const Environment> &env, const RCP<const Comm<int> > &comm, 00461 modelFlag_t &modelFlags); 00462 00463 GraphModel(const GraphAdapter<user_t,userCoord_t> *ia, 00464 const RCP<const Environment> &env, const RCP<const Comm<int> > &comm, 00465 modelFlag_t &modelFlags); 00466 00467 GraphModel(const MeshAdapter<user_t> *ia, 00468 const RCP<const Environment> &env, const RCP<const Comm<int> > &comm, 00469 modelFlag_t &modelflags); 00470 00471 GraphModel(const VectorAdapter<userCoord_t> *ia, 00472 const RCP<const Environment> &env, const RCP<const Comm<int> > &comm, 00473 modelFlag_t &flags) 00474 { 00475 throw std::runtime_error("cannot build GraphModel from VectorAdapter"); 00476 } 00477 00478 GraphModel(const IdentifierAdapter<user_t> *ia, 00479 const RCP<const Environment> &env, const RCP<const Comm<int> > &comm, 00480 modelFlag_t &flags) 00481 { 00482 throw std::runtime_error("cannot build GraphModel from IdentifierAdapter"); 00483 } 00484 00487 size_t getLocalNumVertices() const { return numLocalVertices_; } 00488 00491 size_t getGlobalNumVertices() const { return numGlobalVertices_; } 00492 00496 size_t getLocalNumGlobalEdges() const { return numLocalEdges_; } 00497 00501 size_t getLocalNumLocalEdges() const { return numLocalGraphEdges_; } 00502 00505 size_t getGlobalNumEdges() const { return numGlobalEdges_; } 00506 00509 int getNumWeightsPerVertex() const { return numWeightsPerVertex_; } 00510 00513 int getNumWeightsPerEdge() const { return nWeightsPerEdge_; } 00514 00517 int getCoordinateDim() const { return vCoordDim_; } 00518 00529 size_t getVertexList( 00530 ArrayView<const gno_t> &Ids, 00531 ArrayView<input_t> &xyz, 00532 ArrayView<input_t> &wgts) const 00533 { 00534 Ids = ArrayView<const gno_t>(); 00535 size_t nv = gids_.size(); 00536 if (nv){ 00537 if (gnosAreGids_) 00538 Ids = ArrayView<const gno_t>( 00539 reinterpret_cast<const gno_t*>(gids_.getRawPtr()), nv); 00540 else 00541 Ids = gnosConst_(0, nv); 00542 } 00543 00544 xyz = vCoords_.view(0, vCoordDim_); 00545 wgts = vWeights_.view(0, numWeightsPerVertex_); 00546 return nv; 00547 } 00548 00562 // Implied Vertex LNOs from getVertexList are used as indices to offsets 00563 // array. 00564 // Vertex GNOs are returned as neighbors in edgeIds. 00565 00566 size_t getEdgeList( ArrayView<const gno_t> &edgeIds, 00567 ArrayView<const int> &procIds, ArrayView<const lno_t> &offsets, 00568 ArrayView<input_t> &wgts) const 00569 { 00570 edgeIds = ArrayView<const gno_t>(); 00571 if (numLocalEdges_) { 00572 if (gnosAreGids_) 00573 edgeIds = ArrayView<const gno_t>( 00574 reinterpret_cast<const gno_t*>(edgeGids_.getRawPtr()), 00575 numLocalEdges_); 00576 else 00577 edgeIds = edgeGnosConst_(0, numLocalEdges_); 00578 } 00579 00580 procIds = procIdsConst_.view(0, numLocalEdges_); 00581 offsets = offsets_.view(0, numLocalVertices_+1); 00582 wgts = eWeights_.view(0, nWeightsPerEdge_); 00583 return numLocalEdges_; 00584 } 00585 00609 size_t getLocalEdgeList( 00610 ArrayView<const lno_t> &edgeIds, 00611 ArrayView<const lno_t> &offsets, 00612 ArrayView<input_t> &wgts) 00613 { 00614 env_->timerStart(MACRO_TIMERS, "GraphModel::getLocalEdgeList"); 00615 00616 if (localGraphEdgeOffsets_.size() == 0) { 00617 // Local graph not created yet 00618 RCP<const IdentifierMap<user_t> > idmap = this->getIdentifierMap(); 00619 computeLocalEdgeList(env_, comm_, 00620 numLocalEdges_, numLocalGraphEdges_, 00621 idmap, edgeGids_, edgeGnosConst_, procIds_, offsets_, eWeights_, 00622 localGraphEdgeLnos_, localGraphEdgeOffsets_, localGraphEdgeWeights_); 00623 } 00624 edgeIds = localGraphEdgeLnos_(); 00625 offsets = localGraphEdgeOffsets_(); 00626 wgts = localGraphEdgeWeights_(); 00627 00628 env_->timerStop(MACRO_TIMERS, "GraphModel::getLocalEdgeList"); 00629 00630 return numLocalGraphEdges_; 00631 } 00632 00634 // The Model interface. 00636 00637 size_t getLocalNumObjects() const { return numLocalVertices_; } 00638 00639 size_t getGlobalNumObjects() const { return numGlobalVertices_; } 00640 00641 private: 00642 void shared_constructor(const Adapter *ia, modelFlag_t &modelFlags); 00643 00644 template <typename AdapterWithCoords> 00645 void shared_GetVertexCoords(const AdapterWithCoords *ia); 00646 00647 const RCP<const Environment > env_; 00648 const RCP<const Comm<int> > comm_; 00649 00650 ArrayRCP<const zgid_t> gids_; // vertices of input graph 00651 ArrayRCP<gno_t> gnos_; 00652 00653 int numWeightsPerVertex_; 00654 ArrayRCP<input_t> vWeights_; 00655 00656 int vCoordDim_; 00657 ArrayRCP<input_t> vCoords_; 00658 00659 // Note: in case of graph subsetting, size of these arrays 00660 // may be larger than numLocalEdges_. So do not use .size(). 00661 00662 ArrayRCP<const zgid_t> edgeGids_; 00663 ArrayRCP<gno_t> edgeGnos_; 00664 ArrayRCP<int> procIds_; 00665 ArrayRCP<const lno_t> offsets_; 00666 00667 int nWeightsPerEdge_; 00668 ArrayRCP<input_t> eWeights_; 00669 00670 ArrayRCP<const gno_t> gnosConst_; 00671 ArrayRCP<const gno_t> edgeGnosConst_; 00672 ArrayRCP<const int> procIdsConst_; 00673 00674 bool gnosAreGids_; 00675 00676 // For local graphs (graph restricted to local process). We 00677 // create these arrays only if required by the algorithm. 00678 00679 ArrayRCP<const lno_t> localGraphEdgeLnos_; 00680 ArrayRCP<const lno_t> localGraphEdgeOffsets_; 00681 ArrayRCP<input_t> localGraphEdgeWeights_; 00682 00683 // For convenience 00684 00685 size_t numLocalVertices_; 00686 size_t numGlobalVertices_; 00687 size_t numLocalEdges_; 00688 size_t numGlobalEdges_; 00689 size_t numLocalGraphEdges_; 00690 00691 // For debugging 00692 void print(); 00693 }; 00694 00695 00697 template <typename Adapter> 00698 GraphModel<Adapter>::GraphModel( 00699 const MatrixAdapter<user_t,userCoord_t> *ia, 00700 const RCP<const Environment> &env, 00701 const RCP<const Comm<int> > &comm, 00702 modelFlag_t &modelFlags): 00703 env_(env), 00704 comm_(comm), 00705 gids_(), 00706 gnos_(), 00707 numWeightsPerVertex_(0), 00708 vWeights_(), 00709 vCoordDim_(0), 00710 vCoords_(), 00711 edgeGids_(), 00712 edgeGnos_(), 00713 procIds_(), 00714 offsets_(), 00715 nWeightsPerEdge_(0), 00716 eWeights_(), 00717 gnosConst_(), 00718 edgeGnosConst_(), 00719 procIdsConst_(), 00720 gnosAreGids_(false), 00721 localGraphEdgeLnos_(), 00722 localGraphEdgeOffsets_(), 00723 localGraphEdgeWeights_(), 00724 numLocalVertices_(0), 00725 numGlobalVertices_(0), 00726 numLocalEdges_(0), 00727 numGlobalEdges_(0), 00728 numLocalGraphEdges_(0) 00729 { 00730 // Model creation flags 00731 bool symTranspose = modelFlags.test(SYMMETRIZE_INPUT_TRANSPOSE); 00732 bool symBipartite = modelFlags.test(SYMMETRIZE_INPUT_BIPARTITE); 00733 bool vertexCols = modelFlags.test(VERTICES_ARE_MATRIX_COLUMNS); 00734 bool vertexNz = modelFlags.test(VERTICES_ARE_MATRIX_NONZEROS); 00735 00736 if (symTranspose || symBipartite || vertexCols || vertexNz){ 00737 throw std::runtime_error("graph build option not yet implemented"); 00738 } 00739 00740 // Get the matrix from the input adapter 00741 zgid_t const *vtxIds=NULL, *nborIds=NULL; 00742 lno_t const *offsets=NULL; 00743 try{ 00744 numLocalVertices_ = ia->getLocalNumIDs(); 00745 ia->getIDsView(vtxIds); 00746 } 00747 Z2_FORWARD_EXCEPTIONS; 00748 try{ 00749 if (ia->CRSViewAvailable()) { 00750 ia->getCRSView(offsets, nborIds); 00751 } 00752 else { 00753 // TODO: Add support for CCS matrix layout 00754 throw std::runtime_error("Only MatrixAdapter::getCRSView is supported " 00755 "in graph model"); 00756 } 00757 } 00758 Z2_FORWARD_EXCEPTIONS; 00759 00760 numLocalEdges_ = offsets[numLocalVertices_]; 00761 00762 gids_ = arcp<const zgid_t>(vtxIds, 0, numLocalVertices_, false); 00763 edgeGids_ = arcp<const zgid_t>(nborIds, 0, numLocalEdges_, false); 00764 offsets_ = arcp<const lno_t>(offsets, 0, numLocalVertices_ + 1, false); 00765 00766 nWeightsPerEdge_ = 0; // no edge weights from a matrix yet. 00767 // TODO: use matrix values as edge weights 00768 00769 shared_constructor(ia, modelFlags); 00770 00771 // Get vertex coordinates, if available 00772 if (ia->coordinatesAvailable()) { 00773 typedef VectorAdapter<userCoord_t> adapterWithCoords_t; 00774 shared_GetVertexCoords<adapterWithCoords_t>(ia->getCoordinateInput()); 00775 } 00776 //print(); 00777 } 00778 00779 00781 template <typename Adapter> 00782 GraphModel<Adapter>::GraphModel( 00783 const GraphAdapter<user_t,userCoord_t> *ia, 00784 const RCP<const Environment> &env, 00785 const RCP<const Comm<int> > &comm, 00786 modelFlag_t &modelFlags): 00787 env_(env), 00788 comm_(comm), 00789 gids_(), 00790 gnos_(), 00791 numWeightsPerVertex_(0), 00792 vWeights_(), 00793 vCoordDim_(0), 00794 vCoords_(), 00795 edgeGids_(), 00796 edgeGnos_(), 00797 procIds_(), 00798 offsets_(), 00799 nWeightsPerEdge_(0), 00800 eWeights_(), 00801 gnosConst_(), 00802 edgeGnosConst_(), 00803 procIdsConst_(), 00804 gnosAreGids_(false), 00805 localGraphEdgeLnos_(), 00806 localGraphEdgeOffsets_(), 00807 localGraphEdgeWeights_(), 00808 numLocalVertices_(0), 00809 numGlobalVertices_(0), 00810 numLocalEdges_(0), 00811 numGlobalEdges_(0), 00812 numLocalGraphEdges_(0) 00813 { 00814 00815 // This GraphModel is built with vertices == GRAPH_VERTEX from GraphAdapter. 00816 // It is not ready to use vertices == GRAPH_EDGE from GraphAdapter. 00817 env_->localInputAssertion(__FILE__, __LINE__, 00818 "GraphModel from GraphAdapter is implemented only for " 00819 "Graph Vertices as primary object, not for Graph Edges", 00820 ia->getPrimaryEntityType() == Zoltan2::GRAPH_VERTEX, BASIC_ASSERTION); 00821 00822 // Get the graph from the input adapter 00823 00824 zgid_t const *vtxIds=NULL, *nborIds=NULL; 00825 lno_t const *offsets=NULL; 00826 try{ 00827 numLocalVertices_ = ia->getLocalNumVertices(); 00828 ia->getVertexIDsView(vtxIds); 00829 ia->getEdgesView(offsets, nborIds); 00830 } 00831 Z2_FORWARD_EXCEPTIONS; 00832 00833 numLocalEdges_ = offsets[numLocalVertices_]; 00834 00835 gids_ = arcp<const zgid_t>(vtxIds, 0, numLocalVertices_, false); 00836 edgeGids_ = arcp<const zgid_t>(nborIds, 0, numLocalEdges_, false); 00837 offsets_ = arcp<const lno_t>(offsets, 0, numLocalVertices_ + 1, false); 00838 00839 nWeightsPerEdge_ = ia->getNumWeightsPerEdge(); 00840 00841 if (nWeightsPerEdge_ > 0){ 00842 input_t *wgts = new input_t [nWeightsPerEdge_]; 00843 eWeights_ = arcp(wgts, 0, nWeightsPerEdge_, true); 00844 } 00845 00846 for (int w=0; w < nWeightsPerEdge_; w++){ 00847 const scalar_t *ewgts=NULL; 00848 int stride=0; 00849 00850 ia->getEdgeWeightsView(ewgts, stride, w); 00851 00852 ArrayRCP<const scalar_t> wgtArray(ewgts, 0, numLocalEdges_, false); 00853 eWeights_[w] = input_t(wgtArray, stride); 00854 } 00855 00856 shared_constructor(ia, modelFlags); 00857 00858 // Get vertex coordinates, if available 00859 if (ia->coordinatesAvailable()) { 00860 typedef VectorAdapter<userCoord_t> adapterWithCoords_t; 00861 shared_GetVertexCoords<adapterWithCoords_t>(ia->getCoordinateInput()); 00862 } 00863 //print(); 00864 } 00865 00867 template <typename Adapter> 00868 GraphModel<Adapter>::GraphModel( 00869 const MeshAdapter<user_t> *ia, 00870 const RCP<const Environment> &env, 00871 const RCP<const Comm<int> > &comm, 00872 modelFlag_t &modelFlags): 00873 env_(env), 00874 comm_(comm), 00875 gids_(), 00876 gnos_(), 00877 numWeightsPerVertex_(0), 00878 vWeights_(), 00879 vCoordDim_(0), 00880 vCoords_(), 00881 edgeGids_(), 00882 edgeGnos_(), 00883 procIds_(), 00884 offsets_(), 00885 nWeightsPerEdge_(0), 00886 eWeights_(), 00887 gnosConst_(), 00888 edgeGnosConst_(), 00889 procIdsConst_(), 00890 gnosAreGids_(false), 00891 localGraphEdgeLnos_(), 00892 localGraphEdgeOffsets_(), 00893 localGraphEdgeWeights_(), 00894 numLocalVertices_(0), 00895 numGlobalVertices_(0), 00896 numLocalEdges_(0), 00897 numGlobalEdges_(0), 00898 numLocalGraphEdges_(0) 00899 { 00900 env_->timerStart(MACRO_TIMERS, "GraphModel constructed from MeshAdapter"); 00901 00902 // This GraphModel is built with vertices == ia->getPrimaryEntityType() 00903 // from MeshAdapter. 00904 00905 // Get the graph from the input adapter 00906 00907 Zoltan2::MeshEntityType primaryEType = ia->getPrimaryEntityType(); 00908 Zoltan2::MeshEntityType secondAdjEType = ia->getSecondAdjacencyEntityType(); 00909 00910 // Get the IDs of the primary entity type; these are graph vertices 00911 00912 zgid_t const *vtxIds=NULL; 00913 try { 00914 numLocalVertices_ = ia->getLocalNumOf(primaryEType); 00915 ia->getIDsViewOf(primaryEType, vtxIds); 00916 } 00917 Z2_FORWARD_EXCEPTIONS; 00918 00919 gids_ = arcp<const zgid_t>(vtxIds, 0, numLocalVertices_, false); 00920 00921 // Get the second adjacencies to construct edges of the dual graph. 00922 // TODO: Enable building the graph from 1st adjacencies 00923 00924 zgid_t const *nborIds=NULL; 00925 lno_t const *offsets=NULL; 00926 00927 if (!ia->avail2ndAdjs(primaryEType, secondAdjEType)) { 00928 00929 throw std::logic_error("MeshAdapter must provide 2nd adjacencies for " 00930 "graph construction"); 00931 00932 } 00933 else { // avail2ndAdjs 00934 00935 // Get the edges 00936 try { 00937 ia->get2ndAdjsView(primaryEType, secondAdjEType, offsets, nborIds); 00938 } 00939 Z2_FORWARD_EXCEPTIONS; 00940 00941 numLocalEdges_ = offsets[numLocalVertices_]; 00942 00943 edgeGids_ = arcp<const zgid_t>(nborIds, 0, numLocalEdges_, false); 00944 offsets_ = arcp<const lno_t>(offsets, 0, numLocalVertices_ + 1, false); 00945 00946 // Get edge weights 00947 nWeightsPerEdge_ = ia->getNumWeightsPer2ndAdj(primaryEType, secondAdjEType); 00948 00949 if (nWeightsPerEdge_ > 0){ 00950 input_t *wgts = new input_t [nWeightsPerEdge_]; 00951 eWeights_ = arcp(wgts, 0, nWeightsPerEdge_, true); 00952 } 00953 00954 for (int w=0; w < nWeightsPerEdge_; w++){ 00955 const scalar_t *ewgts=NULL; 00956 int stride=0; 00957 00958 ia->get2ndAdjWeightsView(primaryEType, secondAdjEType, 00959 ewgts, stride, w); 00960 00961 ArrayRCP<const scalar_t> wgtArray(ewgts, 0, numLocalEdges_, false); 00962 eWeights_[w] = input_t(wgtArray, stride); 00963 } 00964 } 00965 00966 shared_constructor(ia, modelFlags); 00967 00968 typedef MeshAdapter<user_t> adapterWithCoords_t; 00969 shared_GetVertexCoords<adapterWithCoords_t>(ia); 00970 00971 env_->timerStop(MACRO_TIMERS, "GraphModel constructed from MeshAdapter"); 00972 print(); 00973 } 00974 00976 template <typename Adapter> 00977 void GraphModel<Adapter>::shared_constructor( 00978 const Adapter *ia, 00979 modelFlag_t &modelFlags) 00980 { 00981 // Model creation flags 00982 bool consecutiveIdsRequired = 00983 modelFlags.test(IDS_MUST_BE_GLOBALLY_CONSECUTIVE); 00984 bool removeSelfEdges = modelFlags.test(SELF_EDGES_MUST_BE_REMOVED); 00985 bool subsetGraph = modelFlags.test(GRAPH_IS_A_SUBSET_GRAPH); 00986 00987 // A subset graph is special in that it may contain neighbor 00988 // vertices that are not owned by processes in this communicator. 00989 // We remove these. 00990 00991 ArrayRCP<const int> nborProcs; 00992 00993 if (subsetGraph){ 00994 RCP<const idmap_t> idMap; 00995 try{ 00996 idMap = rcp(new idmap_t(env_, comm_, gids_, false)); 00997 } 00998 Z2_FORWARD_EXCEPTIONS; 00999 01000 ArrayRCP<int> procArray; 01001 01002 if (numLocalEdges_ > 0){ 01003 int *pids = new int [numLocalEdges_]; 01004 env_->localMemoryAssertion(__FILE__, __LINE__, numLocalEdges_, pids); 01005 procArray = arcp(pids, 0, numLocalEdges_, true); 01006 } 01007 01008 ArrayView<gno_t> dummyGno; 01009 01010 try{ 01011 // All processes must make this call. 01012 // procOwner will be -1 if edge Id is not in our communicator. 01013 01014 idMap->gidGlobalTranslate( edgeGids_.view(0, numLocalEdges_), 01015 dummyGno, procArray.view(0, numLocalEdges_)); 01016 } 01017 Z2_FORWARD_EXCEPTIONS; 01018 01019 int outOfSubset = 0; 01020 for (size_t i=0; i < numLocalEdges_; i++){ 01021 if (procArray[i] < 0){ 01022 outOfSubset++; 01023 break; 01024 } 01025 } 01026 01027 if (outOfSubset == 0){ 01028 procArray.clear(); 01029 subsetGraph = false; 01030 } 01031 else{ 01032 nborProcs = arcp_const_cast<const int>(procArray); 01033 } 01034 } 01035 01036 // Now remove undesired edges. 01037 01038 if (subsetGraph || removeSelfEdges){ 01039 01040 ArrayRCP<const zgid_t> newEdges; 01041 ArrayRCP<const lno_t> newOffs; 01042 scalar_t **newWeights = NULL; 01043 size_t numNewEdges = 0; 01044 01045 ArrayView<const zgid_t> vtxView = gids_.view(0, numLocalVertices_); 01046 ArrayView<const zgid_t> nborView= edgeGids_.view(0, numLocalEdges_); 01047 ArrayView<const int> nborOwner = nborProcs.view(0, nborProcs.size()); 01048 ArrayView<input_t> eWgts = eWeights_.view(0, nWeightsPerEdge_); 01049 ArrayView<const lno_t> offView = offsets_.view(0, numLocalVertices_ + 1); 01050 01051 try{ 01052 numNewEdges = removeUndesiredEdges<user_t>(env_, comm_->getRank(), 01053 removeSelfEdges, 01054 false, 01055 subsetGraph, 01056 vtxView, 01057 nborView, 01058 nborOwner, 01059 eWgts, 01060 offView, 01061 newEdges, 01062 newWeights, 01063 newOffs); 01064 } 01065 Z2_FORWARD_EXCEPTIONS; 01066 01067 nborProcs.clear(); 01068 01069 if (numNewEdges < numLocalEdges_){ 01070 edgeGids_ = newEdges; 01071 offsets_ = newOffs; 01072 numLocalEdges_ = numNewEdges; 01073 01074 for (int w=0; w < nWeightsPerEdge_; w++){ 01075 ArrayRCP<const scalar_t> wgtArray(newWeights[w], 0, numNewEdges, true); 01076 eWeights_[w] = input_t(wgtArray, 1); 01077 } 01078 } 01079 delete [] newWeights; 01080 } 01081 01082 // Create an IdentifierMap, which maps the user's global IDs to 01083 // Zoltan2 internal global numbers if necessary. 01084 // The map can also give us owners of our vertex neighbors. 01085 01086 RCP<const idmap_t> idMap; 01087 01088 try{ 01089 idMap = rcp(new idmap_t(env_, comm_, gids_, consecutiveIdsRequired)); 01090 } 01091 Z2_FORWARD_EXCEPTIONS; 01092 01093 // Model base class needs to have IdentifierMap. 01094 01095 this->setIdentifierMap(idMap); 01096 01097 numGlobalVertices_ = idMap->getGlobalNumberOfIds(); 01098 gnosAreGids_ = idMap->gnosAreGids(); 01099 01100 // Compute internal global numbers if we can not use the 01101 // user's global Ids. Also find the process owning each 01102 // neighboring vertex. 01103 01104 ArrayView<const zgid_t> gidArray(Teuchos::null); // edge gid 01105 ArrayView<gno_t> gnoArray(Teuchos::null); // edge gno 01106 ArrayView<int> procArray(Teuchos::null); // edge owner 01107 01108 if (numLocalVertices_){ 01109 01110 if (!gnosAreGids_){ // need vertex global numbers, edge global numbers 01111 gno_t *tmp = new gno_t [numLocalVertices_]; 01112 env_->localMemoryAssertion(__FILE__, __LINE__, numLocalVertices_, tmp); 01113 gnos_ = arcp(tmp, 0, numLocalVertices_); 01114 01115 try{ 01116 ArrayRCP<zgid_t> tmpGids = arcp_const_cast<zgid_t>(gids_); 01117 01118 idMap->gidTranslate(tmpGids(0,numLocalVertices_), 01119 gnos_(0,numLocalVertices_), TRANSLATE_APP_TO_LIB); 01120 } 01121 Z2_FORWARD_EXCEPTIONS; 01122 01123 if (numLocalEdges_){ 01124 tmp = new gno_t [numLocalEdges_]; 01125 env_->localMemoryAssertion(__FILE__, __LINE__, numLocalEdges_, tmp); 01126 edgeGnos_ = arcp(tmp, 0, numLocalEdges_); 01127 gnoArray = edgeGnos_.view(0, numLocalEdges_); 01128 } 01129 } 01130 01131 if (numLocalEdges_){ 01132 gidArray = edgeGids_.view(0, numLocalEdges_); 01133 01134 int *p = new int [numLocalEdges_]; 01135 env_->localMemoryAssertion(__FILE__, __LINE__, numLocalEdges_, p); 01136 procIds_ = arcp(p, 0, numLocalEdges_); 01137 procArray = procIds_.view(0, numLocalEdges_); 01138 } 01139 } 01140 01141 try{ 01142 // All processes must make this call. 01143 idMap->gidGlobalTranslate(gidArray, gnoArray, procArray); 01144 } 01145 Z2_FORWARD_EXCEPTIONS; 01146 01147 gnosConst_ = arcp_const_cast<const gno_t>(gnos_); 01148 edgeGnosConst_ = arcp_const_cast<const gno_t>(edgeGnos_); 01149 procIdsConst_ = arcp_const_cast<const int>(procIds_); 01150 01151 // Number of edges such that neighbor is on the local process. 01152 // We only create the list of local graph edges if the user 01153 // calls getLocalEdgeList(). 01154 01155 numLocalGraphEdges_ = 0; 01156 int *pids = procArray.getRawPtr(); 01157 int me = comm_->getRank(); 01158 for (size_t i=0; i < numLocalEdges_; i++) 01159 if (pids[i] == me) numLocalGraphEdges_++; 01160 01161 // Vertex weights 01162 01163 numWeightsPerVertex_ = ia->getNumWeightsPerID(); 01164 01165 if (numWeightsPerVertex_ > 0){ 01166 input_t *weightInfo = new input_t [numWeightsPerVertex_]; 01167 env_->localMemoryAssertion(__FILE__, __LINE__, numWeightsPerVertex_, 01168 weightInfo); 01169 01170 for (int idx=0; idx < numWeightsPerVertex_; idx++){ 01171 bool useNumNZ = ia->useDegreeAsWeight(idx); 01172 if (useNumNZ){ 01173 scalar_t *wgts = new scalar_t [numLocalVertices_]; 01174 env_->localMemoryAssertion(__FILE__, __LINE__, numLocalVertices_, wgts); 01175 ArrayRCP<const scalar_t> wgtArray = 01176 arcp(wgts, 0, numLocalVertices_, true); 01177 for (size_t i=0; i < numLocalVertices_; i++){ 01178 wgts[i] = offsets_[i+1] - offsets_[i]; 01179 } 01180 weightInfo[idx] = input_t(wgtArray, 1); 01181 } 01182 else{ 01183 const scalar_t *weights=NULL; 01184 int stride=0; 01185 ia->getWeightsView(weights, stride, idx); 01186 ArrayRCP<const scalar_t> wgtArray = arcp(weights, 0, 01187 stride*numLocalVertices_, 01188 false); 01189 weightInfo[idx] = input_t(wgtArray, stride); 01190 } 01191 } 01192 01193 vWeights_ = arcp<input_t>(weightInfo, 0, numWeightsPerVertex_, true); 01194 } 01195 01196 01197 reduceAll<int, size_t>(*comm_, Teuchos::REDUCE_SUM, 1, 01198 &numLocalEdges_, &numGlobalEdges_); 01199 01200 env_->memory("After construction of graph model"); 01201 } 01202 01204 01205 template <typename Adapter> 01206 template <typename AdapterWithCoords> 01207 void GraphModel<Adapter>::shared_GetVertexCoords(const AdapterWithCoords *ia) 01208 { 01209 // get Vertex coordinates from input adapter 01210 01211 vCoordDim_ = ia->getDimension(); 01212 01213 if (vCoordDim_ > 0){ 01214 input_t *coordInfo = new input_t [vCoordDim_]; 01215 env_->localMemoryAssertion(__FILE__, __LINE__, vCoordDim_, coordInfo); 01216 01217 for (int dim=0; dim < vCoordDim_; dim++){ 01218 const scalar_t *coords=NULL; 01219 int stride=0; 01220 ia->getCoordinatesView(coords, stride, dim); 01221 ArrayRCP<const scalar_t> coordArray = arcp(coords, 0, 01222 stride*numLocalVertices_, 01223 false); 01224 coordInfo[dim] = input_t(coordArray, stride); 01225 } 01226 01227 vCoords_ = arcp<input_t>(coordInfo, 0, vCoordDim_, true); 01228 } 01229 } 01230 01232 template <typename Adapter> 01233 void GraphModel<Adapter>::print() 01234 { 01235 if (env_->getDebugLevel() < VERBOSE_DETAILED_STATUS) 01236 return; 01237 01238 std::ostream *os = env_->getDebugOStream(); 01239 01240 int me = comm_->getRank(); 01241 std::string fn(" "); 01242 01243 *os << me << fn 01244 << " Nvtx " << gids_.size() 01245 << " Nedge " << edgeGids_.size() 01246 << " NLocalEdge " << numLocalGraphEdges_ 01247 << " NVWgt " << numWeightsPerVertex_ 01248 << " NEWgt " << nWeightsPerEdge_ 01249 << " CDim " << vCoordDim_ 01250 << " GnosAreGids " << gnosAreGids_ << std::endl; 01251 01252 for (lno_t i = 0; i < gids_.size(); i++) { 01253 *os << me << fn << i << " GID " << gids_[i] << ": "; 01254 for (lno_t j = offsets_[i]; j < offsets_[i+1]; j++) 01255 *os << edgeGids_[j] << " " << "(" << procIds_[j] << ") "; 01256 *os << std::endl; 01257 } 01258 01259 if (gnos_.size()) 01260 for (lno_t i = 0; i < gnos_.size(); i++) { 01261 *os << me << fn << i << " GNO " << gnos_[i] << ": "; 01262 for (lno_t j = offsets_[i]; j < offsets_[i+1]; j++) 01263 *os << edgeGnos_[j] << " ";//<< "(" << procIds_[j] << ") "; 01264 *os << std::endl; 01265 } 01266 else 01267 *os << me << fn << " GNOS NOT AVAILABLE " << std::endl; 01268 01269 if (comm_->getSize() > 1) { 01270 // Print local graph, with no off-process edges. 01271 ArrayView<const lno_t> localEdgeIds; 01272 ArrayView<const lno_t> localOffsets; 01273 ArrayView<input_t> localWgts; 01274 this->getLocalEdgeList(localEdgeIds, localOffsets, localWgts); 01275 01276 for (lno_t i = 0; i < gids_.size(); i++) { 01277 *os << me << fn << i << " LGNO " << gids_[i] << ": "; 01278 for (lno_t j = localOffsets[i]; j < localOffsets[i+1]; j++) 01279 *os << localEdgeIds[j] << " "; 01280 *os << std::endl; 01281 } 01282 } 01283 else 01284 *os << me << fn 01285 << " LOCAL GRAPH IS SAME AS GLOBAL GRAPH ON ONE RANK " << std::endl; 01286 01287 if (vCoordDim_) { 01288 for (lno_t i = 0; i < gids_.size(); i++) { 01289 *os << me << fn << i << " COORDS " << gids_[i] << ": "; 01290 for (int j = 0; j < vCoordDim_; j++) 01291 *os << vCoords_[j][i] << " "; 01292 *os << std::endl; 01293 } 01294 } 01295 else 01296 *os << me << fn << "NO COORDINATES AVAIL " << std::endl; 01297 } 01298 01299 } // namespace Zoltan2 01300 01301 01302 #endif 01303
1.7.6.1