SparsityPattern::Build Class Reference

#include <dof_map.h>

List of all members.

Public Member Functions

 Build (const MeshBase &mesh_in, const DofMap &dof_map_in, const CouplingMatrix *dof_coupling_in, const bool implicit_neighbor_dofs_in, const bool need_full_sparsity_pattern_in)
 Build (Build &other, Threads::split)
void operator() (const ConstElemRange &range)
void join (const Build &other)

Public Attributes

SparsityPattern::Graph sparsity_pattern
std::vector< unsigned int > n_nz
std::vector< unsigned int > n_oz

Private Attributes

const MeshBasemesh
const DofMapdof_map
const CouplingMatrixdof_coupling
const bool implicit_neighbor_dofs
const bool need_full_sparsity_pattern


Detailed Description

This helper class can be called on multiple threads to compute the sparsity pattern (or graph) of the sparse matrix resulting from the discretization. This pattern may be used directly by a particular sparse matrix format (e.g. LaspackMatrix) or indirectly (e.g. PetscMatrix). In the latter case the number of nonzeros per row of the matrix is needed for efficient preallocation. In this case it suffices to provide estimate (but bounding) values, and in this case the threaded method can take some short-cuts for efficiency.

Definition at line 100 of file dof_map.h.


Constructor & Destructor Documentation

SparsityPattern::Build::Build ( const MeshBase mesh_in,
const DofMap dof_map_in,
const CouplingMatrix dof_coupling_in,
const bool  implicit_neighbor_dofs_in,
const bool  need_full_sparsity_pattern_in 
) [inline]

Definition at line 115 of file dof_map.h.

00119                                                      :
00120       mesh(mesh_in),
00121       dof_map(dof_map_in),
00122       dof_coupling(dof_coupling_in),
00123       implicit_neighbor_dofs(implicit_neighbor_dofs_in),
00124       need_full_sparsity_pattern(need_full_sparsity_pattern_in)
00125     {}

SparsityPattern::Build::Build ( Build other,
Threads::split   
) [inline]

Definition at line 127 of file dof_map.h.

00127                                        :
00128       mesh(other.mesh),
00129       dof_map(other.dof_map),
00130       dof_coupling(other.dof_coupling),
00131       implicit_neighbor_dofs(other.implicit_neighbor_dofs),
00132       need_full_sparsity_pattern(other.need_full_sparsity_pattern)
00133     {}


Member Function Documentation

void SparsityPattern::Build::join ( const Build other  ) 

Definition at line 2131 of file dof_map.C.

References dof_map, DofMap::end_dof(), DofMap::first_dof(), mesh, std::min(), DofMap::n_dofs(), DofMap::n_dofs_on_processor(), n_nz, n_oz, need_full_sparsity_pattern, MeshBase::processor_id(), and sparsity_pattern.

