$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 // C++ includes 00019 #include <algorithm> // for std::fill 00020 #include <cstdlib> // *must* precede <cmath> for proper std:abs() on PGI, Sun Studio CC 00021 #include <cmath> // for sqrt 00022 #include <set> 00023 #include <sstream> // for ostringstream 00024 00025 // Local Includes 00026 #include "libmesh/dof_map.h" 00027 #include "libmesh/elem.h" 00028 #include "libmesh/equation_systems.h" 00029 #include "libmesh/error_vector.h" 00030 #include "libmesh/fe.h" 00031 #include "libmesh/fe_interface.h" 00032 #include "libmesh/libmesh_common.h" 00033 #include "libmesh/libmesh_logging.h" 00034 #include "libmesh/mesh_base.h" 00035 #include "libmesh/mesh_refinement.h" 00036 #include "libmesh/numeric_vector.h" 00037 #include "libmesh/quadrature.h" 00038 #include "libmesh/system.h" 00039 #include "libmesh/implicit_system.h" 00040 #include "libmesh/partitioner.h" 00041 #include "libmesh/adjoint_refinement_estimator.h" 00042 00043 #include LIBMESH_INCLUDE_UNORDERED_MAP 00044 #include LIBMESH_INCLUDE_UNORDERED_SET 00045 00046 #ifdef LIBMESH_ENABLE_AMR 00047 00048 namespace libMesh 00049 { 00050 00051 //----------------------------------------------------------------- 00052 // AdjointRefinementErrorEstimator 00053 00054 // As of 10/2/2012, this function implements a 'brute-force' adjoint based QoI 00055 // error estimator, using the following relationship: 00056 // Q(u) - Q(u_h) \aprrox - R( (u_h)_(h/2), z_(h/2) ) . 00057 // where: Q(u) is the true QoI 00058 // u_h is the approximate primal solution on the current FE space 00059 // Q(u_h) is the approximate QoI 00060 // z_(h/2) is the adjoint corresponding to Q, on a richer FE space 00061 // (u_h)_(h/2) is a projection of the primal solution on the richer FE space 00062 // By richer FE space, we mean a grid that has been refined once and a polynomial order 00063 // that has been increased once, i.e. one h and one p refinement 00064 00065 // Both a global QoI error estimate and element wise error indicators are included 00066 // Note that the element wise error indicators slightly over estimate the error in 00067 // each element 00068 00069 void AdjointRefinementEstimator::estimate_error (const System& _system, 00070 ErrorVector& error_per_cell, 00071 const NumericVector<Number>* solution_vector, 00072 bool /*estimate_parent_error*/) 00073 { 00074 // We have to break the rules here, because we can't refine a const System 00075 System& system = const_cast<System&>(_system); 00076 00077 // An EquationSystems reference will be convenient. 00078 EquationSystems& es = system.get_equation_systems(); 00079 00080 // The current mesh 00081 MeshBase& mesh = es.get_mesh(); 00082 00083 // Resize the error_per_cell vector to be 00084 // the number of elements, initialized to 0. 00085 error_per_cell.clear(); 00086 error_per_cell.resize (mesh.max_elem_id(), 0.); 00087 00088 // We'll want to back up all coarse grid vectors 00089 std::map<std::string, NumericVector<Number> *> coarse_vectors; 00090 for (System::vectors_iterator vec = system.vectors_begin(); vec != 00091 system.vectors_end(); ++vec) 00092 { 00093 // The (string) name of this vector 00094 const std::string& var_name = vec->first; 00095 00096 coarse_vectors[var_name] = vec->second->clone().release(); 00097 } 00098 // Back up the coarse solution and coarse local solution 00099 NumericVector<Number> * coarse_solution = 00100 system.solution->clone().release(); 00101 NumericVector<Number> * coarse_local_solution = 00102 system.current_local_solution->clone().release(); 00103 00104 // And we'll need to temporarily change solution projection settings 00105 bool old_projection_setting; 00106 old_projection_setting = system.project_solution_on_reinit(); 00107 00108 // Make sure the solution is projected when we refine the mesh 00109 system.project_solution_on_reinit() = true; 00110 00111 // And it'll be best to avoid any repartitioning 00112 UniquePtr<Partitioner> old_partitioner(mesh.partitioner().release()); 00113 00114 // And we can't allow any renumbering 00115 const bool old_renumbering_setting = mesh.allow_renumbering(); 00116 mesh.allow_renumbering(false); 00117 00118 // Use a non-standard solution vector if necessary 00119 if (solution_vector && solution_vector != system.solution.get()) 00120 { 00121 NumericVector<Number> *newsol = 00122 const_cast<NumericVector<Number>*> (solution_vector); 00123 newsol->swap(*system.solution); 00124 system.update(); 00125 } 00126 00127 // Get coarse grid adjoint solutions. This should be a relatively 00128 // quick (especially with preconditioner reuse) way to get a good 00129 // initial guess for the fine grid adjoint solutions. More 00130 // importantly, subtracting off a coarse adjoint approximation gives 00131 // us better local error indication, and subtracting off *some* lift 00132 // function is necessary for correctness if we have heterogeneous 00133 // adjoint Dirichlet conditions. 00134 00135 // Solve the adjoint problem(s) on the coarse FE space 00136 system.adjoint_solve(_qoi_set); 00137 00138 00139 // Loop over all the adjoint problems and, if any have heterogenous 00140 // Dirichlet conditions, get the corresponding coarse lift 00141 // function(s) 00142 for (unsigned int j=0; j != system.qoi.size(); j++) 00143 { 00144 // Skip this QoI if it is not in the QoI Set or if there are no 00145 // heterogeneous Dirichlet boundaries for it 00146 if (_qoi_set.has_index(j) && 00147 system.get_dof_map().has_adjoint_dirichlet_boundaries(j)) 00148 { 00149 std::ostringstream liftfunc_name; 00150 liftfunc_name << "adjoint_lift_function" << j; 00151 NumericVector<Number> &liftvec = 00152 system.add_vector(liftfunc_name.str()); 00153 00154 system.get_dof_map().enforce_constraints_exactly 00155 (system, &liftvec, true); 00156 } 00157 } 00158 00159 00160 00161 00162 #ifndef NDEBUG 00163 // n_coarse_elem is only used in an assertion later so 00164 // avoid declaring it unless asserts are active. 00165 const dof_id_type n_coarse_elem = mesh.n_elem(); 00166 #endif 00167 00168 // Uniformly refine the mesh 00169 MeshRefinement mesh_refinement(mesh); 00170 00171 libmesh_assert (number_h_refinements > 0 || number_p_refinements > 0); 00172 00173 // FIXME: this may break if there is more than one System 00174 // on this mesh but estimate_error was still called instead of 00175 // estimate_errors 00176 for (unsigned int i = 0; i != number_h_refinements; ++i) 00177 { 00178 mesh_refinement.uniformly_refine(1); 00179 es.reinit(); 00180 } 00181 00182 for (unsigned int i = 0; i != number_p_refinements; ++i) 00183 { 00184 mesh_refinement.uniformly_p_refine(1); 00185 es.reinit(); 00186 } 00187 00188 // Copy the projected coarse grid solutions, which will be 00189 // overwritten by solve() 00190 std::vector<NumericVector<Number> *> coarse_adjoints; 00191 for (unsigned int j=0; j != system.qoi.size(); j++) 00192 { 00193 if (_qoi_set.has_index(j)) 00194 { 00195 NumericVector<Number> *coarse_adjoint = 00196 NumericVector<Number>::build(mesh.comm()).release(); 00197 00198 // Can do "fast" init since we're overwriting this in a sec 00199 coarse_adjoint->init(system.solution->size(), 00200 system.solution->local_size(), 00201 true, 00202 system.get_adjoint_solution(j).type()); 00203 00204 *coarse_adjoint = system.get_adjoint_solution(j); 00205 00206 coarse_adjoints.push_back(coarse_adjoint); 00207 } 00208 else 00209 coarse_adjoints.push_back(NULL); 00210 } 00211 00212 // Rebuild the rhs with the projected primal solution 00213 (dynamic_cast<ImplicitSystem&>(system)).assembly(true, false); 00214 NumericVector<Number> & projected_residual = (dynamic_cast<ExplicitSystem&>(system)).get_vector("RHS Vector"); 00215 projected_residual.close(); 00216 00217 // Solve the adjoint problem(s) on the refined FE space 00218 system.adjoint_solve(_qoi_set); 00219 00220 // Now that we have the refined adjoint solution and the projected primal solution, 00221 // we first compute the global QoI error estimate 00222 00223 // Resize the computed_global_QoI_errors vector to hold the error estimates for each QoI 00224 computed_global_QoI_errors.resize(system.qoi.size()); 00225 00226 // Loop over all the adjoint solutions and get the QoI error 00227 // contributions from all of them. While we're looping anyway we'll 00228 // pull off the coarse adjoints 00229 for (unsigned int j=0; j != system.qoi.size(); j++) 00230 { 00231 // Skip this QoI if not in the QoI Set 00232 if (_qoi_set.has_index(j)) 00233 { 00234 // If the adjoint solution has heterogeneous dirichlet 00235 // values, then to get a proper error estimate here we need 00236 // to subtract off a coarse grid lift function. In any case 00237 // we can get a better error estimate by separating off a 00238 // coarse representation of the adjoint solution, so we'll 00239 // use that for our lift function. 00240 system.get_adjoint_solution(j) -= *coarse_adjoints[j]; 00241 00242 computed_global_QoI_errors[j] = projected_residual.dot(system.get_adjoint_solution(j)); 00243 } 00244 } 00245 00246 // Done with the global error estimates, now construct the element wise error indicators 00247 00248 // We ought to account for 'spill-over' effects while computing the 00249 // element error indicators This happens because the same dof is 00250 // shared by multiple elements, one way of mitigating this is to 00251 // scale the contribution from each dof by the number of elements it 00252 // belongs to We first obtain the number of elements each node 00253 // belongs to 00254 00255 // A map that relates a node id to an int that will tell us how many elements it is a node of 00256 LIBMESH_BEST_UNORDERED_MAP<dof_id_type, unsigned int>shared_element_count; 00257 00258 // To fill this map, we will loop over elements, and then in each element, we will loop 00259 // over the nodes each element contains, and then query it for the number of coarse 00260 // grid elements it was a node of 00261 00262 // Keep track of which nodes we have already dealt with 00263 LIBMESH_BEST_UNORDERED_SET<dof_id_type> processed_node_ids; 00264 00265 // We will be iterating over all the active elements in the fine mesh that live on 00266 // this processor 00267 { 00268 MeshBase::const_element_iterator elem_it = mesh.active_local_elements_begin(); 00269 const MeshBase::const_element_iterator elem_end = mesh.active_local_elements_end(); 00270 00271 // Start loop over elems 00272 for(; elem_it != elem_end; ++elem_it) 00273 { 00274 // Pointer to this element 00275 const Elem* elem = *elem_it; 00276 00277 // Loop over the nodes in the element 00278 for(unsigned int n=0; n != elem->n_nodes(); ++n) 00279 { 00280 // Get a pointer to the current node 00281 Node* node = elem->get_node(n); 00282 00283 // Get the id of this node 00284 dof_id_type node_id = node->id(); 00285 00286 // If we havent already processed this node, do so now 00287 if(processed_node_ids.find(node_id) == processed_node_ids.end()) 00288 { 00289 // Declare a neighbor_set to be filled by the find_point_neighbors 00290 std::set<const Elem *> fine_grid_neighbor_set; 00291 00292 // Call find_point_neighbors to fill the neighbor_set 00293 elem->find_point_neighbors(*node, fine_grid_neighbor_set); 00294 00295 // A vector to hold the coarse grid parents neighbors 00296 std::vector<dof_id_type> coarse_grid_neighbors; 00297 00298 // Iterators over the fine grid neighbors set 00299 std::set<const Elem*>::iterator fine_neighbor_it = fine_grid_neighbor_set.begin(); 00300 const std::set<const Elem*>::iterator fine_neighbor_end = fine_grid_neighbor_set.end(); 00301 00302 // Loop over all the fine neighbors of this node 00303 for(; fine_neighbor_it != fine_neighbor_end ; ++fine_neighbor_it) 00304 { 00305 // Pointer to the current fine neighbor element 00306 const Elem* fine_elem = *fine_neighbor_it; 00307 00308 // Find the element id for the corresponding coarse grid element 00309 const Elem* coarse_elem = fine_elem; 00310 for (unsigned int j = 0; j != number_h_refinements; ++j) 00311 { 00312 libmesh_assert (coarse_elem->parent()); 00313 00314 coarse_elem = coarse_elem->parent(); 00315 } 00316 00317 // Loop over the existing coarse neighbors and check if this one is 00318 // already in there 00319 const dof_id_type coarse_id = coarse_elem->id(); 00320 std::size_t j = 0; 00321 for (; j != coarse_grid_neighbors.size(); j++) 00322 { 00323 // If the set already contains this element break out of the loop 00324 if(coarse_grid_neighbors[j] == coarse_id) 00325 { 00326 break; 00327 } 00328 } 00329 00330 // If we didn't leave the loop even at the last element, 00331 // this is a new neighbour, put in the coarse_grid_neighbor_set 00332 if(j == coarse_grid_neighbors.size()) 00333 { 00334 coarse_grid_neighbors.push_back(coarse_id); 00335 } 00336 00337 } // End loop over fine neighbors 00338 00339 // Set the shared_neighbour index for this node to the 00340 // size of the coarse grid neighbor set 00341 shared_element_count[node_id] = 00342 cast_int<unsigned int>(coarse_grid_neighbors.size()); 00343 00344 // Add this node to processed_node_ids vector 00345 processed_node_ids.insert(node_id); 00346 00347 } // End if not processed node 00348 00349 } // End loop over nodes 00350 00351 } // End loop over elems 00352 } 00353 00354 // Get a DoF map, will be used to get the nodal dof_indices for each element 00355 DofMap &dof_map = system.get_dof_map(); 00356 00357 // The global DOF indices, we will use these later on when we compute the element wise indicators 00358 std::vector<dof_id_type> dof_indices; 00359 00360 // Localize the global rhs and adjoint solution vectors (which might be shared on multiple processsors) onto a 00361 // local ghosted vector, this ensures each processor has all the dof_indices to compute an error indicator for 00362 // an element it owns 00363 UniquePtr<NumericVector<Number> > localized_projected_residual = NumericVector<Number>::build(system.comm()); 00364 localized_projected_residual->init(system.n_dofs(), system.n_local_dofs(), system.get_dof_map().get_send_list(), false, GHOSTED); 00365 projected_residual.localize(*localized_projected_residual, system.get_dof_map().get_send_list()); 00366 00367 // Each adjoint solution will also require ghosting; for efficiency we'll reuse the same memory 00368 UniquePtr<NumericVector<Number> > localized_adjoint_solution = NumericVector<Number>::build(system.comm()); 00369 localized_adjoint_solution->init(system.n_dofs(), system.n_local_dofs(), system.get_dof_map().get_send_list(), false, GHOSTED); 00370 00371 // We will loop over each adjoint solution, localize that adjoint 00372 // solution and then loop over local elements 00373 for (unsigned int i=0; i != system.qoi.size(); i++) 00374 { 00375 // Skip this QoI if not in the QoI Set 00376 if (_qoi_set.has_index(i)) 00377 { 00378 // Get the weight for the current QoI 00379 Real error_weight = _qoi_set.weight(i); 00380 00381 (system.get_adjoint_solution(i)).localize(*localized_adjoint_solution, system.get_dof_map().get_send_list()); 00382 00383 // Loop over elements 00384 MeshBase::const_element_iterator elem_it = mesh.active_local_elements_begin(); 00385 const MeshBase::const_element_iterator elem_end = mesh.active_local_elements_end(); 00386 00387 for(; elem_it != elem_end; ++elem_it) 00388 { 00389 // Pointer to the element 00390 const Elem* elem = *elem_it; 00391 00392 // Go up number_h_refinements levels up to find the coarse parent 00393 const Elem* coarse = elem; 00394 00395 for (unsigned int j = 0; j != number_h_refinements; ++j) 00396 { 00397 libmesh_assert (coarse->parent()); 00398 00399 coarse = coarse->parent(); 00400 } 00401 00402 const dof_id_type e_id = coarse->id(); 00403 00404 // Get the local to global degree of freedom maps for this element 00405 dof_map.dof_indices (elem, dof_indices); 00406 00407 // We will have to manually do the dot products. 00408 Number local_contribution = 0.; 00409 00410 for (unsigned int j=0; j != dof_indices.size(); j++) 00411 { 00412 // The contribution to the error indicator for this element from the current QoI 00413 local_contribution += (*localized_projected_residual)(dof_indices[j]) * (*localized_adjoint_solution)(dof_indices[j]); 00414 } 00415 00416 // Multiply by the error weight for this QoI 00417 local_contribution *= error_weight; 00418 00419 // FIXME: we're throwing away information in the 00420 // --enable-complex case 00421 error_per_cell[e_id] += static_cast<ErrorVectorReal> 00422 (libmesh_real(local_contribution)); 00423 00424 } // End loop over elements 00425 00426 } // End if belong to QoI set 00427 00428 } // End loop over QoIs 00429 00430 for (unsigned int j=0; j != system.qoi.size(); j++) 00431 { 00432 if (_qoi_set.has_index(j)) 00433 { 00434 delete coarse_adjoints[j]; 00435 } 00436 } 00437 00438 // Don't bother projecting the solution; we'll restore from backup 00439 // after coarsening 00440 system.project_solution_on_reinit() = false; 00441 00442 // Uniformly coarsen the mesh, without projecting the solution 00443 libmesh_assert (number_h_refinements > 0 || number_p_refinements > 0); 00444 00445 for (unsigned int i = 0; i != number_h_refinements; ++i) 00446 { 00447 mesh_refinement.uniformly_coarsen(1); 00448 // FIXME - should the reinits here be necessary? - RHS 00449 es.reinit(); 00450 } 00451 00452 for (unsigned int i = 0; i != number_p_refinements; ++i) 00453 { 00454 mesh_refinement.uniformly_p_coarsen(1); 00455 es.reinit(); 00456 } 00457 00458 // We should be back where we started 00459 libmesh_assert_equal_to (n_coarse_elem, mesh.n_elem()); 00460 00461 // Restore old solutions and clean up the heap 00462 system.project_solution_on_reinit() = old_projection_setting; 00463 00464 // Restore the coarse solution vectors and delete their copies 00465 *system.solution = *coarse_solution; 00466 delete coarse_solution; 00467 *system.current_local_solution = *coarse_local_solution; 00468 delete coarse_local_solution; 00469 00470 for (System::vectors_iterator vec = system.vectors_begin(); vec != 00471 system.vectors_end(); ++vec) 00472 { 00473 // The (string) name of this vector 00474 const std::string& var_name = vec->first; 00475 00476 // If it's a vector we already had (and not a newly created 00477 // vector like an adjoint rhs), we need to restore it. 00478 std::map<std::string, NumericVector<Number> *>::iterator it = 00479 coarse_vectors.find(var_name); 00480 if (it != coarse_vectors.end()) 00481 { 00482 NumericVector<Number> *coarsevec = it->second; 00483 system.get_vector(var_name) = *coarsevec; 00484 00485 coarsevec->clear(); 00486 delete coarsevec; 00487 } 00488 } 00489 00490 // Restore old partitioner and renumbering settings 00491 mesh.partitioner().reset(old_partitioner.release()); 00492 mesh.allow_renumbering(old_renumbering_setting); 00493 00494 // Fiinally sum the vector of estimated error values. 00495 this->reduce_error(error_per_cell, system.comm()); 00496 00497 // We don't take a square root here; this is a goal-oriented 00498 // estimate not a Hilbert norm estimate. 00499 } // end estimate_error function 00500 00501 }// namespace libMesh 00502 00503 #endif // #ifdef LIBMESH_ENABLE_AMR