$extrastylesheet
00001 // The libMesh Finite Element Library. 00002 // Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner 00003 00004 // This library is free software; you can redistribute it and/or 00005 // modify it under the terms of the GNU Lesser General Public 00006 // License as published by the Free Software Foundation; either 00007 // version 2.1 of the License, or (at your option) any later version. 00008 00009 // This library is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 // Lesser General Public License for more details. 00013 00014 // You should have received a copy of the GNU Lesser General Public 00015 // License along with this library; if not, write to the Free Software 00016 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00017 00018 00019 // C++ includes 00020 #include <algorithm> // for std::fill 00021 #include <cstdlib> // *must* precede <cmath> for proper std:abs() on PGI, Sun Studio CC 00022 #include <cmath> // for sqrt 00023 00024 00025 // Local Includes 00026 #include "libmesh/libmesh_common.h" 00027 #include "libmesh/jump_error_estimator.h" 00028 #include "libmesh/dof_map.h" 00029 #include "libmesh/error_vector.h" 00030 #include "libmesh/fe_base.h" 00031 #include "libmesh/fe_interface.h" 00032 #include "libmesh/quadrature_gauss.h" 00033 #include "libmesh/libmesh_logging.h" 00034 #include "libmesh/elem.h" 00035 #include "libmesh/mesh_base.h" 00036 #include "libmesh/system.h" 00037 00038 #include "libmesh/dense_vector.h" 00039 #include "libmesh/numeric_vector.h" 00040 00041 namespace libMesh 00042 { 00043 00044 //----------------------------------------------------------------- 00045 // JumpErrorEstimator implementations 00046 void JumpErrorEstimator::initialize (const System&, 00047 ErrorVector&, 00048 bool) 00049 { 00050 } 00051 00052 00053 00054 void JumpErrorEstimator::estimate_error (const System& system, 00055 ErrorVector& error_per_cell, 00056 const NumericVector<Number>* solution_vector, 00057 bool estimate_parent_error) 00058 { 00059 START_LOG("estimate_error()", "JumpErrorEstimator"); 00060 /* 00061 00062 Conventions for assigning the direction of the normal: 00063 00064 - e & f are global element ids 00065 00066 Case (1.) Elements are at the same level, e<f 00067 Compute the flux jump on the face and 00068 add it as a contribution to error_per_cell[e] 00069 and error_per_cell[f] 00070 00071 ---------------------- 00072 | | | 00073 | | f | 00074 | | | 00075 | e |---> n | 00076 | | | 00077 | | | 00078 ---------------------- 00079 00080 00081 Case (2.) The neighbor is at a higher level. 00082 Compute the flux jump on e's face and 00083 add it as a contribution to error_per_cell[e] 00084 and error_per_cell[f] 00085 00086 ---------------------- 00087 | | | | 00088 | | e |---> n | 00089 | | | | 00090 |-----------| f | 00091 | | | | 00092 | | | | 00093 | | | | 00094 ---------------------- 00095 */ 00096 00097 // The current mesh 00098 const MeshBase& mesh = system.get_mesh(); 00099 00100 // The dimensionality of the mesh 00101 const unsigned int dim = mesh.mesh_dimension(); 00102 00103 // The number of variables in the system 00104 const unsigned int n_vars = system.n_vars(); 00105 00106 // The DofMap for this system 00107 const DofMap& dof_map = system.get_dof_map(); 00108 00109 // Resize the error_per_cell vector to be 00110 // the number of elements, initialize it to 0. 00111 error_per_cell.resize (mesh.max_elem_id()); 00112 std::fill (error_per_cell.begin(), error_per_cell.end(), 0.); 00113 00114 // Declare a vector of floats which is as long as 00115 // error_per_cell above, and fill with zeros. This vector will be 00116 // used to keep track of the number of edges (faces) on each active 00117 // element which are either: 00118 // 1) an internal edge 00119 // 2) an edge on a Neumann boundary for which a boundary condition 00120 // function has been specified. 00121 // The error estimator can be scaled by the number of flux edges (faces) 00122 // which the element actually has to obtain a more uniform measure 00123 // of the error. Use floats instead of ints since in case 2 (above) 00124 // f gets 1/2 of a flux face contribution from each of his 00125 // neighbors 00126 std::vector<float> n_flux_faces (error_per_cell.size()); 00127 00128 // Prepare current_local_solution to localize a non-standard 00129 // solution vector if necessary 00130 if (solution_vector && solution_vector != system.solution.get()) 00131 { 00132 NumericVector<Number>* newsol = 00133 const_cast<NumericVector<Number>*>(solution_vector); 00134 System &sys = const_cast<System&>(system); 00135 newsol->swap(*sys.solution); 00136 sys.update(); 00137 } 00138 00139 // Loop over all the variables in the system 00140 for (var=0; var<n_vars; var++) 00141 { 00142 // Possibly skip this variable 00143 if (error_norm.weight(var) == 0.0) continue; 00144 00145 // The type of finite element to use for this variable 00146 const FEType& fe_type = dof_map.variable_type (var); 00147 00148 // Finite element objects for the same face from 00149 // different sides 00150 fe_fine = FEBase::build (dim, fe_type); 00151 fe_coarse = FEBase::build (dim, fe_type); 00152 00153 // Build an appropriate Gaussian quadrature rule 00154 QGauss qrule (dim-1, fe_type.default_quadrature_order()); 00155 00156 // Tell the finite element for the fine element about the quadrature 00157 // rule. The finite element for the coarse element need not know about it 00158 fe_fine->attach_quadrature_rule (&qrule); 00159 00160 // By convention we will always do the integration 00161 // on the face of element e. We'll need its Jacobian values and 00162 // physical point locations, at least 00163 fe_fine->get_JxW(); 00164 fe_fine->get_xyz(); 00165 00166 // Our derived classes may want to do some initialization here 00167 this->initialize(system, error_per_cell, estimate_parent_error); 00168 00169 // The global DOF indices for elements e & f 00170 std::vector<dof_id_type> dof_indices_fine; 00171 std::vector<dof_id_type> dof_indices_coarse; 00172 00173 00174 00175 // Iterate over all the active elements in the mesh 00176 // that live on this processor. 00177 MeshBase::const_element_iterator elem_it = mesh.active_local_elements_begin(); 00178 const MeshBase::const_element_iterator elem_end = mesh.active_local_elements_end(); 00179 00180 for (; elem_it != elem_end; ++elem_it) 00181 { 00182 // e is necessarily an active element on the local processor 00183 const Elem* e = *elem_it; 00184 const dof_id_type e_id = e->id(); 00185 00186 #ifdef LIBMESH_ENABLE_AMR 00187 // See if the parent of element e has been examined yet; 00188 // if not, we may want to compute the estimator on it 00189 const Elem* parent = e->parent(); 00190 00191 // We only can compute and only need to compute on 00192 // parents with all active children 00193 bool compute_on_parent = true; 00194 if (!parent || !estimate_parent_error) 00195 compute_on_parent = false; 00196 else 00197 for (unsigned int c=0; c != parent->n_children(); ++c) 00198 if (!parent->child(c)->active()) 00199 compute_on_parent = false; 00200 00201 if (compute_on_parent && 00202 !error_per_cell[parent->id()]) 00203 { 00204 // Compute a projection onto the parent 00205 DenseVector<Number> Uparent; 00206 FEBase::coarsened_dof_values(*(system.solution), 00207 dof_map, parent, Uparent, 00208 var, false); 00209 00210 // Loop over the neighbors of the parent 00211 for (unsigned int n_p=0; n_p<parent->n_neighbors(); n_p++) 00212 { 00213 if (parent->neighbor(n_p) != NULL) // parent has a neighbor here 00214 { 00215 // Find the active neighbors in this direction 00216 std::vector<const Elem*> active_neighbors; 00217 parent->neighbor(n_p)-> 00218 active_family_tree_by_neighbor(active_neighbors, 00219 parent); 00220 // Compute the flux to each active neighbor 00221 for (unsigned int a=0; 00222 a != active_neighbors.size(); ++a) 00223 { 00224 const Elem *f = active_neighbors[a]; 00225 // FIXME - what about when f->level < 00226 // parent->level()?? 00227 if (f->level() >= parent->level()) 00228 { 00229 fine_elem = f; 00230 coarse_elem = parent; 00231 Ucoarse = Uparent; 00232 00233 dof_map.dof_indices (fine_elem, dof_indices_fine, var); 00234 const unsigned int n_dofs_fine = 00235 cast_int<unsigned int>(dof_indices_fine.size()); 00236 Ufine.resize(n_dofs_fine); 00237 00238 for (unsigned int i=0; i<n_dofs_fine; i++) 00239 Ufine(i) = system.current_solution(dof_indices_fine[i]); 00240 this->reinit_sides(); 00241 this->internal_side_integration(); 00242 00243 error_per_cell[fine_elem->id()] += 00244 static_cast<ErrorVectorReal>(fine_error); 00245 error_per_cell[coarse_elem->id()] += 00246 static_cast<ErrorVectorReal>(coarse_error); 00247 00248 // Keep track of the number of internal flux 00249 // sides found on each element 00250 n_flux_faces[fine_elem->id()]++; 00251 n_flux_faces[coarse_elem->id()] += this->coarse_n_flux_faces_increment(); 00252 } 00253 } 00254 } 00255 else if (integrate_boundary_sides) 00256 { 00257 fine_elem = parent; 00258 Ufine = Uparent; 00259 00260 // Reinitialize shape functions on the fine element side 00261 fe_fine->reinit (fine_elem, fine_side); 00262 00263 if (this->boundary_side_integration()) 00264 { 00265 error_per_cell[fine_elem->id()] += 00266 static_cast<ErrorVectorReal>(fine_error); 00267 n_flux_faces[fine_elem->id()]++; 00268 } 00269 } 00270 } 00271 } 00272 #endif // #ifdef LIBMESH_ENABLE_AMR 00273 00274 // If we do any more flux integration, e will be the fine element 00275 fine_elem = e; 00276 00277 // Loop over the neighbors of element e 00278 for (unsigned int n_e=0; n_e<e->n_neighbors(); n_e++) 00279 { 00280 fine_side = n_e; 00281 00282 if (e->neighbor(n_e) != NULL) // e is not on the boundary 00283 { 00284 const Elem* f = e->neighbor(n_e); 00285 const dof_id_type f_id = f->id(); 00286 00287 // Compute flux jumps if we are in case 1 or case 2. 00288 if ((f->active() && (f->level() == e->level()) && (e_id < f_id)) 00289 || (f->level() < e->level())) 00290 { 00291 // f is now the coarse element 00292 coarse_elem = f; 00293 00294 // Get the DOF indices for the two elements 00295 dof_map.dof_indices (fine_elem, dof_indices_fine, var); 00296 dof_map.dof_indices (coarse_elem, dof_indices_coarse, var); 00297 00298 // The number of DOFS on each element 00299 const unsigned int n_dofs_fine = 00300 cast_int<unsigned int>(dof_indices_fine.size()); 00301 const unsigned int n_dofs_coarse = 00302 cast_int<unsigned int>(dof_indices_coarse.size()); 00303 Ufine.resize(n_dofs_fine); 00304 Ucoarse.resize(n_dofs_coarse); 00305 00306 // The local solutions on each element 00307 for (unsigned int i=0; i<n_dofs_fine; i++) 00308 Ufine(i) = system.current_solution(dof_indices_fine[i]); 00309 for (unsigned int i=0; i<n_dofs_coarse; i++) 00310 Ucoarse(i) = system.current_solution(dof_indices_coarse[i]); 00311 00312 this->reinit_sides(); 00313 this->internal_side_integration(); 00314 00315 error_per_cell[fine_elem->id()] += 00316 static_cast<ErrorVectorReal>(fine_error); 00317 error_per_cell[coarse_elem->id()] += 00318 static_cast<ErrorVectorReal>(coarse_error); 00319 00320 // Keep track of the number of internal flux 00321 // sides found on each element 00322 n_flux_faces[fine_elem->id()]++; 00323 n_flux_faces[coarse_elem->id()] += this->coarse_n_flux_faces_increment(); 00324 } // end if (case1 || case2) 00325 } // if (e->neigbor(n_e) != NULL) 00326 00327 // Otherwise, e is on the boundary. If it happens to 00328 // be on a Dirichlet boundary, we need not do anything. 00329 // On the other hand, if e is on a Neumann (flux) boundary 00330 // with grad(u).n = g, we need to compute the additional residual 00331 // (h * \int |g - grad(u_h).n|^2 dS)^(1/2). 00332 // We can only do this with some knowledge of the boundary 00333 // conditions, i.e. the user must have attached an appropriate 00334 // BC function. 00335 else 00336 { 00337 if (integrate_boundary_sides) 00338 { 00339 // Reinitialize shape functions on the fine element side 00340 fe_fine->reinit (fine_elem, fine_side); 00341 00342 // Get the DOF indices 00343 dof_map.dof_indices (fine_elem, dof_indices_fine, var); 00344 00345 // The number of DOFS on each element 00346 const unsigned int n_dofs_fine = 00347 cast_int<unsigned int>(dof_indices_fine.size()); 00348 Ufine.resize(n_dofs_fine); 00349 00350 for (unsigned int i=0; i<n_dofs_fine; i++) 00351 Ufine(i) = system.current_solution(dof_indices_fine[i]); 00352 00353 if (this->boundary_side_integration()) 00354 { 00355 error_per_cell[fine_elem->id()] += 00356 static_cast<ErrorVectorReal>(fine_error); 00357 n_flux_faces[fine_elem->id()]++; 00358 } 00359 } // end if _bc_function != NULL 00360 } // end if (e->neighbor(n_e) == NULL) 00361 } // end loop over neighbors 00362 } // End loop over active local elements 00363 } // End loop over variables 00364 00365 00366 00367 // Each processor has now computed the error contribuions 00368 // for its local elements. We need to sum the vector 00369 // and then take the square-root of each component. Note 00370 // that we only need to sum if we are running on multiple 00371 // processors, and we only need to take the square-root 00372 // if the value is nonzero. There will in general be many 00373 // zeros for the inactive elements. 00374 00375 // First sum the vector of estimated error values 00376 this->reduce_error(error_per_cell, system.comm()); 00377 00378 // Compute the square-root of each component. 00379 for (std::size_t i=0; i<error_per_cell.size(); i++) 00380 if (error_per_cell[i] != 0.) 00381 error_per_cell[i] = std::sqrt(error_per_cell[i]); 00382 00383 00384 if (this->scale_by_n_flux_faces) 00385 { 00386 // Sum the vector of flux face counts 00387 this->reduce_error(n_flux_faces, system.comm()); 00388 00389 // Sanity check: Make sure the number of flux faces is 00390 // always an integer value 00391 #ifdef DEBUG 00392 for (unsigned int i=0; i<n_flux_faces.size(); ++i) 00393 libmesh_assert_equal_to (n_flux_faces[i], static_cast<float>(static_cast<unsigned int>(n_flux_faces[i])) ); 00394 #endif 00395 00396 // Scale the error by the number of flux faces for each element 00397 for (unsigned int i=0; i<n_flux_faces.size(); ++i) 00398 { 00399 if (n_flux_faces[i] == 0.0) // inactive or non-local element 00400 continue; 00401 00402 //libMesh::out << "Element " << i << " has " << n_flux_faces[i] << " flux faces." << std::endl; 00403 error_per_cell[i] /= static_cast<ErrorVectorReal>(n_flux_faces[i]); 00404 } 00405 } 00406 00407 // If we used a non-standard solution before, now is the time to fix 00408 // the current_local_solution 00409 if (solution_vector && solution_vector != system.solution.get()) 00410 { 00411 NumericVector<Number>* newsol = 00412 const_cast<NumericVector<Number>*>(solution_vector); 00413 System &sys = const_cast<System&>(system); 00414 newsol->swap(*sys.solution); 00415 sys.update(); 00416 } 00417 00418 STOP_LOG("estimate_error()", "JumpErrorEstimator"); 00419 } 00420 00421 00422 00423 void 00424 JumpErrorEstimator::reinit_sides () 00425 { 00426 // The master quadrature point locations on the coarse element 00427 std::vector<Point> qp_coarse; 00428 00429 // Reinitialize shape functions on the fine element side 00430 fe_fine->reinit (fine_elem, fine_side); 00431 00432 // Get the physical locations of the fine element quadrature points 00433 std::vector<Point> qface_point = fe_fine->get_xyz(); 00434 00435 // Find their locations on the coarse element 00436 FEInterface::inverse_map (coarse_elem->dim(), fe_coarse->get_fe_type(), 00437 coarse_elem, qface_point, qp_coarse); 00438 00439 // Calculate the coarse element shape functions at those locations 00440 fe_coarse->reinit (coarse_elem, &qp_coarse); 00441 } 00442 00443 00444 00445 float JumpErrorEstimator::coarse_n_flux_faces_increment () 00446 { 00447 // Keep track of the number of internal flux sides found on each 00448 // element 00449 unsigned int dim = coarse_elem->dim(); 00450 00451 const unsigned int divisor = 00452 1 << (dim-1)*(fine_elem->level() - coarse_elem->level()); 00453 00454 // With a difference of n levels between fine and coarse elements, 00455 // we compute a fractional flux face for the coarse element by adding: 00456 // 1/2^n in 2D 00457 // 1/4^n in 3D 00458 // each time. This code will get hit 2^n times in 2D and 4^n 00459 // times in 3D so that the final flux face count for the coarse 00460 // element will be an integer value. 00461 00462 return 1.0f / static_cast<float>(divisor); 00463 } 00464 00465 } // namespace libMesh