02132 {
02133   const unsigned int proc_id           = mesh.processor_id();
02134   const unsigned int n_global_dofs     = dof_map.n_dofs();
02135   const unsigned int n_dofs_on_proc    = dof_map.n_dofs_on_processor(proc_id);
02136   const unsigned int first_dof_on_proc = dof_map.first_dof(proc_id);
02137   const unsigned int end_dof_on_proc   = dof_map.end_dof(proc_id);
02138 
02139   libmesh_assert (sparsity_pattern.size() ==  other.sparsity_pattern.size());
02140   libmesh_assert (n_nz.size() == sparsity_pattern.size());
02141   libmesh_assert (n_oz.size() == sparsity_pattern.size());
02142   
02143   for (unsigned int r=0; r<n_dofs_on_proc; r++)
02144     {
02145       // incriment the number of on and off-processor nonzeros in this row
02146       // (note this will be an upper bound unless we need the full sparsity pattern)
02147       n_nz[r] += other.n_nz[r];  n_nz[r] = std::min(n_nz[r], n_dofs_on_proc);
02148       n_oz[r] += other.n_oz[r];  n_oz[r] = std::min(n_oz[r], n_global_dofs-n_nz[r]);
02149 
02150       if (need_full_sparsity_pattern)
02151         {
02152           SparsityPattern::Row       &my_row    = sparsity_pattern[r];
02153           const SparsityPattern::Row &their_row = other.sparsity_pattern[r];
02154       
02155           // simple copy if I have no dofs
02156           if (my_row.empty())
02157             my_row = their_row;
02158           
02159           // otherwise add their DOFs to mine, resort, and re-unique the row
02160           else if (!their_row.empty()) // do nothing for the trivial case where 
02161             {                          // their row is empty
02162               my_row.insert (my_row.end(),
02163                              their_row.begin(),
02164                              their_row.end());
02165 
02166               // We cannot use SparsityPattern::sort_row() here because it expects
02167               // the [begin,middle) [middle,end) to be non-overlapping.  This is not
02168               // necessarily the case here, so use std::sort()
02169               std::sort (my_row.begin(), my_row.end());
02170          
02171               my_row.erase(std::unique (my_row.begin(), my_row.end()), my_row.end());
02172             }
02173 
02174           // fix the number of on and off-processor nonzeros in this row
02175           n_nz[r] = n_oz[r] = 0;
02176           
02177           for (unsigned int j=0; j<my_row.size(); j++)
02178             if ((my_row[j] < first_dof_on_proc) || (my_row[j] >= end_dof_on_proc))
02179               n_oz[r]++;
02180             else
02181               n_nz[r]++;
02182         }
02183     }
02184 }

void SparsityPattern::Build::operator() ( const ConstElemRange range  ) 

Definition at line 1814 of file dof_map.C.

References Elem::active_family_tree_by_neighbor(), StoredRange< iterator_type, object_type >::begin(), dof_coupling, DofMap::dof_indices(), dof_map, CouplingMatrix::empty(), StoredRange< iterator_type, object_type >::end(), DofMap::end_dof(), DofMap::find_connected_dofs(), DofMap::first_dof(), implicit_neighbor_dofs, mesh, DofMap::n_dofs_on_processor(), n_nz, n_oz, Elem::n_sides(), DofMap::n_variables(), Elem::neighbor(), MeshBase::processor_id(), CouplingMatrix::size(), SparsityPattern::sort_row(), and sparsity_pattern.

