$extrastylesheet
adjoint_refinement_estimator.C
Go to the documentation of this file.
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