PetscTools.cpp

00001 /*
00002 
00003 Copyright (c) 2005-2014, University of Oxford.
00004 All rights reserved.
00005 
00006 University of Oxford means the Chancellor, Masters and Scholars of the
00007 University of Oxford, having an administrative office at Wellington
00008 Square, Oxford OX1 2JD, UK.
00009 
00010 This file is part of Chaste.
00011 
00012 Redistribution and use in source and binary forms, with or without
00013 modification, are permitted provided that the following conditions are met:
00014  * Redistributions of source code must retain the above copyright notice,
00015    this list of conditions and the following disclaimer.
00016  * Redistributions in binary form must reproduce the above copyright notice,
00017    this list of conditions and the following disclaimer in the documentation
00018    and/or other materials provided with the distribution.
00019  * Neither the name of the University of Oxford nor the names of its
00020    contributors may be used to endorse or promote products derived from this
00021    software without specific prior written permission.
00022 
00023 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00024 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00025 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00026 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
00027 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00028 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00029 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00030 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
00032 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00033 
00034 */
00035 
00036 #include "PetscTools.hpp"
00037 #include "Exception.hpp"
00038 #include "Warnings.hpp"
00039 #include <iostream>
00040 #include <sstream>
00041 #include <cassert>
00042 #include <cstring> // For strcmp etc. Needed in gcc-4.3
00043 
00044 bool PetscTools::mPetscIsInitialised = false;
00045 unsigned PetscTools::mNumProcessors = 0;
00046 unsigned PetscTools::mRank = 0;
00047 bool PetscTools::mIsolateProcesses = false;
00048 //unsigned PetscTools::mMaxNumNonzerosIfMatMpiAij = 54;
00049 
00050 #ifdef DEBUG_BARRIERS
00051 unsigned PetscTools::mNumBarriers = 0u;
00052 #endif
00053 
00054 void PetscTools::ResetCache()
00055 {
00056     PetscTruth is_there;
00057     PetscInitialized(&is_there);
00058     if (is_there)
00059     {
00060         mPetscIsInitialised = true;
00061 
00062         PetscInt num_procs;
00063         MPI_Comm_size(PETSC_COMM_WORLD, &num_procs);
00064         mNumProcessors = (unsigned) num_procs;
00065 
00066         PetscInt my_rank;
00067         MPI_Comm_rank(PETSC_COMM_WORLD, &my_rank);
00068         mRank = (unsigned) my_rank;
00069     }
00070     else
00071     {
00072         // No PETSc
00073         mPetscIsInitialised = false;
00074         mNumProcessors = 1;
00075         mRank = 0;
00076     }
00077 }
00078 
00079 // Information methods
00080 
00081 bool PetscTools::IsSequential()
00082 {
00083     CheckCache();
00084     return (mIsolateProcesses || mNumProcessors == 1);
00085 }
00086 
00087 bool PetscTools::IsParallel()
00088 {
00089     CheckCache();
00090     return (mNumProcessors > 1);
00091 }
00092 
00093 bool PetscTools::IsIsolated()
00094 {
00095     return mIsolateProcesses;
00096 }
00097 
00098 unsigned PetscTools::GetNumProcs()
00099 {
00100     CheckCache();
00101     return mNumProcessors;
00102 }
00103 
00104 unsigned PetscTools::GetMyRank()
00105 {
00106     CheckCache();
00107     return mRank;
00108 }
00109 
00110 bool PetscTools::AmMaster()
00111 {
00112     CheckCache();
00113     return (mRank == MASTER_RANK || mIsolateProcesses);
00114 }
00115 
00116 bool PetscTools::AmTopMost()
00117 {
00118     CheckCache();
00119     return (mRank == mNumProcessors - 1 || mIsolateProcesses);
00120 }
00121 
00122 // Little utility methods
00123 
00124 void PetscTools::Barrier(const std::string callerId)
00125 {
00126     CheckCache();
00127     if (mPetscIsInitialised && !mIsolateProcesses)
00128     {
00129 #ifdef DEBUG_BARRIERS
00130         std::cout << "DEBUG: proc " << PetscTools::GetMyRank() << ": Pre " << callerId << " Barrier " << mNumBarriers << "." << std::endl << std::flush;
00131 #endif
00132         PetscBarrier(PETSC_NULL);
00133 #ifdef DEBUG_BARRIERS
00134         std::cout << "DEBUG: proc " << PetscTools::GetMyRank() << ": Post " << callerId << " Barrier " << mNumBarriers++ << "." << std::endl << std::flush;
00135 #endif
00136     }
00137 }
00138 
00139 void PetscTools::BeginRoundRobin()
00140 {
00141     Barrier("PetscTools::RoundRobin"); // We want barriers both before all and after all, just in case
00142     const unsigned my_rank = GetMyRank();
00143     for (unsigned turn=0; turn<my_rank; turn++)
00144     {
00145         Barrier("PetscTools::RoundRobin");
00146     }
00147 }
00148 
00149 void PetscTools::EndRoundRobin()
00150 {
00151     const unsigned num_procs = GetNumProcs();
00152     for (unsigned turn=GetMyRank(); turn<num_procs; turn++)
00153     {
00154         Barrier("PetscTools::RoundRobin");
00155     }
00156 }
00157 
00158 void PetscTools::IsolateProcesses(bool isolate)
00159 {
00160     mIsolateProcesses = isolate;
00161 }
00162 
00163 MPI_Comm PetscTools::GetWorld()
00164 {
00165     if (mIsolateProcesses)
00166     {
00167         return PETSC_COMM_SELF;
00168     }
00169     else
00170     {
00171         return PETSC_COMM_WORLD;
00172     }
00173 }
00174 
00175 bool PetscTools::ReplicateBool(bool flag)
00176 {
00177     CheckCache();
00178     unsigned my_flag = (unsigned) flag;
00179     unsigned anyones_flag_is_true = my_flag;
00180     if (mPetscIsInitialised && !mIsolateProcesses)
00181     {
00182         MPI_Allreduce(&my_flag, &anyones_flag_is_true, 1, MPI_UNSIGNED, MPI_MAX, PETSC_COMM_WORLD);
00183     }
00184     return (anyones_flag_is_true == 1);
00185 }
00186 
00187 void PetscTools::ReplicateException(bool flag)
00188 {
00189     bool anyones_error=ReplicateBool(flag);
00190     if (flag)
00191     {
00192         // Return control to exception thrower
00193         return;
00194     }
00195     if (anyones_error)
00196     {
00197         EXCEPTION("Another process threw an exception; bailing out.");
00198     }
00199 }
00200 
00201 // Vector & matrix creation routines
00202 
00203 Vec PetscTools::CreateVec(int size, int localSize, bool ignoreOffProcEntries)
00204 {
00205     assert(size >= 0); // There is one test where we create a zero-sized vector
00206     Vec ret;
00207     VecCreate(PETSC_COMM_WORLD, &ret);
00208     VecSetSizes(ret, localSize, size); // localSize usually defaults to PETSC_DECIDE
00209     VecSetFromOptions(ret);
00210 
00211     if (ignoreOffProcEntries)
00212     {
00213 #if (PETSC_VERSION_MAJOR == 3) //PETSc 3.x.x
00214         VecSetOption(ret, VEC_IGNORE_OFF_PROC_ENTRIES, PETSC_TRUE);
00215 #else
00216         VecSetOption(ret, VEC_IGNORE_OFF_PROC_ENTRIES);
00217 #endif
00218     }
00219 
00220     return ret;
00221 }
00222 
00223 Vec PetscTools::CreateVec(std::vector<double> data)
00224 {
00225     assert(data.size() > 0);
00226     Vec ret = CreateVec(data.size());
00227 
00228     double* p_ret;
00229     VecGetArray(ret, &p_ret);
00230     int lo, hi;
00231     VecGetOwnershipRange(ret, &lo, &hi);
00232 
00233     for (int global_index=lo; global_index<hi; global_index++)
00234     {
00235         int local_index = global_index - lo;
00236         p_ret[local_index] = data[global_index];
00237     }
00238     VecRestoreArray(ret, &p_ret);
00239 
00240     return ret;
00241 }
00242 
00243 Vec PetscTools::CreateAndSetVec(int size, double value)
00244 {
00245     assert(size > 0);
00246     Vec ret = CreateVec(size);
00247 
00248 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
00249     VecSet(&value, ret);
00250 #else
00251     VecSet(ret, value);
00252 #endif
00253 
00254     return ret;
00255 }
00256 
00257 void PetscTools::SetupMat(Mat& rMat, int numRows, int numColumns,
00258                           unsigned rowPreallocation,
00259                           int numLocalRows,
00260                           int numLocalColumns,
00261                           bool ignoreOffProcEntries,
00262                           bool newAllocationError)
00263 {
00264     assert(numRows > 0);
00265     assert(numColumns > 0);
00266     if ((int) rowPreallocation>numColumns)
00267     {
00268         WARNING("Preallocation failure: requested number of nonzeros per row greater than number of columns");//+rowPreallocation+">"+numColumns);
00269         rowPreallocation = numColumns;
00270     }
00271 
00272 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
00273     MatCreate(PETSC_COMM_WORLD,numLocalRows,numLocalColumns,numRows,numColumns,&rMat);
00274 #else //New API
00275     MatCreate(PETSC_COMM_WORLD,&rMat);
00276     MatSetSizes(rMat,numLocalRows,numLocalColumns,numRows,numColumns);
00277 #endif
00278 
00279     if (PetscTools::IsSequential())
00280     {
00281         MatSetType(rMat, MATSEQAIJ);
00282         if (rowPreallocation > 0)
00283         {
00284             MatSeqAIJSetPreallocation(rMat, rowPreallocation, PETSC_NULL);
00285         }
00286     }
00287     else
00288     {
00289         MatSetType(rMat, MATMPIAIJ);
00290         if (rowPreallocation > 0)
00291         {
00292             MatMPIAIJSetPreallocation(rMat, rowPreallocation, PETSC_NULL, rowPreallocation, PETSC_NULL);
00293         }
00294     }
00295 
00296     MatSetFromOptions(rMat);
00297 
00298     if (ignoreOffProcEntries)//&& IsParallel())
00299     {
00300         if (rowPreallocation == 0)
00301         {
00302             // We aren't allowed to do non-zero allocation after setting MAT_IGNORE_OFF_PROC_ENTRIES
00303             WARNING("Ignoring MAT_IGNORE_OFF_PROC_ENTRIES flag because we might set non-zeroes later");
00304         }
00305         else
00306         {
00307 #if (PETSC_VERSION_MAJOR == 3) //PETSc 3.x.x
00308             MatSetOption(rMat, MAT_IGNORE_OFF_PROC_ENTRIES, PETSC_TRUE);
00309 #else
00310             MatSetOption(rMat, MAT_IGNORE_OFF_PROC_ENTRIES);
00311 #endif
00312         }
00313     }
00314 #if (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR >= 3) //PETSc 3.3 or later
00315     if (newAllocationError == false)
00316     {
00317         MatSetOption(rMat, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE);
00318     }
00319 #endif
00320 }
00321 
00322 void PetscTools::DumpPetscObject(const Mat& rMat, const std::string& rOutputFileFullPath)
00323 {
00324     PetscViewer view;
00325 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
00326     PetscViewerFileType type = PETSC_FILE_CREATE;
00327 #else
00328     PetscFileMode type = FILE_MODE_WRITE;
00329 #endif
00330 
00331     PetscViewerBinaryOpen(PETSC_COMM_WORLD, rOutputFileFullPath.c_str(), type, &view);
00332     MatView(rMat, view);
00333     PetscViewerDestroy(PETSC_DESTROY_PARAM(view));
00334 }
00335 
00336 void PetscTools::DumpPetscObject(const Vec& rVec, const std::string& rOutputFileFullPath)
00337 {
00338     PetscViewer view;
00339 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
00340     PetscViewerFileType type = PETSC_FILE_CREATE;
00341 #else
00342     PetscFileMode type = FILE_MODE_WRITE;
00343 #endif
00344 
00345     PetscViewerBinaryOpen(PETSC_COMM_WORLD, rOutputFileFullPath.c_str(), type, &view);
00346     VecView(rVec, view);
00347     PetscViewerDestroy(PETSC_DESTROY_PARAM(view));
00348 }
00349 
00350 void PetscTools::ReadPetscObject(Mat& rMat, const std::string& rOutputFileFullPath, Vec rParallelLayout)
00351 {
00352     /*
00353      * PETSc (as of 3.1) doesn't provide any method for loading a Mat object with a user-defined parallel
00354      * layout, i.e. there's no equivalent to VecLoadIntoVector for Mat's.
00355      *
00356      * It seems to be in their future plans though: http://lists.mcs.anl.gov/pipermail/petsc-users/2010-March/006062.html
00357      */
00358 
00359     PetscViewer view;
00360 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
00361     PetscViewerFileType type = PETSC_FILE_RDONLY;
00362 #else
00363     PetscFileMode type = FILE_MODE_READ;
00364 #endif
00365 
00366     PetscViewerBinaryOpen(PETSC_COMM_WORLD, rOutputFileFullPath.c_str(),
00367                           type, &view);
00368 
00369 #if (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR >= 2) //PETSc 3.2 or later
00370     MatCreate(PETSC_COMM_WORLD,&rMat);
00371     MatSetType(rMat,MATMPIAIJ);
00372     MatLoad(rMat,view);
00373 #else
00374     MatLoad(view, MATMPIAIJ, &rMat);
00375 #endif
00376 
00377     PetscViewerDestroy(PETSC_DESTROY_PARAM(view));
00378 
00379     if (rParallelLayout != NULL)
00380     {
00381         /*
00382          * The idea is to copy rMat into a matrix that has the appropriate
00383          * parallel layout. Inefficient...
00384          */
00385         PetscInt num_rows, num_local_rows;
00386 
00387         VecGetSize(rParallelLayout, &num_rows);
00388         VecGetLocalSize(rParallelLayout, &num_local_rows);
00389 
00390         Mat temp_mat;
00392         PetscTools::SetupMat(temp_mat, num_rows, num_rows, 100, num_local_rows, num_local_rows, false);
00393 
00394         MatCopy(rMat, temp_mat, DIFFERENT_NONZERO_PATTERN);
00395 
00396         PetscTools::Destroy(rMat);
00397         rMat = temp_mat;
00398     }
00399 }
00400 
00401 void PetscTools::ReadPetscObject(Vec& rVec, const std::string& rOutputFileFullPath, Vec rParallelLayout)
00402 {
00403     PetscViewer view;
00404 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
00405     PetscViewerFileType type = PETSC_FILE_RDONLY;
00406 #else
00407     PetscFileMode type = FILE_MODE_READ;
00408 #endif
00409 
00410     PetscViewerBinaryOpen(PETSC_COMM_WORLD, rOutputFileFullPath.c_str(),
00411                           type, &view);
00412     if (rParallelLayout == NULL)
00413     {
00414 #if (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR >= 2) //PETSc 3.2 or later
00415         VecCreate(PETSC_COMM_WORLD,&rVec);
00416         VecSetType(rVec,VECMPI);
00417         VecLoad(rVec,view);
00418 #else
00419         VecLoad(view, VECMPI, &rVec);
00420 #endif
00421     }
00422     else
00423     {
00424         VecDuplicate(rParallelLayout, &rVec);
00425 #if (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR >= 2) //PETSc 3.2 or later
00426         VecLoad(rVec,view);
00427 #else
00428         VecLoadIntoVector(view, rVec);
00429 #endif
00430     }
00431     PetscViewerDestroy(PETSC_DESTROY_PARAM(view));
00432 }
00433 
00434 bool PetscTools::HasParMetis()
00435 {
00436 #ifdef __INTEL_COMPILER
00437     //Old versions of the intel compiler can result in a PETSC ERROR and the program aborting if parmetis is checked for before
00438     //some other calls to Petsc are made. This nasty hack ensures that the HasParMetis method always works on Intel
00439     Mat mat;
00440     PetscTools::SetupMat(mat, 1, 1, 1);
00441     PetscTools::Destroy(mat);
00442 #endif
00443 
00444     MatPartitioning part;
00445     MatPartitioningCreate(PETSC_COMM_WORLD, &part);
00446 
00447 
00448     // We are expecting an error from PETSC on systems that don't have the interface, so suppress it
00449     // in case it aborts
00450     PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL);
00451 
00452 #if (PETSC_VERSION_MAJOR == 2 || (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR < 2))
00453     PetscErrorCode parmetis_installed_error = MatPartitioningSetType(part,MAT_PARTITIONING_PARMETIS);
00454 #else
00455     PetscErrorCode parmetis_installed_error = MatPartitioningSetType(part,MATPARTITIONINGPARMETIS);
00456 #endif
00457 
00458     // Stop supressing error
00459     PetscPopErrorHandler();
00460 
00461     // Note that this method probably leaks memory inside PETSc because if MatPartitioningCreate fails
00462     // then there isn't a proper handle to destroy.
00463     MatPartitioningDestroy(PETSC_DESTROY_PARAM(part));
00464     return (parmetis_installed_error == 0);
00465 }

Generated by  doxygen 1.6.2