01815 {
01816   // Compute the sparsity structure of the global matrix.  This can be
01817   // fed into a PetscMatrix to allocate exacly the number of nonzeros
01818   // necessary to store the matrix.  This algorithm should be linear
01819   // in the (# of elements)*(# nodes per element)
01820   const unsigned int proc_id           = mesh.processor_id();
01821   const unsigned int n_dofs_on_proc    = dof_map.n_dofs_on_processor(proc_id);
01822   const unsigned int first_dof_on_proc = dof_map.first_dof(proc_id);
01823   const unsigned int end_dof_on_proc   = dof_map.end_dof(proc_id);
01824 
01825   sparsity_pattern.resize(n_dofs_on_proc);
01826   
01827   // If the user did not explicitly specify the DOF coupling
01828   // then all the DOFS are coupled to each other.  Furthermore,
01829   // we can take a shortcut and do this more quickly here.  So
01830   // we use an if-test.
01831   if ((dof_coupling == NULL) || (dof_coupling->empty()))
01832     {
01833       std::vector<unsigned int>
01834         element_dofs,
01835         neighbor_dofs,
01836         dofs_to_add;
01837 
01838       std::vector<const Elem*> active_neighbors;
01839 
01840       for (ConstElemRange::const_iterator elem_it = range.begin() ; elem_it != range.end(); ++elem_it)
01841         {
01842           const Elem* const elem = *elem_it;
01843 
01844           // Get the global indices of the DOFs with support on this element
01845           dof_map.dof_indices (elem, element_dofs);
01846 #if defined(LIBMESH_ENABLE_AMR) || defined(LIBMESH_ENABLE_PERIODIC)
01847           dof_map.find_connected_dofs (element_dofs);
01848 #endif
01849 
01850           // We can be more efficient if we sort the element DOFs
01851           // into increasing order
01852           std::sort(element_dofs.begin(), element_dofs.end());
01853           
01854           const unsigned int n_dofs_on_element = element_dofs.size();
01855             
01856           for (unsigned int i=0; i<n_dofs_on_element; i++)
01857             {
01858               const unsigned int ig = element_dofs[i];
01859               
01860               // Only bother if this matrix row will be stored
01861               // on this processor.
01862               if ((ig >= first_dof_on_proc) &&
01863                   (ig <  end_dof_on_proc))
01864                 {
01865                   // This is what I mean
01866                   // libmesh_assert ((ig - first_dof_on_proc) >= 0);
01867                   // but do the test like this because ig and
01868                   // first_dof_on_proc are unsigned ints
01869                   libmesh_assert (ig >= first_dof_on_proc);
01870                   libmesh_assert ((ig - first_dof_on_proc) < sparsity_pattern.size());
01871                   
01872                   SparsityPattern::Row &row = sparsity_pattern[ig - first_dof_on_proc];
01873 
01874                   // If the row is empty we will add *all* the element DOFs,
01875                   // so just do that.
01876                   if (row.empty())
01877                     {
01878                       row.insert(row.end(),
01879                                  element_dofs.begin(),
01880                                  element_dofs.end());
01881                     }
01882                   else
01883                     {             
01884                       // Build a list of the DOF indices not found in the
01885                       // sparsity pattern
01886                       dofs_to_add.clear();
01887 
01888                       // Cache iterators.  Low will move forward, subsequent
01889                       // searches will be on smaller ranges
01890                       SparsityPattern::Row::iterator
01891                         low  = std::lower_bound (row.begin(), row.end(), element_dofs.front()),
01892                         high = std::upper_bound (low,         row.end(), element_dofs.back());
01893                   
01894                       for (unsigned int j=0; j<n_dofs_on_element; j++)
01895                         {
01896                           const unsigned int jg = element_dofs[j];
01897                           
01898                           // See if jg is in the sorted range
01899                           std::pair<SparsityPattern::Row::iterator,
01900                                     SparsityPattern::Row::iterator>
01901                             pos = std::equal_range (low, high, jg);
01902                         
01903                           // Must add jg if it wasn't found
01904                           if (pos.first == pos.second)
01905                             dofs_to_add.push_back(jg);
01906                         
01907                           // pos.first is now a valid lower bound for any
01908                           // remaining element DOFs. (That's why we sorted them.)
01909                           // Use it for the next search
01910                           low = pos.first;
01911                         }
01912 
01913                       // Add to the sparsity pattern
01914                       if (!dofs_to_add.empty())
01915                         {
01916                           const unsigned int old_size = row.size();
01917                           
01918                           row.insert (row.end(),
01919                                       dofs_to_add.begin(),
01920                                       dofs_to_add.end());
01921                       
01922                           SparsityPattern::sort_row (row.begin(), row.begin()+old_size, row.end());
01923                         }
01924                     }
01925                   
01926                   // Now (possibly) add dofs from neighboring elements
01927                   // TODO:[BSK] optimize this like above!
01928                   if (implicit_neighbor_dofs)
01929                     for (unsigned int s=0; s<elem->n_sides(); s++)
01930                       if (elem->neighbor(s) != NULL)
01931                         {
01932                           const Elem* const neighbor_0 = elem->neighbor(s);
01933 #ifdef LIBMESH_ENABLE_AMR
01934                           neighbor_0->active_family_tree_by_neighbor(active_neighbors,elem);
01935 #else
01936                           active_neighbors.clear();
01937                           active_neighbors.push_back(neighbor_0);
01938 #endif
01939                           
01940                           for (unsigned int a=0; a != active_neighbors.size(); ++a)
01941                             {
01942                               const Elem *neighbor = active_neighbors[a];
01943                               
01944                               dof_map.dof_indices (neighbor, neighbor_dofs);
01945 #if defined(LIBMESH_ENABLE_AMR) || defined(LIBMESH_ENABLE_PERIODIC)
01946                               dof_map.find_connected_dofs (neighbor_dofs);
01947 #endif                        
01948                               const unsigned int n_dofs_on_neighbor = neighbor_dofs.size();
01949                               
01950                               for (unsigned int j=0; j<n_dofs_on_neighbor; j++)
01951                                 {
01952                                   const unsigned int jg = neighbor_dofs[j];
01953                                   
01954                                   // See if jg is in the sorted range
01955                                   std::pair<SparsityPattern::Row::iterator,
01956                                             SparsityPattern::Row::iterator>
01957                                     pos = std::equal_range (row.begin(), row.end(), jg);
01958                                 
01959                                   // Insert jg if it wasn't found
01960                                   if (pos.first == pos.second)
01961                                     row.insert (pos.first, jg);
01962                                 }    
01963                             }
01964                         }
01965                 }
01966             }
01967         }             
01968     } 
01969 
01970 
01971   
01972   // This is what we do in the case that the user has specified
01973   // explicit DOF coupling.
01974   else
01975     {
01976       libmesh_assert (dof_coupling != NULL);
01977       libmesh_assert (dof_coupling->size() ==
01978                       dof_map.n_variables());
01979       
01980       const unsigned int n_var = dof_map.n_variables();
01981       
01982       std::vector<unsigned int>
01983         element_dofs_i,
01984         element_dofs_j,
01985         dofs_to_add;
01986 
01987 
01988       for (ConstElemRange::const_iterator elem_it = range.begin() ; elem_it != range.end(); ++elem_it)
01989         for (unsigned int vi=0; vi<n_var; vi++)
01990           {
01991             const Elem* const elem = *elem_it;
01992             
01993             // Find element dofs for variable vi
01994             dof_map.dof_indices (elem, element_dofs_i, vi);
01995 #if defined(LIBMESH_ENABLE_AMR) || defined(LIBMESH_ENABLE_PERIODIC)
01996             dof_map.find_connected_dofs (element_dofs_i);
01997 #endif
01998 
01999             // We can be more efficient if we sort the element DOFs
02000             // into increasing order
02001             std::sort(element_dofs_i.begin(), element_dofs_i.end());
02002             const unsigned int n_dofs_on_element_i = element_dofs_i.size();
02003             
02004             for (unsigned int vj=0; vj<n_var; vj++)
02005               if ((*dof_coupling)(vi,vj)) // If vi couples to vj
02006                 {
02007                   // Find element dofs for variable vj, note that
02008                   // if vi==vj we already have the dofs.
02009                   if (vi != vj)
02010                     {
02011                       dof_map.dof_indices (elem, element_dofs_j, vj);
02012 #if defined(LIBMESH_ENABLE_AMR) || defined(LIBMESH_ENABLE_PERIODIC)
02013                       dof_map.find_connected_dofs (element_dofs_j);
02014 #endif
02015 
02016                       // We can be more efficient if we sort the element DOFs
02017                       // into increasing order
02018                       std::sort (element_dofs_j.begin(), element_dofs_j.end());
02019                     }
02020                   else
02021                     element_dofs_j = element_dofs_i;
02022                   
02023                   const unsigned int n_dofs_on_element_j =
02024                     element_dofs_j.size();
02025                   
02026                   for (unsigned int i=0; i<n_dofs_on_element_i; i++)
02027                     {
02028                       const unsigned int ig = element_dofs_i[i];
02029                       
02030                       // We are only interested in computing the sparsity pattern
02031                       // for the rows in [range.begin(),range.end()).  So, if this
02032                       // element's DOFs are not in that range just go ahead to the
02033                       // next one.
02034                       //              
02035                       // Also, only bother if this matrix row will be stored
02036                       // on this processor.
02037                       if ((ig >= first_dof_on_proc) &&
02038                           (ig <  end_dof_on_proc))                      
02039                         {
02040                           // This is what I mean
02041                           // libmesh_assert ((ig - first_dof_on_proc) >= 0);
02042                           // but do the test like this because ig and
02043                           // first_dof_on_proc are unsigned ints
02044                           libmesh_assert (ig >= first_dof_on_proc);
02045                           libmesh_assert (ig < (sparsity_pattern.size() +
02046                                         first_dof_on_proc));
02047                           
02048                           SparsityPattern::Row &row = sparsity_pattern[ig - first_dof_on_proc];
02049 
02050                           // If the row is empty we will add *all* the element j DOFs,
02051                           // so just do that.
02052                           if (row.empty())
02053                             {
02054                               row.insert(row.end(),
02055                                          element_dofs_j.begin(),
02056                                          element_dofs_j.end());
02057                             }
02058                           else
02059                             {             
02060                               // Build a list of the DOF indices not found in the
02061                               // sparsity pattern
02062                               dofs_to_add.clear();
02063 
02064                               // Cache iterators.  Low will move forward, subsequent
02065                               // searches will be on smaller ranges
02066                               SparsityPattern::Row::iterator
02067                                 low  = std::lower_bound (row.begin(), row.end(), element_dofs_j.front()),
02068                                 high = std::upper_bound (low,         row.end(), element_dofs_j.back());
02069 
02070                               for (unsigned int j=0; j<n_dofs_on_element_j; j++)
02071                                 {
02072                                   const unsigned int jg = element_dofs_j[j];
02073                                   
02074                                   // See if jg is in the sorted range
02075                                   std::pair<SparsityPattern::Row::iterator,
02076                                             SparsityPattern::Row::iterator>
02077                                     pos = std::equal_range (low, high, jg);
02078                               
02079                                   // Must add jg if it wasn't found
02080                                   if (pos.first == pos.second)
02081                                     dofs_to_add.push_back(jg);
02082                                 
02083                                   // pos.first is now a valid lower bound for any
02084                                   // remaining element j DOFs. (That's why we sorted them.)
02085                                   // Use it for the next search
02086                                   low = pos.first;
02087                                 }
02088                               
02089                               // Add to the sparsity pattern
02090                               if (!dofs_to_add.empty())
02091                                 {
02092                                   const unsigned int old_size = row.size();
02093                                   
02094                                   row.insert (row.end(),
02095                                               dofs_to_add.begin(),
02096                                               dofs_to_add.end());
02097                       
02098                                   SparsityPattern::sort_row (row.begin(), row.begin()+old_size, row.end());
02099                                 }
02100                             }
02101                         }
02102                     }
02103                 }
02104           }
02105     }
02106   
02107   // Now the full sparsity structure is built for all of the
02108   // DOFs connected to our rows of the matrix.
02109   n_nz.resize (n_dofs_on_proc);
02110   n_oz.resize (n_dofs_on_proc);
02111 
02112   // First zero the counters.
02113   std::fill(n_nz.begin(), n_nz.end(), 0);
02114   std::fill(n_oz.begin(), n_oz.end(), 0);
02115   
02116   for (unsigned int i=0; i<n_dofs_on_proc; i++)
02117     {
02118       // Get the row of the sparsity pattern
02119       const SparsityPattern::Row &row = sparsity_pattern[i];
02120 
02121       for (unsigned int j=0; j<row.size(); j++)
02122         if ((row[j] < first_dof_on_proc) || (row[j] >= end_dof_on_proc))
02123           n_oz[i]++;
02124         else
02125           n_nz[i]++;
02126     }
02127 }


Member Data Documentation

Definition at line 105 of file dof_map.h.

Referenced by operator()().

Definition at line 104 of file dof_map.h.

Referenced by join(), and operator()().

Definition at line 106 of file dof_map.h.

Referenced by operator()().

Definition at line 103 of file dof_map.h.

Referenced by join(), and operator()().

std::vector<unsigned int> SparsityPattern::Build::n_nz

Definition at line 112 of file dof_map.h.

Referenced by DofMap::compute_sparsity(), join(), and operator()().

std::vector<unsigned int> SparsityPattern::Build::n_oz

Definition at line 113 of file dof_map.h.

Referenced by DofMap::compute_sparsity(), join(), and operator()().

Definition at line 107 of file dof_map.h.

Referenced by join().


The documentation for this class was generated from the following files:

Site Created By: libMesh Developers
Last modified: November 25 2009 03:45:23.

Hosted By:
SourceForge.net Logo