SparsityPattern::Build Class Reference
#include <dof_map.h>
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 MeshBase & | mesh |
| const DofMap & | dof_map |
| const CouplingMatrix * | dof_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
const CouplingMatrix* SparsityPattern::Build::dof_coupling [private] |
const DofMap& SparsityPattern::Build::dof_map [private] |
const bool SparsityPattern::Build::implicit_neighbor_dofs [private] |
const MeshBase& SparsityPattern::Build::mesh [private] |
| 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()().
const bool SparsityPattern::Build::need_full_sparsity_pattern [private] |
Definition at line 111 of file dof_map.h.
Referenced by DofMap::compute_sparsity(), join(), and operator()().
The documentation for this class was generated from the following files: