Chaste Release::3.1
PetscTools.cpp
00001 /*
00002 
00003 Copyright (c) 2005-2012, 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 (mNumProcessors == 1);
00085 }
00086 
00087 bool PetscTools::IsParallel()
00088 {
00089     CheckCache();
00090     return (mNumProcessors > 1);
00091 }
00092 
00093 unsigned PetscTools::GetNumProcs()
00094 {
00095     CheckCache();
00096     return mNumProcessors;
00097 }
00098 
00099 unsigned PetscTools::GetMyRank()
00100 {
00101     CheckCache();
00102     return mRank;
00103 }
00104 
00105 bool PetscTools::AmMaster()
00106 {
00107     CheckCache();
00108     return (mRank == MASTER_RANK || mIsolateProcesses);
00109 }
00110 
00111 bool PetscTools::AmTopMost()
00112 {
00113     CheckCache();
00114     return (mRank == mNumProcessors - 1);
00115 }
00116 
00117 // Little utility methods
00118 
00119 void PetscTools::Barrier(const std::string callerId)
00120 {
00121     CheckCache();
00122     if (mPetscIsInitialised && !mIsolateProcesses)
00123     {
00124 #ifdef DEBUG_BARRIERS
00125         std::cout << "DEBUG: proc " << PetscTools::GetMyRank() << ": Pre " << callerId << " Barrier " << mNumBarriers << "." << std::endl << std::flush;
00126 #endif
00127         PetscBarrier(PETSC_NULL);
00128 #ifdef DEBUG_BARRIERS
00129         std::cout << "DEBUG: proc " << PetscTools::GetMyRank() << ": Post " << callerId << " Barrier " << mNumBarriers++ << "." << std::endl << std::flush;
00130 #endif
00131     }
00132 }
00133 
00134 void PetscTools::BeginRoundRobin()
00135 {
00136     Barrier("PetscTools::RoundRobin"); // We want barriers both before all and after all, just in case
00137     const unsigned my_rank = GetMyRank();
00138     for (unsigned turn=0; turn<my_rank; turn++)
00139     {
00140         Barrier("PetscTools::RoundRobin");
00141     }
00142 }
00143 
00144 void PetscTools::EndRoundRobin()
00145 {
00146     const unsigned num_procs = GetNumProcs();
00147     for (unsigned turn=GetMyRank(); turn<num_procs; turn++)
00148     {
00149         Barrier("PetscTools::RoundRobin");
00150     }
00151 }
00152 
00153 void PetscTools::IsolateProcesses(bool isolate)
00154 {
00155     mIsolateProcesses = isolate;
00156 }
00157 
00158 bool PetscTools::ReplicateBool(bool flag)
00159 {
00160     CheckCache();
00161     unsigned my_flag = (unsigned) flag;
00162     unsigned anyones_flag_is_true = my_flag;
00163     if (mPetscIsInitialised && !mIsolateProcesses)
00164     {
00165         MPI_Allreduce(&my_flag, &anyones_flag_is_true, 1, MPI_UNSIGNED, MPI_MAX, PETSC_COMM_WORLD);
00166     }
00167     return (anyones_flag_is_true == 1);
00168 }
00169 
00170 void PetscTools::ReplicateException(bool flag)
00171 {
00172     bool anyones_error=ReplicateBool(flag);
00173     if (flag)
00174     {
00175         // Return control to exception thrower
00176         return;
00177     }
00178     if (anyones_error)
00179     {
00180         EXCEPTION("Another process threw an exception; bailing out.");
00181     }
00182 }
00183 
00184 // Vector & matrix creation routines
00185 
00186 Vec PetscTools::CreateVec(int size, int localSize, bool ignoreOffProcEntries)
00187 {
00188     assert(size >= 0); // There is one test where we create a zero-sized vector
00189     Vec ret;
00190     VecCreate(PETSC_COMM_WORLD, &ret);
00191     VecSetSizes(ret, localSize, size); // localSize usually defaults to PETSC_DECIDE
00192     VecSetFromOptions(ret);
00193 
00194     if (ignoreOffProcEntries)
00195     {
00196 #if (PETSC_VERSION_MAJOR == 3) //PETSc 3.x.x
00197         VecSetOption(ret, VEC_IGNORE_OFF_PROC_ENTRIES, PETSC_TRUE);
00198 #else
00199         VecSetOption(ret, VEC_IGNORE_OFF_PROC_ENTRIES);
00200 #endif
00201     }
00202 
00203     return ret;
00204 }
00205 
00206 Vec PetscTools::CreateVec(std::vector<double> data)
00207 {
00208     assert(data.size() > 0);
00209     Vec ret = CreateVec(data.size());
00210 
00211     double* p_ret;
00212     VecGetArray(ret, &p_ret);
00213     int lo, hi;
00214     VecGetOwnershipRange(ret, &lo, &hi);
00215 
00216     for (int global_index=lo; global_index<hi; global_index++)
00217     {
00218         int local_index = global_index - lo;
00219         p_ret[local_index] = data[global_index];
00220     }
00221     VecRestoreArray(ret, &p_ret);
00222 
00223     return ret;
00224 }
00225 
00226 Vec PetscTools::CreateAndSetVec(int size, double value)
00227 {
00228     assert(size > 0);
00229     Vec ret = CreateVec(size);
00230 
00231 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
00232     VecSet(&value, ret);
00233 #else
00234     VecSet(ret, value);
00235 #endif
00236 
00237     return ret;
00238 }
00239 
00240 void PetscTools::SetupMat(Mat& rMat, int numRows, int numColumns,
00241                           unsigned rowPreallocation,
00242                           int numLocalRows,
00243                           int numLocalColumns,
00244                           bool ignoreOffProcEntries)
00245 {
00246     assert(numRows > 0);
00247     assert(numColumns > 0);
00248     if ((int) rowPreallocation>numColumns)
00249     {
00250         WARNING("Preallocation failure: requested number of nonzeros per row greater than number of columns");//+rowPreallocation+">"+numColumns);
00251         rowPreallocation = numColumns;
00252     }
00253 
00254 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
00255     MatCreate(PETSC_COMM_WORLD,numLocalRows,numLocalColumns,numRows,numColumns,&rMat);
00256 #else //New API
00257     MatCreate(PETSC_COMM_WORLD,&rMat);
00258     MatSetSizes(rMat,numLocalRows,numLocalColumns,numRows,numColumns);
00259 #endif
00260 
00261     if (PetscTools::IsSequential())
00262     {
00263         MatSetType(rMat, MATSEQAIJ);
00264         if (rowPreallocation > 0)
00265         {
00266             MatSeqAIJSetPreallocation(rMat, rowPreallocation, PETSC_NULL);
00267         }
00268     }
00269     else
00270     {
00271         MatSetType(rMat, MATMPIAIJ);
00272         if (rowPreallocation > 0)
00273         {
00275             MatMPIAIJSetPreallocation(rMat, rowPreallocation, PETSC_NULL, (PetscInt) (rowPreallocation*0.7), PETSC_NULL);
00276         }
00277     }
00278 
00279     MatSetFromOptions(rMat);
00280 
00281     if (ignoreOffProcEntries)//&& IsParallel())
00282     {
00283         if (rowPreallocation == 0)
00284         {
00285             // We aren't allowed to do non-zero allocation after setting MAT_IGNORE_OFF_PROC_ENTRIES
00286             WARNING("Ignoring MAT_IGNORE_OFF_PROC_ENTRIES flag because we might set non-zeroes later");
00287         }
00288         else
00289         {
00290 #if (PETSC_VERSION_MAJOR == 3) //PETSc 3.x.x
00291             MatSetOption(rMat, MAT_IGNORE_OFF_PROC_ENTRIES, PETSC_TRUE);
00292 #else
00293             MatSetOption(rMat, MAT_IGNORE_OFF_PROC_ENTRIES);
00294 #endif
00295         }
00296     }
00297 }
00298 
00299 void PetscTools::DumpPetscObject(const Mat& rMat, const std::string& rOutputFileFullPath)
00300 {
00301     PetscViewer view;
00302 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
00303     PetscViewerFileType type = PETSC_FILE_CREATE;
00304 #else
00305     PetscFileMode type = FILE_MODE_WRITE;
00306 #endif
00307 
00308     PetscViewerBinaryOpen(PETSC_COMM_WORLD, rOutputFileFullPath.c_str(), type, &view);
00309     MatView(rMat, view);
00310     PetscViewerDestroy(PETSC_DESTROY_PARAM(view));
00311 }
00312 
00313 void PetscTools::DumpPetscObject(const Vec& rVec, const std::string& rOutputFileFullPath)
00314 {
00315     PetscViewer view;
00316 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
00317     PetscViewerFileType type = PETSC_FILE_CREATE;
00318 #else
00319     PetscFileMode type = FILE_MODE_WRITE;
00320 #endif
00321 
00322     PetscViewerBinaryOpen(PETSC_COMM_WORLD, rOutputFileFullPath.c_str(), type, &view);
00323     VecView(rVec, view);
00324     PetscViewerDestroy(PETSC_DESTROY_PARAM(view));
00325 }
00326 
00327 void PetscTools::ReadPetscObject(Mat& rMat, const std::string& rOutputFileFullPath, Vec rParallelLayout)
00328 {
00329     /*
00330      * PETSc (as of 3.1) doesn't provide any method for loading a Mat object with a user-defined parallel
00331      * layout, i.e. there's no equivalent to VecLoadIntoVector for Mat's.
00332      *
00333      * It seems to be in their future plans though: http://lists.mcs.anl.gov/pipermail/petsc-users/2010-March/006062.html
00334      */
00335 
00336     PetscViewer view;
00337 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
00338     PetscViewerFileType type = PETSC_FILE_RDONLY;
00339 #else
00340     PetscFileMode type = FILE_MODE_READ;
00341 #endif
00342 
00343     PetscViewerBinaryOpen(PETSC_COMM_WORLD, rOutputFileFullPath.c_str(),
00344                           type, &view);
00345 
00346 #if (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR >= 2) //PETSc 3.2 or later
00347     MatCreate(PETSC_COMM_WORLD,&rMat);
00348     MatSetType(rMat,MATMPIAIJ);
00349     MatLoad(rMat,view);
00350 #else
00351     MatLoad(view, MATMPIAIJ, &rMat);
00352 #endif
00353 
00354     PetscViewerDestroy(PETSC_DESTROY_PARAM(view));
00355 
00356     if (rParallelLayout != NULL)
00357     {
00358         /*
00359          * The idea is to copy rMat into a matrix that has the appropriate
00360          * parallel layout. Inefficient...
00361          */
00362         PetscInt num_rows, num_local_rows;
00363 
00364         VecGetSize(rParallelLayout, &num_rows);
00365         VecGetLocalSize(rParallelLayout, &num_local_rows);
00366 
00367         Mat temp_mat;
00369         PetscTools::SetupMat(temp_mat, num_rows, num_rows, 100, num_local_rows, num_local_rows, false);
00370 
00371         MatCopy(rMat, temp_mat, DIFFERENT_NONZERO_PATTERN);
00372 
00373         PetscTools::Destroy(rMat);
00374         rMat = temp_mat;
00375     }
00376 }
00377 
00378 void PetscTools::ReadPetscObject(Vec& rVec, const std::string& rOutputFileFullPath, Vec rParallelLayout)
00379 {
00380     PetscViewer view;
00381 #if (PETSC_VERSION_MAJOR == 2 && PETSC_VERSION_MINOR == 2) //PETSc 2.2
00382     PetscViewerFileType type = PETSC_FILE_RDONLY;
00383 #else
00384     PetscFileMode type = FILE_MODE_READ;
00385 #endif
00386 
00387     PetscViewerBinaryOpen(PETSC_COMM_WORLD, rOutputFileFullPath.c_str(),
00388                           type, &view);
00389     if (rParallelLayout == NULL)
00390     {
00391 #if (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR >= 2) //PETSc 3.2 or later
00392         VecCreate(PETSC_COMM_WORLD,&rVec);
00393         VecSetType(rVec,VECMPI);
00394         VecLoad(rVec,view);
00395 #else
00396         VecLoad(view, VECMPI, &rVec);
00397 #endif
00398     }
00399     else
00400     {
00401         VecDuplicate(rParallelLayout, &rVec);
00402 #if (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR >= 2) //PETSc 3.2 or later
00403         VecLoad(rVec,view);
00404 #else
00405         VecLoadIntoVector(view, rVec);
00406 #endif
00407     }
00408     PetscViewerDestroy(PETSC_DESTROY_PARAM(view));
00409 }
00410 
00411 bool PetscTools::HasParMetis()
00412 {
00413 #ifdef __INTEL_COMPILER
00414     //Old versions of the intel compiler can result in a PETSC ERROR and the program aborting if parmetis is checked for before
00415     //some other calls to Petsc are made. This nasty hack ensures that the HasParMetis method always works on Intel
00416     Mat mat;
00417     PetscTools::SetupMat(mat, 1, 1, 1);
00418     PetscTools::Destroy(mat);
00419 #endif
00420 
00421     MatPartitioning part;
00422     MatPartitioningCreate(PETSC_COMM_WORLD, &part);
00423 
00424 #if (PETSC_VERSION_MAJOR == 2 || (PETSC_VERSION_MAJOR == 3 && PETSC_VERSION_MINOR < 2))
00425     PetscErrorCode parmetis_installed_error = MatPartitioningSetType(part,MAT_PARTITIONING_PARMETIS);
00426 #else
00427     PetscErrorCode parmetis_installed_error = MatPartitioningSetType(part,MATPARTITIONINGPARMETIS);
00428 #endif
00429     // Note that this method probably leaks memory inside PETSc because if MatPartitioningCreate fails
00430     // then there isn't a proper handle to destroy.
00431     MatPartitioningDestroy(PETSC_DESTROY_PARAM(part));
00432     return (parmetis_installed_error == 0);
00433 }
00434 
00435 
00436