|
EpetraExt
Development
|
00001 //@HEADER 00002 // *********************************************************************** 00003 // 00004 // EpetraExt: Epetra Extended - Linear Algebra Services Package 00005 // Copyright (2011) Sandia Corporation 00006 // 00007 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, 00008 // the U.S. Government retains certain rights in this software. 00009 // 00010 // Redistribution and use in source and binary forms, with or without 00011 // modification, are permitted provided that the following conditions are 00012 // met: 00013 // 00014 // 1. Redistributions of source code must retain the above copyright 00015 // notice, this list of conditions and the following disclaimer. 00016 // 00017 // 2. Redistributions in binary form must reproduce the above copyright 00018 // notice, this list of conditions and the following disclaimer in the 00019 // documentation and/or other materials provided with the distribution. 00020 // 00021 // 3. Neither the name of the Corporation nor the names of the 00022 // contributors may be used to endorse or promote products derived from 00023 // this software without specific prior written permission. 00024 // 00025 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY 00026 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00027 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 00028 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE 00029 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 00030 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 00031 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 00032 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 00033 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00034 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00035 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00036 // 00037 // Questions? Contact Michael A. Heroux (maherou@sandia.gov) 00038 // 00039 // *********************************************************************** 00040 //@HEADER 00041 #include "EpetraExt_CrsMatrixIn.h" 00042 #include "Epetra_Comm.h" 00043 #include "Epetra_CrsMatrix.h" 00044 #include "Epetra_Map.h" 00045 #include "Epetra_IntVector.h" 00046 #include "Epetra_IntSerialDenseVector.h" 00047 #include "Epetra_Import.h" 00048 #include "Epetra_Time.h" 00049 #include "Epetra_Util.h" 00050 00053 // Functions to read a MatrixMarket file and load it into a matrix. 00054 // Adapted from 00055 // Trilinos/packages/epetraext/src/inout/EpetraExt_CrsMatrixIn.cpp 00056 // Modified by Jon Berry and Karen Devine to make matrix reallocations 00057 // more efficient; rather than insert each non-zero separately, we 00058 // collect rows of non-zeros for insertion. 00059 // Modified by Karen Devine and Steve Plimpton to prevent all processors 00060 // from reading the same file at the same time (ouch!); chunks of the file 00061 // are read and broadcast by processor zero; each processor then extracts 00062 // its nonzeros from the chunk, sorts them by row, and inserts them into 00063 // the matrix. 00064 // The variable "chunk_read" can be changed to modify the size of the chunks 00065 // read from the file. Larger values of chunk_read lead to faster execution 00066 // and greater memory use. 00069 00070 using namespace EpetraExt; 00071 namespace EpetraExt { 00072 00073 static void sort_three(int* list, int *parlista, double *parlistb, 00074 int start, int end); 00076 int MatrixMarketFileToCrsMatrix(const char *filename, 00077 const Epetra_Comm & comm, Epetra_CrsMatrix * & A) 00078 { 00079 EPETRA_CHK_ERR(MatrixMarketFileToCrsMatrixHandle(filename, comm, A)); 00080 return(0); 00081 } 00082 00084 int MatrixMarketFileToCrsMatrix(const char *filename, 00085 const Epetra_Comm & comm, Epetra_CrsMatrix * & A, const bool transpose) 00086 { 00087 EPETRA_CHK_ERR(MatrixMarketFileToCrsMatrixHandle(filename, comm, A, 00088 0, 0, 0, 0, transpose)); 00089 return(0); 00090 } 00092 00093 int MatrixMarketFileToCrsMatrix(const char *filename, 00094 const Epetra_Comm & comm, Epetra_CrsMatrix * & A, const bool transpose, 00095 const bool verbose) 00096 { 00097 EPETRA_CHK_ERR(MatrixMarketFileToCrsMatrixHandle(filename, comm, A, 00098 0, 0, 0, 0, 00099 transpose, verbose)); 00100 return(0); 00101 } 00102 00104 int MatrixMarketFileToCrsMatrix(const char *filename, 00105 const Epetra_Map & rowMap, const Epetra_Map& rangeMap, 00106 const Epetra_Map& domainMap, Epetra_CrsMatrix * & A, const bool transpose, 00107 const bool verbose) 00108 { 00109 EPETRA_CHK_ERR(MatrixMarketFileToCrsMatrixHandle(filename, 00110 rowMap.Comm(), A, 00111 &rowMap, 0, 00112 &rangeMap, &domainMap, 00113 transpose, verbose)); 00114 return(0); 00115 } 00116 00118 int MatrixMarketFileToCrsMatrix(const char *filename, 00119 const Epetra_Map & rowMap, Epetra_CrsMatrix * & A, const bool transpose, 00120 const bool verbose) 00121 { 00122 EPETRA_CHK_ERR(MatrixMarketFileToCrsMatrixHandle(filename, 00123 rowMap.Comm(), A, 00124 &rowMap, 0, 0, 0, 00125 transpose, verbose)); 00126 return(0); 00127 } 00128 00130 int MatrixMarketFileToCrsMatrix(const char *filename, 00131 const Epetra_Map & rowMap, const Epetra_Map & colMap, 00132 Epetra_CrsMatrix * & A, const bool transpose, 00133 const bool verbose) 00134 { 00135 EPETRA_CHK_ERR(MatrixMarketFileToCrsMatrixHandle(filename, 00136 rowMap.Comm(), A, 00137 &rowMap, &colMap, 0, 0, 00138 transpose, verbose)); 00139 return(0); 00140 } 00141 00143 int MatrixMarketFileToCrsMatrix(const char *filename, 00144 const Epetra_Map & rowMap, const Epetra_Map & colMap, 00145 const Epetra_Map& rangeMap, const Epetra_Map& domainMap, 00146 Epetra_CrsMatrix * & A, const bool transpose, 00147 const bool verbose) 00148 { 00149 EPETRA_CHK_ERR(MatrixMarketFileToCrsMatrixHandle(filename, 00150 rowMap.Comm(), A, 00151 &rowMap, &colMap, 00152 &rangeMap, &domainMap, 00153 transpose, verbose)); 00154 return(0); 00155 } 00156 00158 int MatrixMarketFileToCrsMatrixHandle(const char *filename, 00159 const Epetra_Comm & comm, 00160 Epetra_CrsMatrix * & A, 00161 const Epetra_Map * rowMap, 00162 const Epetra_Map * colMap, 00163 const Epetra_Map * rangeMap, 00164 const Epetra_Map * domainMap, 00165 const bool transpose, 00166 const bool verbose 00167 ) 00168 { 00169 const int chunk_read = 500000; // Modify this variable to change the 00170 // size of the chunks read from the file. 00171 const int headerlineLength = 257; 00172 const int lineLength = 81; 00173 const int tokenLength = 35; 00174 char line[headerlineLength]; 00175 char token1[tokenLength]; 00176 char token2[tokenLength]; 00177 char token3[tokenLength]; 00178 char token4[tokenLength]; 00179 char token5[tokenLength]; 00180 int M, N, NZ; // Matrix dimensions 00181 int i; 00182 int me = comm.MyPID(); 00183 00184 Epetra_Time timer(comm); 00185 00186 // Make sure domain and range maps are either both defined or undefined 00187 if ((domainMap!=0 && rangeMap==0) || (domainMap==0 && rangeMap!=0)) { 00188 EPETRA_CHK_ERR(-3); 00189 } 00190 00191 // check maps to see if domain and range are 1-to-1 00192 00193 if (domainMap!=0) { 00194 if (!domainMap->UniqueGIDs()) {EPETRA_CHK_ERR(-2);} 00195 if (!rangeMap->UniqueGIDs()) {EPETRA_CHK_ERR(-2);} 00196 } 00197 else { 00198 // If domain and range are not specified, 00199 // rowMap becomes both and must be 1-to-1 if specified 00200 if (rowMap!=0) { 00201 if (!rowMap->UniqueGIDs()) {EPETRA_CHK_ERR(-2);} 00202 } 00203 } 00204 00205 FILE * handle = 0; 00206 if (me == 0) { 00207 if (verbose) cout << "Reading MatrixMarket file " << filename << endl; 00208 handle = fopen(filename,"r"); // Open file 00209 if (handle == 0) 00210 EPETRA_CHK_ERR(-1); // file not found 00211 00212 // Check first line, which should be 00213 // %%MatrixMarket matrix coordinate real general 00214 if(fgets(line, headerlineLength, handle)==0) { 00215 if (handle!=0) fclose(handle); 00216 EPETRA_CHK_ERR(-1); 00217 } 00218 if(sscanf(line, "%s %s %s %s %s", token1,token2,token3,token4,token5)==0) { 00219 if (handle!=0) fclose(handle); 00220 EPETRA_CHK_ERR(-1); 00221 } 00222 if (strcmp(token1, "%%MatrixMarket") || 00223 strcmp(token2, "matrix") || 00224 strcmp(token3, "coordinate") || 00225 strcmp(token4, "real") || 00226 strcmp(token5, "general")) { 00227 if (handle!=0) fclose(handle); 00228 EPETRA_CHK_ERR(-1); 00229 } 00230 00231 // Next, strip off header lines (which start with "%") 00232 do { 00233 if(fgets(line, headerlineLength, handle)==0) { 00234 if (handle!=0) fclose(handle); 00235 EPETRA_CHK_ERR(-1); 00236 } 00237 } while (line[0] == '%'); 00238 00239 // Next get problem dimensions: M, N, NZ 00240 if(sscanf(line, "%d %d %d", &M, &N, &NZ)==0) { 00241 if (handle!=0) fclose(handle); 00242 EPETRA_CHK_ERR(-1); 00243 } 00244 } 00245 comm.Broadcast(&M, 1, 0); 00246 comm.Broadcast(&N, 1, 0); 00247 comm.Broadcast(&NZ, 1, 0); 00248 00249 // Now create matrix using user maps if provided. 00250 00251 00252 // Now read in chunks of triplets and broadcast them to other processors. 00253 char *buffer = new char[chunk_read*lineLength]; 00254 int nchunk; 00255 int nmillion = 0; 00256 int nread = 0; 00257 int rlen; 00258 00259 // Storage for this processor's nonzeros. 00260 const int localblock = 100000; 00261 int localsize = NZ / comm.NumProc() + localblock; 00262 int *iv = (int *) malloc(localsize * sizeof(int)); 00263 int *jv = (int *) malloc(localsize * sizeof(int)); 00264 double *vv = (double *) malloc(localsize * sizeof(double)); 00265 int lnz = 0; // Number of non-zeros on this processor. 00266 00267 if (!iv || !jv || !vv) 00268 EPETRA_CHK_ERR(-1); 00269 00270 Epetra_Map *rowMap1; 00271 bool allocatedHere=false; 00272 if (rowMap != 0) 00273 rowMap1 = const_cast<Epetra_Map *>(rowMap); 00274 else { 00275 rowMap1 = new Epetra_Map(M, 0, comm); 00276 allocatedHere = true; 00277 } 00278 int ioffset = rowMap1->IndexBase()-1; 00279 int joffset = (colMap != 0 ? colMap->IndexBase()-1 : ioffset); 00280 00281 int rowmajor = 1; // Assume non-zeros are listed in row-major order, 00282 int prevrow = -1; // but test to detect otherwise. If non-zeros 00283 // are row-major, we can avoid the sort. 00284 00285 while (nread < NZ) { 00286 if (NZ-nread > chunk_read) nchunk = chunk_read; 00287 else nchunk = NZ - nread; 00288 00289 if (me == 0) { 00290 char *eof; 00291 rlen = 0; 00292 for (int i = 0; i < nchunk; i++) { 00293 eof = fgets(&buffer[rlen],lineLength,handle); 00294 if (eof == NULL) { 00295 fprintf(stderr, "%s", "Unexpected end of matrix file."); 00296 EPETRA_CHK_ERR(-1); 00297 } 00298 rlen += strlen(&buffer[rlen]); 00299 } 00300 buffer[rlen++]='\n'; 00301 } 00302 comm.Broadcast(&rlen, 1, 0); 00303 comm.Broadcast(buffer, rlen, 0); 00304 00305 buffer[rlen++] = '\0'; 00306 nread += nchunk; 00307 00308 // Process the received data, saving non-zeros belonging on this proc. 00309 char *lineptr = buffer; 00310 for (rlen = 0; rlen < nchunk; rlen++) { 00311 char *next = strchr(lineptr,'\n'); 00312 int I = atoi(strtok(lineptr," \t\n")) + ioffset; 00313 int J = atoi(strtok(NULL," \t\n")) + joffset; 00314 double V = atof(strtok(NULL," \t\n")); 00315 lineptr = next + 1; 00316 if (transpose) { 00317 // swap I and J indices. 00318 int tmp = I; 00319 I = J; 00320 J = tmp; 00321 } 00322 if (rowMap1->MyGID(I)) { 00323 // This processor keeps this non-zero. 00324 if (lnz >= localsize) { 00325 // Need more memory to store nonzeros. 00326 localsize += localblock; 00327 iv = (int *) realloc(iv, localsize * sizeof(int)); 00328 jv = (int *) realloc(jv, localsize * sizeof(int)); 00329 vv = (double *) realloc(vv, localsize * sizeof(double)); 00330 } 00331 iv[lnz] = I; 00332 jv[lnz] = J; 00333 vv[lnz] = V; 00334 lnz++; 00335 if (I < prevrow) rowmajor = 0; 00336 prevrow = I; 00337 } 00338 } 00339 00340 // Status check 00341 if (nread / 1000000 > nmillion) { 00342 nmillion++; 00343 if (verbose && me == 0) cout << nmillion << "M "; 00344 } 00345 } 00346 00347 delete [] buffer; 00348 00349 if (!rowmajor) { 00350 // Sort into row-major order (by iv) so can insert entire rows at once. 00351 // Reorder jv and vv to parallel iv. 00352 if (verbose && me == 0) cout << endl << " Sorting local nonzeros" << endl; 00353 sort_three(iv, jv, vv, 0, lnz-1); 00354 } 00355 00356 // Compute number of entries per local row for use in constructor; 00357 // saves reallocs in FillComplete. 00358 00359 // Now construct the matrix. 00360 // Count number of entries in each row so can use StaticProfile=2. 00361 if (verbose && me == 0) cout << endl << " Constructing the matrix" << endl; 00362 int numRows = rowMap1->NumMyElements(); 00363 int *numNonzerosPerRow = new int[numRows]; 00364 for (i = 0; i < numRows; i++) numNonzerosPerRow[i] = 0; 00365 for (i = 0; i < lnz; i++) 00366 numNonzerosPerRow[rowMap1->LID(iv[i])]++; 00367 00368 if (rowMap!=0 && colMap !=0) 00369 A = new Epetra_CrsMatrix(Copy, *rowMap, *colMap, numNonzerosPerRow); 00370 else if (rowMap!=0) { 00371 // Construct with StaticProfile=true since we know numNonzerosPerRow. 00372 // Less memory will be needed in FillComplete. 00373 A = new Epetra_CrsMatrix(Copy, *rowMap, numNonzerosPerRow, true); 00374 } 00375 else { 00376 // Construct with StaticProfile=true since we know numNonzerosPerRow. 00377 // Less memory will be needed in FillComplete. 00378 A = new Epetra_CrsMatrix(Copy, *rowMap1, numNonzerosPerRow, true); 00379 } 00380 A->SetTracebackMode(1); 00381 00382 // Rows are inserted in ascending global number, and the insertion uses numNonzerosPerRow. 00383 // Therefore numNonzerosPerRow must be permuted, as it was created in ascending local order. 00384 int *gidList = new int[numRows]; 00385 for (i = 0; i < numRows; i++) gidList[i] = rowMap1->GID(i); 00386 Epetra_Util::Sort(true,numRows,gidList,0,0,1,&numNonzerosPerRow); 00387 delete [] gidList; 00388 00389 // Insert the global values into the matrix row-by-row. 00390 if (verbose && me == 0) cout << " Inserting global values" << endl; 00391 for (int sum = 0, i = 0; i < numRows; i++) { 00392 if (numNonzerosPerRow[i]) { 00393 int ierr = A->InsertGlobalValues(iv[sum], numNonzerosPerRow[i], 00394 &vv[sum], &jv[sum]); 00395 if (ierr<0) EPETRA_CHK_ERR(ierr); 00396 sum += numNonzerosPerRow[i]; 00397 } 00398 } 00399 00400 delete [] numNonzerosPerRow; 00401 free(iv); 00402 free(jv); 00403 free(vv); 00404 00405 if (verbose && me == 0) cout << " Completing matrix fill" << endl; 00406 if (rangeMap != 0 && domainMap != 0) { 00407 EPETRA_CHK_ERR(A->FillComplete(*domainMap, *rangeMap)); 00408 } 00409 else if (M!=N) { 00410 Epetra_Map newDomainMap(N,rowMap1->IndexBase(), comm); 00411 EPETRA_CHK_ERR(A->FillComplete(newDomainMap, *rowMap1)); 00412 } 00413 else { 00414 EPETRA_CHK_ERR(A->FillComplete()); 00415 } 00416 00417 if (allocatedHere) delete rowMap1; 00418 00419 if (handle!=0) fclose(handle); 00420 double dt = timer.ElapsedTime(); 00421 if (verbose && me == 0) cout << "File Read time (secs): " << dt << endl; 00422 return(0); 00423 } 00424 00426 // Sorting values in array list in increasing order. Criteria is int. 00427 // Also rearrange values in arrays parlista and partlistb 00428 // to match the new order of list. 00429 00430 static void quickpart_list_inc_int ( 00431 int *list, int *parlista, double *parlistb, 00432 int start, int end, int *equal, int *larger) 00433 { 00434 int i, key, itmp; 00435 double dtmp; 00436 00437 key = list ? list[(end+start)/2] : 1; 00438 00439 *equal = *larger = start; 00440 for (i = start; i <= end; i++) 00441 if (list[i] < key) { 00442 itmp = parlista[i]; 00443 parlista[i] = parlista[*larger]; 00444 parlista[(*larger)] = parlista[*equal]; 00445 parlista[(*equal)] = itmp; 00446 dtmp = parlistb[i]; 00447 parlistb[i] = parlistb[*larger]; 00448 parlistb[(*larger)] = parlistb[*equal]; 00449 parlistb[(*equal)] = dtmp; 00450 itmp = list[i]; 00451 list[i] = list[*larger]; 00452 list[(*larger)++] = list[*equal]; 00453 list[(*equal)++] = itmp; 00454 } 00455 else if (list[i] == key) { 00456 itmp = parlista[i]; 00457 parlista[i] = parlista[*larger]; 00458 parlista[(*larger)] = itmp; 00459 dtmp = parlistb[i]; 00460 parlistb[i] = parlistb[*larger]; 00461 parlistb[(*larger)] = dtmp; 00462 list[i] = list[*larger]; 00463 list[(*larger)++] = key; 00464 } 00465 } 00466 00468 static void sort_three(int* list, int *parlista, double *parlistb, 00469 int start, int end) 00470 { 00471 int equal, larger; 00472 00473 if (start < end) { 00474 quickpart_list_inc_int(list, parlista, parlistb, start, end, 00475 &equal, &larger); 00476 sort_three(list, parlista, parlistb, start, equal-1); 00477 sort_three(list, parlista, parlistb, larger, end); 00478 } 00479 } 00480 00482 int MatlabFileToCrsMatrix(const char *filename, 00483 const Epetra_Comm & comm, 00484 Epetra_CrsMatrix * & A) 00485 { 00486 const int lineLength = 1025; 00487 char line[lineLength]; 00488 int I, J; 00489 double V; 00490 00491 FILE * handle = 0; 00492 00493 handle = fopen(filename,"r"); // Open file 00494 if (handle == 0) 00495 EPETRA_CHK_ERR(-1); // file not found 00496 00497 int numGlobalRows = 0; 00498 int numGlobalCols = 0; 00499 while(fgets(line, lineLength, handle)!=0) { 00500 if(sscanf(line, "%d %d %lg\n", &I, &J, &V)==0) {if (handle!=0) fclose(handle); EPETRA_CHK_ERR(-1);} 00501 if (I>numGlobalRows) numGlobalRows = I; 00502 if (J>numGlobalCols) numGlobalCols = J; 00503 } 00504 00505 if (handle!=0) fclose(handle); 00506 Epetra_Map rangeMap(numGlobalRows, 0, comm); 00507 Epetra_Map domainMap(numGlobalCols, 0, comm); 00508 A = new Epetra_CrsMatrix(Copy, rangeMap, 0); 00509 00510 // Now read in each triplet and store to the local portion of the matrix if the row is owned. 00511 const Epetra_Map & rowMap1 = A->RowMap(); 00512 00513 handle = 0; 00514 00515 handle = fopen(filename,"r"); // Open file 00516 if (handle == 0) 00517 EPETRA_CHK_ERR(-1); // file not found 00518 00519 while (fgets(line, lineLength, handle)!=0) { 00520 if(sscanf(line, "%d %d %lg\n", &I, &J, &V)==0) {if (handle!=0) fclose(handle); EPETRA_CHK_ERR(-1);} 00521 I--; J--; // Convert to Zero based 00522 if (rowMap1.MyGID(I)) { 00523 int ierr = A->InsertGlobalValues(I, 1, &V, &J); 00524 if (ierr<0) EPETRA_CHK_ERR(ierr); 00525 } 00526 } 00527 00528 EPETRA_CHK_ERR(A->FillComplete(domainMap, rangeMap)); 00529 00530 if (handle!=0) fclose(handle); 00531 return(0); 00532 } 00533 00534 int HypreFileToCrsMatrix(const char *filename, const Epetra_Comm &comm, Epetra_CrsMatrix *&Matrix){ 00535 int MyPID = comm.MyPID(); 00536 // This double will be in the format we want for the extension besides the leading zero 00537 double filePID = (double)MyPID/(double)100000; 00538 std::ostringstream stream; 00539 // Using setprecision() puts it in the string 00540 stream << std::setiosflags(std::ios::fixed) << std::setprecision(5) << filePID; 00541 // Then just ignore the first character 00542 std::string fileName(filename); 00543 fileName += stream.str().substr(1,7); 00544 // Open the file 00545 std::ifstream file(fileName.c_str()); 00546 string line; 00547 if(file.is_open()){ 00548 std::getline(file, line); 00549 int ilower, iupper; 00550 std::istringstream istream(line); 00551 // The first line of the file has the beginning and ending rows 00552 istream >> ilower; 00553 istream >> iupper; 00554 // Using those we can create a row map 00555 Epetra_Map RowMap(-1, iupper-ilower+1, 0, comm); 00556 Matrix = new Epetra_CrsMatrix(Copy, RowMap, 0); 00557 int currRow = -1; 00558 int counter = 0; 00559 std::vector<int> indices; 00560 std::vector<double> values; 00561 while(!file.eof()){ 00562 std::getline(file, line); 00563 std::istringstream lineStr(line); 00564 int row, col; 00565 double val; 00566 lineStr >> row; 00567 lineStr >> col; 00568 lineStr >> val; 00569 if(currRow == -1) currRow = row; // First line 00570 if(row == currRow){ 00571 // add to the vector 00572 counter = counter + 1; 00573 indices.push_back(col); 00574 values.push_back(val); 00575 } else { 00576 Matrix->InsertGlobalValues(currRow, counter, &values[0], &indices[0]); 00577 indices.clear(); 00578 values.clear(); 00579 counter = 0; 00580 currRow = row; 00581 // make a new vector 00582 indices.push_back(col); 00583 values.push_back(val); 00584 counter = counter + 1; 00585 } 00586 } 00587 Matrix->InsertGlobalValues(currRow, counter, &values[0], &indices[0]); 00588 Matrix->Comm().Barrier(); 00589 Matrix->FillComplete(); 00590 file.close(); 00591 return 0; 00592 } else { 00593 cout << "\nERROR:\nCouldn't open " << fileName << ".\n"; 00594 return -1; 00595 } 00596 } 00597 00598 } // namespace EpetraExt 00599
1.7.6.1