$extrastylesheet
system_projection.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 
00019 
00020 // C++ includes
00021 #include <vector>
00022 
00023 // Local includes
00024 #include "libmesh/boundary_info.h"
00025 #include "libmesh/dense_matrix.h"
00026 #include "libmesh/dense_vector.h"
00027 #include "libmesh/dirichlet_boundaries.h"
00028 #include "libmesh/dof_map.h"
00029 #include "libmesh/elem.h"
00030 #include "libmesh/equation_systems.h"
00031 #include "libmesh/fe_base.h"
00032 #include "libmesh/fe_interface.h"
00033 #include "libmesh/libmesh_logging.h"
00034 #include "libmesh/mesh_base.h"
00035 #include "libmesh/numeric_vector.h"
00036 #include "libmesh/quadrature_gauss.h"
00037 #include "libmesh/system.h"
00038 #include "libmesh/threads.h"
00039 #include "libmesh/wrapped_function.h"
00040 
00041 namespace libMesh
00042 {
00043 
00044 // ------------------------------------------------------------
00045 // Helper class definitions
00046 
00052 class ProjectVector
00053 {
00054 private:
00055   const System                &system;
00056   const NumericVector<Number> &old_vector;
00057   NumericVector<Number>       &new_vector;
00058 
00059 public:
00060   ProjectVector (const System &system_in,
00061                  const NumericVector<Number> &old_v_in,
00062                  NumericVector<Number> &new_v_in) :
00063     system(system_in),
00064     old_vector(old_v_in),
00065     new_vector(new_v_in)
00066   {}
00067 
00068   void operator()(const ConstElemRange &range) const;
00069 };
00070 
00071 
00081 class BuildProjectionList
00082 {
00083 private:
00084   const System              &system;
00085 
00086 public:
00087   BuildProjectionList (const System &system_in) :
00088     system(system_in),
00089     send_list()
00090   {}
00091 
00092   BuildProjectionList (BuildProjectionList &other, Threads::split) :
00093     system(other.system),
00094     send_list()
00095   {}
00096 
00097   void unique();
00098   void operator()(const ConstElemRange &range);
00099   void join (const BuildProjectionList &other);
00100   std::vector<dof_id_type> send_list;
00101 };
00102 
00103 
00104 
00110 class ProjectSolution
00111 {
00112 private:
00113   const System                &system;
00114 
00115   UniquePtr<FunctionBase<Number> > f;
00116   UniquePtr<FunctionBase<Gradient> > g;
00117   const Parameters &parameters;
00118   NumericVector<Number>       &new_vector;
00119 
00120 public:
00121   ProjectSolution (const System &system_in,
00122                    FunctionBase<Number>* f_in,
00123                    FunctionBase<Gradient>* g_in,
00124                    const Parameters &parameters_in,
00125                    NumericVector<Number> &new_v_in) :
00126     system(system_in),
00127     f(f_in ? f_in->clone() : UniquePtr<FunctionBase<Number> >()),
00128     g(g_in ? g_in->clone() : UniquePtr<FunctionBase<Gradient> >()),
00129     parameters(parameters_in),
00130     new_vector(new_v_in)
00131   {
00132     libmesh_assert(f.get());
00133     f->init();
00134     if (g.get())
00135       g->init();
00136   }
00137 
00138   ProjectSolution (const ProjectSolution &in) :
00139     system(in.system),
00140     f(in.f.get() ? in.f->clone() : UniquePtr<FunctionBase<Number> >()),
00141     g(in.g.get() ? in.g->clone() : UniquePtr<FunctionBase<Gradient> >()),
00142     parameters(in.parameters),
00143     new_vector(in.new_vector)
00144   {
00145     libmesh_assert(f.get());
00146     f->init();
00147     if (g.get())
00148       g->init();
00149   }
00150 
00151   void operator()(const ConstElemRange &range) const;
00152 };
00153 
00154 
00160 class ProjectFEMSolution
00161 {
00162 private:
00163   const System                &system;
00164 
00165   UniquePtr<FEMFunctionBase<Number> > f;
00166   UniquePtr<FEMFunctionBase<Gradient> > g;
00167   NumericVector<Number>       &new_vector;
00168 
00169 public:
00170   ProjectFEMSolution (const System &system_in,
00171                       FEMFunctionBase<Number>* f_in,
00172                       FEMFunctionBase<Gradient>* g_in,
00173                       NumericVector<Number> &new_v_in) :
00174     system(system_in),
00175     f(f_in ? f_in->clone() : UniquePtr<FEMFunctionBase<Number> >()),
00176     g(g_in ? g_in->clone() : UniquePtr<FEMFunctionBase<Gradient> >()),
00177     new_vector(new_v_in)
00178   {
00179     libmesh_assert(f.get());
00180   }
00181 
00182   ProjectFEMSolution (const ProjectFEMSolution &in) :
00183     system(in.system),
00184     f(in.f.get() ? in.f->clone() : UniquePtr<FEMFunctionBase<Number> >()),
00185     g(in.g.get() ? in.g->clone() : UniquePtr<FEMFunctionBase<Gradient> >()),
00186     new_vector(in.new_vector)
00187   {
00188     libmesh_assert(f.get());
00189   }
00190 
00191   void operator()(const ConstElemRange &range) const;
00192 };
00193 
00194 
00200 class BoundaryProjectSolution
00201 {
00202 private:
00203   const std::set<boundary_id_type> &b;
00204   const std::vector<unsigned int>  &variables;
00205   const System                     &system;
00206   UniquePtr<FunctionBase<Number> >    f;
00207   UniquePtr<FunctionBase<Gradient> >  g;
00208   const Parameters                 &parameters;
00209   NumericVector<Number>            &new_vector;
00210 
00211 public:
00212   BoundaryProjectSolution (const std::set<boundary_id_type> &b_in,
00213                            const std::vector<unsigned int> &variables_in,
00214                            const System &system_in,
00215                            FunctionBase<Number>* f_in,
00216                            FunctionBase<Gradient>* g_in,
00217                            const Parameters &parameters_in,
00218                            NumericVector<Number> &new_v_in) :
00219     b(b_in),
00220     variables(variables_in),
00221     system(system_in),
00222     f(f_in ? f_in->clone() : UniquePtr<FunctionBase<Number> >()),
00223     g(g_in ? g_in->clone() : UniquePtr<FunctionBase<Gradient> >()),
00224     parameters(parameters_in),
00225     new_vector(new_v_in)
00226   {
00227     libmesh_assert(f.get());
00228     f->init();
00229     if (g.get())
00230       g->init();
00231   }
00232 
00233   BoundaryProjectSolution (const BoundaryProjectSolution &in) :
00234     b(in.b),
00235     variables(in.variables),
00236     system(in.system),
00237     f(in.f.get() ? in.f->clone() : UniquePtr<FunctionBase<Number> >()),
00238     g(in.g.get() ? in.g->clone() : UniquePtr<FunctionBase<Gradient> >()),
00239     parameters(in.parameters),
00240     new_vector(in.new_vector)
00241   {
00242     libmesh_assert(f.get());
00243     f->init();
00244     if (g.get())
00245       g->init();
00246   }
00247 
00248   void operator()(const ConstElemRange &range) const;
00249 };
00250 
00251 
00252 
00253 // ------------------------------------------------------------
00254 // System implementation
00255 void System::project_vector (NumericVector<Number>& vector,
00256                              int is_adjoint) const
00257 {
00258   // Create a copy of the vector, which currently
00259   // contains the old data.
00260   UniquePtr<NumericVector<Number> >
00261     old_vector (vector.clone());
00262 
00263   // Project the old vector to the new vector
00264   this->project_vector (*old_vector, vector, is_adjoint);
00265 }
00266 
00267 
00273 void System::project_vector (const NumericVector<Number>& old_v,
00274                              NumericVector<Number>& new_v,
00275                              int is_adjoint) const
00276 {
00277   START_LOG ("project_vector()", "System");
00278 
00285   new_v.clear();
00286 
00287 #ifdef LIBMESH_ENABLE_AMR
00288 
00289   // Resize the new vector and get a serial version.
00290   NumericVector<Number> *new_vector_ptr = NULL;
00291   UniquePtr<NumericVector<Number> > new_vector_built;
00292   NumericVector<Number> *local_old_vector;
00293   UniquePtr<NumericVector<Number> > local_old_vector_built;
00294   const NumericVector<Number> *old_vector_ptr = NULL;
00295 
00296   ConstElemRange active_local_elem_range
00297     (this->get_mesh().active_local_elements_begin(),
00298      this->get_mesh().active_local_elements_end());
00299 
00300   // If the old vector was uniprocessor, make the new
00301   // vector uniprocessor
00302   if (old_v.type() == SERIAL)
00303     {
00304       new_v.init (this->n_dofs(), false, SERIAL);
00305       new_vector_ptr = &new_v;
00306       old_vector_ptr = &old_v;
00307     }
00308 
00309   // Otherwise it is a parallel, distributed vector, which
00310   // we need to localize.
00311   else if (old_v.type() == PARALLEL)
00312     {
00313       // Build a send list for efficient localization
00314       BuildProjectionList projection_list(*this);
00315       Threads::parallel_reduce (active_local_elem_range,
00316                                 projection_list);
00317 
00318       // Create a sorted, unique send_list
00319       projection_list.unique();
00320 
00321       new_v.init (this->n_dofs(), this->n_local_dofs(), false, PARALLEL);
00322       new_vector_built = NumericVector<Number>::build(this->comm());
00323       local_old_vector_built = NumericVector<Number>::build(this->comm());
00324       new_vector_ptr = new_vector_built.get();
00325       local_old_vector = local_old_vector_built.get();
00326       new_vector_ptr->init(this->n_dofs(), false, SERIAL);
00327       local_old_vector->init(old_v.size(), false, SERIAL);
00328       old_v.localize(*local_old_vector, projection_list.send_list);
00329       local_old_vector->close();
00330       old_vector_ptr = local_old_vector;
00331     }
00332   else if (old_v.type() == GHOSTED)
00333     {
00334       // Build a send list for efficient localization
00335       BuildProjectionList projection_list(*this);
00336       Threads::parallel_reduce (active_local_elem_range,
00337                                 projection_list);
00338 
00339       // Create a sorted, unique send_list
00340       projection_list.unique();
00341 
00342       new_v.init (this->n_dofs(), this->n_local_dofs(),
00343                   this->get_dof_map().get_send_list(), false, GHOSTED);
00344 
00345       local_old_vector_built = NumericVector<Number>::build(this->comm());
00346       new_vector_ptr = &new_v;
00347       local_old_vector = local_old_vector_built.get();
00348       local_old_vector->init(old_v.size(), old_v.local_size(),
00349                              projection_list.send_list, false, GHOSTED);
00350       old_v.localize(*local_old_vector, projection_list.send_list);
00351       local_old_vector->close();
00352       old_vector_ptr = local_old_vector;
00353     }
00354   else // unknown old_v.type()
00355     libmesh_error_msg("ERROR: Unknown old_v.type() == " << old_v.type());
00356 
00357   // Note that the above will have zeroed the new_vector.
00358   // Just to be sure, assert that new_vector_ptr and old_vector_ptr
00359   // were successfully set before trying to deref them.
00360   libmesh_assert(new_vector_ptr);
00361   libmesh_assert(old_vector_ptr);
00362 
00363   NumericVector<Number> &new_vector = *new_vector_ptr;
00364   const NumericVector<Number> &old_vector = *old_vector_ptr;
00365 
00366   Threads::parallel_for (active_local_elem_range,
00367                          ProjectVector(*this,
00368                                        old_vector,
00369                                        new_vector)
00370                          );
00371 
00372   // Copy the SCALAR dofs from old_vector to new_vector
00373   // Note: We assume that all SCALAR dofs are on the
00374   // processor with highest ID
00375   if(this->processor_id() == (this->n_processors()-1))
00376     {
00377       const DofMap& dof_map = this->get_dof_map();
00378       for (unsigned int var=0; var<this->n_vars(); var++)
00379         if(this->variable(var).type().family == SCALAR)
00380           {
00381             // We can just map SCALAR dofs directly across
00382             std::vector<dof_id_type> new_SCALAR_indices, old_SCALAR_indices;
00383             dof_map.SCALAR_dof_indices (new_SCALAR_indices, var, false);
00384             dof_map.SCALAR_dof_indices (old_SCALAR_indices, var, true);
00385             const unsigned int new_n_dofs =
00386               cast_int<unsigned int>(new_SCALAR_indices.size());
00387 
00388             for (unsigned int i=0; i<new_n_dofs; i++)
00389               {
00390                 new_vector.set( new_SCALAR_indices[i], old_vector(old_SCALAR_indices[i]) );
00391               }
00392           }
00393     }
00394 
00395   new_vector.close();
00396 
00397   // If the old vector was serial, we probably need to send our values
00398   // to other processors
00399   //
00400   // FIXME: I'm not sure how to make a NumericVector do that without
00401   // creating a temporary parallel vector to use localize! - RHS
00402   if (old_v.type() == SERIAL)
00403     {
00404       UniquePtr<NumericVector<Number> > dist_v = NumericVector<Number>::build(this->comm());
00405       dist_v->init(this->n_dofs(), this->n_local_dofs(), false, PARALLEL);
00406       dist_v->close();
00407 
00408       for (dof_id_type i=0; i!=dist_v->size(); i++)
00409         if (new_vector(i) != 0.0)
00410           dist_v->set(i, new_vector(i));
00411 
00412       dist_v->close();
00413 
00414       dist_v->localize (new_v, this->get_dof_map().get_send_list());
00415       new_v.close();
00416     }
00417   // If the old vector was parallel, we need to update it
00418   // and free the localized copies
00419   else if (old_v.type() == PARALLEL)
00420     {
00421       // We may have to set dof values that this processor doesn't
00422       // own in certain special cases, like LAGRANGE FIRST or
00423       // HERMITE THIRD elements on second-order meshes
00424       for (dof_id_type i=0; i!=new_v.size(); i++)
00425         if (new_vector(i) != 0.0)
00426           new_v.set(i, new_vector(i));
00427       new_v.close();
00428     }
00429 
00430   if (is_adjoint == -1)
00431     this->get_dof_map().enforce_constraints_exactly(*this, &new_v);
00432   else if (is_adjoint >= 0)
00433     this->get_dof_map().enforce_adjoint_constraints_exactly(new_v,
00434                                                             is_adjoint);
00435 
00436 #else
00437 
00438   // AMR is disabled: simply copy the vector
00439   new_v = old_v;
00440 
00441 #endif // #ifdef LIBMESH_ENABLE_AMR
00442 
00443   STOP_LOG("project_vector()", "System");
00444 }
00445 
00446 
00447 
00452 void System::project_solution (Number fptr(const Point& p,
00453                                            const Parameters& parameters,
00454                                            const std::string& sys_name,
00455                                            const std::string& unknown_name),
00456                                Gradient gptr(const Point& p,
00457                                              const Parameters& parameters,
00458                                              const std::string& sys_name,
00459                                              const std::string& unknown_name),
00460                                const Parameters& parameters) const
00461 {
00462   WrappedFunction<Number> f(*this, fptr, &parameters);
00463   WrappedFunction<Gradient> g(*this, gptr, &parameters);
00464   this->project_solution(&f, &g);
00465 }
00466 
00467 
00472 void System::project_solution (FunctionBase<Number> *f,
00473                                FunctionBase<Gradient> *g) const
00474 {
00475   this->project_vector(*solution, f, g);
00476 
00477   solution->localize(*current_local_solution, _dof_map->get_send_list());
00478 }
00479 
00480 
00485 void System::project_solution (FEMFunctionBase<Number> *f,
00486                                FEMFunctionBase<Gradient> *g) const
00487 {
00488   this->project_vector(*solution, f, g);
00489 
00490   solution->localize(*current_local_solution, _dof_map->get_send_list());
00491 }
00492 
00493 
00498 void System::project_vector (Number fptr(const Point& p,
00499                                          const Parameters& parameters,
00500                                          const std::string& sys_name,
00501                                          const std::string& unknown_name),
00502                              Gradient gptr(const Point& p,
00503                                            const Parameters& parameters,
00504                                            const std::string& sys_name,
00505                                            const std::string& unknown_name),
00506                              const Parameters& parameters,
00507                              NumericVector<Number>& new_vector,
00508                              int is_adjoint) const
00509 {
00510   WrappedFunction<Number> f(*this, fptr, &parameters);
00511   WrappedFunction<Gradient> g(*this, gptr, &parameters);
00512   this->project_vector(new_vector, &f, &g, is_adjoint);
00513 }
00514 
00519 void System::project_vector (NumericVector<Number>& new_vector,
00520                              FunctionBase<Number> *f,
00521                              FunctionBase<Gradient> *g,
00522                              int is_adjoint) const
00523 {
00524   START_LOG ("project_vector()", "System");
00525 
00526   Threads::parallel_for
00527     (ConstElemRange (this->get_mesh().active_local_elements_begin(),
00528                      this->get_mesh().active_local_elements_end() ),
00529      ProjectSolution(*this, f, g,
00530                      this->get_equation_systems().parameters,
00531                      new_vector)
00532      );
00533 
00534   // Also, load values into the SCALAR dofs
00535   // Note: We assume that all SCALAR dofs are on the
00536   // processor with highest ID
00537   if(this->processor_id() == (this->n_processors()-1))
00538     {
00539       // We get different scalars as different
00540       // components from a new-style f functor.
00541       DenseVector<Number> fout(this->n_components());
00542       bool filled_fout = false;
00543 
00544       const DofMap& dof_map = this->get_dof_map();
00545       for (unsigned int var=0; var<this->n_vars(); var++)
00546         if(this->variable(var).type().family == SCALAR)
00547           {
00548             if (!filled_fout)
00549               {
00550                 (*f) (Point(), this->time, fout);
00551                 filled_fout = true;
00552               }
00553 
00554             std::vector<dof_id_type> SCALAR_indices;
00555             dof_map.SCALAR_dof_indices (SCALAR_indices, var);
00556             const unsigned int n_SCALAR_dofs =
00557               cast_int<unsigned int>(SCALAR_indices.size());
00558 
00559             for (unsigned int i=0; i<n_SCALAR_dofs; i++)
00560               {
00561                 const dof_id_type global_index = SCALAR_indices[i];
00562                 const unsigned int component_index =
00563                   this->variable_scalar_number(var,i);
00564                 new_vector.set(global_index, fout(component_index));
00565               }
00566           }
00567     }
00568 
00569   new_vector.close();
00570 
00571 #ifdef LIBMESH_ENABLE_CONSTRAINTS
00572   if (is_adjoint == -1)
00573     this->get_dof_map().enforce_constraints_exactly(*this, &new_vector);
00574   else if (is_adjoint >= 0)
00575     this->get_dof_map().enforce_adjoint_constraints_exactly(new_vector,
00576                                                             is_adjoint);
00577 #endif
00578 
00579   STOP_LOG("project_vector()", "System");
00580 }
00581 
00582 
00587 void System::project_vector (NumericVector<Number>& new_vector,
00588                              FEMFunctionBase<Number> *f,
00589                              FEMFunctionBase<Gradient> *g,
00590                              int is_adjoint) const
00591 {
00592   START_LOG ("project_fem_vector()", "System");
00593 
00594   Threads::parallel_for
00595     (ConstElemRange (this->get_mesh().active_local_elements_begin(),
00596                      this->get_mesh().active_local_elements_end() ),
00597      ProjectFEMSolution(*this, f, g, new_vector)
00598      );
00599 
00600   // Also, load values into the SCALAR dofs
00601   // Note: We assume that all SCALAR dofs are on the
00602   // processor with highest ID
00603   if(this->processor_id() == (this->n_processors()-1))
00604     {
00605       // FIXME: Do we want to first check for SCALAR vars before building this? [PB]
00606       FEMContext context( *this );
00607 
00608       const DofMap& dof_map = this->get_dof_map();
00609       for (unsigned int var=0; var<this->n_vars(); var++)
00610         if(this->variable(var).type().family == SCALAR)
00611           {
00612             // FIXME: We reinit with an arbitrary element in case the user
00613             //        doesn't override FEMFunctionBase::component. Is there
00614             //        any use case we're missing? [PB]
00615             Elem *el = const_cast<Elem *>(*(this->get_mesh().active_local_elements_begin()));
00616             context.pre_fe_reinit( *this, el );
00617 
00618             std::vector<dof_id_type> SCALAR_indices;
00619             dof_map.SCALAR_dof_indices (SCALAR_indices, var);
00620             const unsigned int n_SCALAR_dofs =
00621               cast_int<unsigned int>(SCALAR_indices.size());
00622 
00623             for (unsigned int i=0; i<n_SCALAR_dofs; i++)
00624               {
00625                 const dof_id_type global_index = SCALAR_indices[i];
00626                 const unsigned int component_index =
00627                   this->variable_scalar_number(var,i);
00628 
00629                 new_vector.set(global_index, f->component(context, component_index, Point(), this->time));
00630               }
00631           }
00632     }
00633 
00634   new_vector.close();
00635 
00636 #ifdef LIBMESH_ENABLE_CONSTRAINTS
00637   if (is_adjoint == -1)
00638     this->get_dof_map().enforce_constraints_exactly(*this, &new_vector);
00639   else if (is_adjoint >= 0)
00640     this->get_dof_map().enforce_adjoint_constraints_exactly(new_vector,
00641                                                             is_adjoint);
00642 #endif
00643 
00644   STOP_LOG("project_fem_vector()", "System");
00645 }
00646 
00647 
00653 void System::boundary_project_solution
00654 (const std::set<boundary_id_type> &b,
00655  const std::vector<unsigned int> &variables,
00656  Number fptr(const Point& p,
00657              const Parameters& parameters,
00658              const std::string& sys_name,
00659              const std::string& unknown_name),
00660  Gradient gptr(const Point& p,
00661                const Parameters& parameters,
00662                const std::string& sys_name,
00663                const std::string& unknown_name),
00664  const Parameters& parameters)
00665 {
00666   WrappedFunction<Number> f(*this, fptr, &parameters);
00667   WrappedFunction<Gradient> g(*this, gptr, &parameters);
00668   this->boundary_project_solution(b, variables, &f, &g);
00669 }
00670 
00671 
00677 void System::boundary_project_solution
00678 (const std::set<boundary_id_type> &b,
00679  const std::vector<unsigned int> &variables,
00680  FunctionBase<Number> *f,
00681  FunctionBase<Gradient> *g)
00682 {
00683   this->boundary_project_vector(b, variables, *solution, f, g);
00684 
00685   solution->localize(*current_local_solution);
00686 }
00687 
00688 
00689 
00690 
00691 
00696 void System::boundary_project_vector
00697 (const std::set<boundary_id_type> &b,
00698  const std::vector<unsigned int> &variables,
00699  Number fptr(const Point& p,
00700              const Parameters& parameters,
00701              const std::string& sys_name,
00702              const std::string& unknown_name),
00703  Gradient gptr(const Point& p,
00704                const Parameters& parameters,
00705                const std::string& sys_name,
00706                const std::string& unknown_name),
00707  const Parameters& parameters,
00708  NumericVector<Number>& new_vector,
00709  int is_adjoint) const
00710 {
00711   WrappedFunction<Number> f(*this, fptr, &parameters);
00712   WrappedFunction<Gradient> g(*this, gptr, &parameters);
00713   this->boundary_project_vector(b, variables, new_vector, &f, &g,
00714                                 is_adjoint);
00715 }
00716 
00721 void System::boundary_project_vector
00722 (const std::set<boundary_id_type> &b,
00723  const std::vector<unsigned int> &variables,
00724  NumericVector<Number>& new_vector,
00725  FunctionBase<Number> *f,
00726  FunctionBase<Gradient> *g,
00727  int is_adjoint) const
00728 {
00729   START_LOG ("boundary_project_vector()", "System");
00730 
00731   Threads::parallel_for
00732     (ConstElemRange (this->get_mesh().active_local_elements_begin(),
00733                      this->get_mesh().active_local_elements_end() ),
00734      BoundaryProjectSolution(b, variables, *this, f, g,
00735                              this->get_equation_systems().parameters,
00736                              new_vector)
00737      );
00738 
00739   // We don't do SCALAR dofs when just projecting the boundary, so
00740   // we're done here.
00741 
00742   new_vector.close();
00743 
00744 #ifdef LIBMESH_ENABLE_CONSTRAINTS
00745   if (is_adjoint == -1)
00746     this->get_dof_map().enforce_constraints_exactly(*this, &new_vector);
00747   else if (is_adjoint >= 0)
00748     this->get_dof_map().enforce_adjoint_constraints_exactly(new_vector,
00749                                                             is_adjoint);
00750 #endif
00751 
00752   STOP_LOG("boundary_project_vector()", "System");
00753 }
00754 
00755 
00756 
00757 #ifndef LIBMESH_ENABLE_AMR
00758 void ProjectVector::operator()(const ConstElemRange &) const
00759 {
00760   libmesh_not_implemented();
00761 }
00762 #else
00763 void ProjectVector::operator()(const ConstElemRange &range) const
00764 {
00765   START_LOG ("operator()","ProjectVector");
00766 
00767   // A vector for Lagrange element interpolation, indicating if we
00768   // have visited a DOF yet.  Note that this is thread-local storage,
00769   // hence shared DOFS that live on thread boundaries may be doubly
00770   // computed.  It is expected that this will be more efficient
00771   // than locking a thread-global version of already_done, though.
00772   //
00773   // FIXME: we should use this for non-Lagrange coarsening, too
00774   std::vector<bool> already_done (system.n_dofs(), false);
00775 
00776   // The number of variables in this system
00777   const unsigned int n_variables = system.n_vars();
00778 
00779   // The dimensionality of the current mesh
00780   const unsigned int dim = system.get_mesh().mesh_dimension();
00781 
00782   // The DofMap for this system
00783   const DofMap& dof_map = system.get_dof_map();
00784 
00785   // The element matrix and RHS for projections.
00786   // Note that Ke is always real-valued, whereas
00787   // Fe may be complex valued if complex number
00788   // support is enabled
00789   DenseMatrix<Real> Ke;
00790   DenseVector<Number> Fe;
00791   // The new element coefficients
00792   DenseVector<Number> Ue;
00793 
00794 
00795   // Loop over all the variables in the system
00796   for (unsigned int var=0; var<n_variables; var++)
00797     {
00798       const Variable& variable = dof_map.variable(var);
00799 
00800       const FEType& base_fe_type = variable.type();
00801 
00802       if (base_fe_type.family == SCALAR)
00803         continue;
00804 
00805       // Get FE objects of the appropriate type
00806       UniquePtr<FEBase> fe (FEBase::build(dim, base_fe_type));
00807       UniquePtr<FEBase> fe_coarse (FEBase::build(dim, base_fe_type));
00808 
00809       // Create FE objects with potentially different p_level
00810       FEType fe_type, temp_fe_type;
00811 
00812       // Prepare variables for non-Lagrange projection
00813       UniquePtr<QBase> qrule     (base_fe_type.default_quadrature_rule(dim));
00814       UniquePtr<QBase> qedgerule (base_fe_type.default_quadrature_rule(1));
00815       UniquePtr<QBase> qsiderule (base_fe_type.default_quadrature_rule(dim-1));
00816       std::vector<Point> coarse_qpoints;
00817 
00818       // The values of the shape functions at the quadrature
00819       // points
00820       const std::vector<std::vector<Real> >& phi_values =
00821         fe->get_phi();
00822       const std::vector<std::vector<Real> >& phi_coarse =
00823         fe_coarse->get_phi();
00824 
00825       // The Jacobian * quadrature weight at the quadrature points
00826       const std::vector<Real>& JxW =
00827         fe->get_JxW();
00828 
00829       // The XYZ locations of the quadrature points on the
00830       // child element
00831       const std::vector<Point>& xyz_values =
00832         fe->get_xyz();
00833 
00834 
00835       // The global DOF indices
00836       std::vector<dof_id_type> new_dof_indices, old_dof_indices;
00837 
00838       // Iterate over the elements in the range
00839       for (ConstElemRange::const_iterator elem_it=range.begin(); elem_it != range.end(); ++elem_it)
00840         {
00841           const Elem* elem = *elem_it;
00842           // If this element doesn't have an old_dof_object with dofs for the
00843           // current system, then it must be newly added, so the user
00844           // is responsible for setting the new dofs.
00845 
00846           // ... but we need a better way to test for that; the code
00847           // below breaks on any FE type for which the elem stores no
00848           // dofs.
00849           // if (!elem->old_dof_object || !elem->old_dof_object->has_dofs(system.number()))
00850           //  continue;
00851           const Elem* parent = elem->parent();
00852 
00853           // Per-subdomain variables don't need to be projected on
00854           // elements where they're not active
00855           if (!variable.active_on_subdomain(elem->subdomain_id()))
00856             continue;
00857 
00858           // Adjust the FE type for p-refined elements
00859           fe_type = base_fe_type;
00860           fe_type.order = static_cast<Order>(fe_type.order +
00861                                              elem->p_level());
00862 
00863           // We may need to remember the parent's p_level
00864           unsigned int old_parent_level = 0;
00865 
00866           // Update the DOF indices for this element based on
00867           // the new mesh
00868           dof_map.dof_indices (elem, new_dof_indices, var);
00869 
00870           // The number of DOFs on the new element
00871           const unsigned int new_n_dofs =
00872             cast_int<unsigned int>(new_dof_indices.size());
00873 
00874           // Fixed vs. free DoFs on edge/face projections
00875           std::vector<char> dof_is_fixed(new_n_dofs, false); // bools
00876           std::vector<int> free_dof(new_n_dofs, 0);
00877 
00878           // The element type
00879           const ElemType elem_type = elem->type();
00880 
00881           // The number of nodes on the new element
00882           const unsigned int n_nodes = elem->n_nodes();
00883 
00884           // Zero the interpolated values
00885           Ue.resize (new_n_dofs); Ue.zero();
00886 
00887           // Update the DOF indices based on the old mesh.
00888           // This is done in one of three ways:
00889           // 1.) If the child was just refined then it was not
00890           //     active in the previous mesh & hence has no solution
00891           //     values on it.  In this case simply project or
00892           //     interpolate the solution from the parent, who was
00893           //     active in the previous mesh
00894           // 2.) If the child was just coarsened, obtaining a
00895           //     well-defined solution may require doing independent
00896           //     projections on nodes, edges, faces, and interiors
00897           // 3.) If the child was active in the previous
00898           //     mesh, we can just copy coefficients directly
00899           if (elem->refinement_flag() == Elem::JUST_REFINED)
00900             {
00901               libmesh_assert(parent);
00902               old_parent_level = parent->p_level();
00903 
00904               // We may have done p refinement or coarsening as well;
00905               // if so then we need to reset the parent's p level
00906               // so we can get the right DoFs from it
00907               if (elem->p_refinement_flag() == Elem::JUST_REFINED)
00908                 {
00909                   libmesh_assert_greater (elem->p_level(), 0);
00910                   (const_cast<Elem *>(parent))->hack_p_level(elem->p_level() - 1);
00911                 }
00912               else if (elem->p_refinement_flag() == Elem::JUST_COARSENED)
00913                 {
00914                   (const_cast<Elem *>(parent))->hack_p_level(elem->p_level() + 1);
00915                 }
00916 
00917               dof_map.old_dof_indices (parent, old_dof_indices, var);
00918             }
00919           else
00920             {
00921               dof_map.old_dof_indices (elem, old_dof_indices, var);
00922 
00923               if (elem->p_refinement_flag() == Elem::DO_NOTHING)
00924                 libmesh_assert_equal_to (old_dof_indices.size(), new_n_dofs);
00925 
00926               // if (elem->p_refinement_flag() == Elem::JUST_COARSENED)
00927               //   libmesh_assert (elem->has_children());
00928             }
00929 
00930           unsigned int old_n_dofs =
00931             cast_int<unsigned int>(old_dof_indices.size());
00932 
00933           if (fe_type.family != LAGRANGE) {
00934 
00935             // For refined non-Lagrange elements, we do an L2
00936             // projection
00937             // FIXME: this will be a suboptimal and ill-defined
00938             // result if we're using non-nested finite element
00939             // spaces or if we're on a p-coarsened element!
00940             if (elem->refinement_flag() == Elem::JUST_REFINED)
00941               {
00942                 // Update the fe object based on the current child
00943                 fe->attach_quadrature_rule (qrule.get());
00944                 fe->reinit (elem);
00945 
00946                 // The number of quadrature points on the child
00947                 const unsigned int n_qp = qrule->n_points();
00948 
00949                 FEInterface::inverse_map (dim, fe_type, parent,
00950                                           xyz_values, coarse_qpoints);
00951 
00952                 fe_coarse->reinit(parent, &coarse_qpoints);
00953 
00954                 // Reinitialize the element matrix and vector for
00955                 // the current element.  Note that this will zero them
00956                 // before they are summed.
00957                 Ke.resize (new_n_dofs, new_n_dofs); Ke.zero();
00958                 Fe.resize (new_n_dofs); Fe.zero();
00959 
00960 
00961                 // Loop over the quadrature points
00962                 for (unsigned int qp=0; qp<n_qp; qp++)
00963                   {
00964                     // The solution value at the quadrature point
00965                     Number val = libMesh::zero;
00966 
00967                     // Sum the function values * the DOF values
00968                     // at the point of interest to get the function value
00969                     // (Note that the # of DOFs on the parent need not be the
00970                     //  same as on the child!)
00971                     for (unsigned int i=0; i<old_n_dofs; i++)
00972                       {
00973                         val += (old_vector(old_dof_indices[i])*
00974                                 phi_coarse[i][qp]);
00975                       }
00976 
00977                     // Now \p val contains the solution value of variable
00978                     // \p var at the qp'th quadrature point on the child
00979                     // element.  It has been interpolated from the parent
00980                     // in case the child was just refined.  Next we will
00981                     // construct the L2-projection matrix for the element.
00982 
00983                     // Construct the Mass Matrix
00984                     for (unsigned int i=0; i<new_n_dofs; i++)
00985                       for (unsigned int j=0; j<new_n_dofs; j++)
00986                         Ke(i,j) += JxW[qp]*phi_values[i][qp]*phi_values[j][qp];
00987 
00988                     // Construct the RHS
00989                     for (unsigned int i=0; i<new_n_dofs; i++)
00990                       Fe(i) += JxW[qp]*phi_values[i][qp]*val;
00991 
00992                   } // end qp loop
00993 
00994                 Ke.cholesky_solve(Fe, Ue);
00995 
00996                 // Fix up the parent's p level in case we changed it
00997                 (const_cast<Elem *>(parent))->hack_p_level(old_parent_level);
00998               }
00999             else if (elem->refinement_flag() == Elem::JUST_COARSENED)
01000               {
01001                 FEBase::coarsened_dof_values(old_vector, dof_map,
01002                                              elem, Ue, var, true);
01003               }
01004             // For unrefined uncoarsened elements, we just copy DoFs
01005             else
01006               {
01007                 // FIXME - I'm sure this function would be about half
01008                 // the size if anyone ever figures out how to improve
01009                 // the DofMap interface... - RHS
01010                 if (elem->p_refinement_flag() == Elem::JUST_REFINED)
01011                   {
01012                     libmesh_assert_greater (elem->p_level(), 0);
01013                     // P refinement of non-hierarchic bases will
01014                     // require a whole separate code path
01015                     libmesh_assert (fe->is_hierarchic());
01016                     temp_fe_type = fe_type;
01017                     temp_fe_type.order =
01018                       static_cast<Order>(temp_fe_type.order - 1);
01019                     unsigned int old_index = 0, new_index = 0;
01020                     for (unsigned int n=0; n != n_nodes; ++n)
01021                       {
01022                         const unsigned int nc =
01023                           FEInterface::n_dofs_at_node (dim, temp_fe_type,
01024                                                        elem_type, n);
01025                         for (unsigned int i=0; i != nc; ++i)
01026                           {
01027                             Ue(new_index + i) =
01028                               old_vector(old_dof_indices[old_index++]);
01029                           }
01030                         new_index +=
01031                           FEInterface::n_dofs_at_node (dim, fe_type,
01032                                                        elem_type, n);
01033                       }
01034                     const unsigned int nc =
01035                       FEInterface::n_dofs_per_elem (dim, temp_fe_type,
01036                                                     elem_type);
01037                     for (unsigned int i=0; i != nc; ++i)
01038                       {
01039                         Ue(new_index++) =
01040                           old_vector(old_dof_indices[old_index+i]);
01041                       }
01042                   }
01043                 else if (elem->p_refinement_flag() ==
01044                          Elem::JUST_COARSENED)
01045                   {
01046                     // P coarsening of non-hierarchic bases will
01047                     // require a whole separate code path
01048                     libmesh_assert (fe->is_hierarchic());
01049                     temp_fe_type = fe_type;
01050                     temp_fe_type.order =
01051                       static_cast<Order>(temp_fe_type.order + 1);
01052                     unsigned int old_index = 0, new_index = 0;
01053                     for (unsigned int n=0; n != n_nodes; ++n)
01054                       {
01055                         const unsigned int nc =
01056                           FEInterface::n_dofs_at_node (dim, fe_type,
01057                                                        elem_type, n);
01058                         for (unsigned int i=0; i != nc; ++i)
01059                           {
01060                             Ue(new_index++) =
01061                               old_vector(old_dof_indices[old_index+i]);
01062                           }
01063                         old_index +=
01064                           FEInterface::n_dofs_at_node (dim, temp_fe_type,
01065                                                        elem_type, n);
01066                       }
01067                     const unsigned int nc =
01068                       FEInterface::n_dofs_per_elem (dim, fe_type,
01069                                                     elem_type);
01070                     for (unsigned int i=0; i != nc; ++i)
01071                       {
01072                         Ue(new_index++) =
01073                           old_vector(old_dof_indices[old_index+i]);
01074                       }
01075                   }
01076                 else
01077                   // If there's no p refinement, we can copy every DoF
01078                   for (unsigned int i=0; i<new_n_dofs; i++)
01079                     Ue(i) = old_vector(old_dof_indices[i]);
01080               }
01081           }
01082           else { // fe type is Lagrange
01083             // Loop over the DOFs on the element
01084             for (unsigned int new_local_dof=0;
01085                  new_local_dof<new_n_dofs; new_local_dof++)
01086               {
01087                 // The global DOF number for this local DOF
01088                 const dof_id_type new_global_dof =
01089                   new_dof_indices[new_local_dof];
01090 
01091                 // The global DOF might lie outside of the bounds of a
01092                 // distributed vector.  Check for that and possibly continue
01093                 // the loop
01094                 if ((new_global_dof <  new_vector.first_local_index()) ||
01095                     (new_global_dof >= new_vector.last_local_index()))
01096                   continue;
01097 
01098                 // We might have already computed the solution for this DOF.
01099                 // This is likely in the case of a shared node, particularly
01100                 // at the corners of an element.  Check to see if that is the
01101                 // case
01102                 if (already_done[new_global_dof] == true)
01103                   continue;
01104 
01105                 already_done[new_global_dof] = true;
01106 
01107                 if (elem->refinement_flag() == Elem::JUST_REFINED ||
01108                     elem->p_refinement_flag() == Elem::JUST_REFINED)
01109                   {
01110                     // The location of the child's node on the parent element
01111                     const Point point =
01112                       FEInterface::inverse_map (dim, fe_type, parent,
01113                                                 elem->point(new_local_dof));
01114 
01115                     // Sum the function values * the DOF values
01116                     // at the point of interest to get the function value
01117                     // (Note that the # of DOFs on the parent need not be the
01118                     //  same as on the child!)
01119                     for (unsigned int old_local_dof=0;
01120                          old_local_dof<old_n_dofs; old_local_dof++)
01121                       {
01122                         const dof_id_type old_global_dof =
01123                           old_dof_indices[old_local_dof];
01124 
01125                         Ue(new_local_dof) +=
01126                           (old_vector(old_global_dof)*
01127                            FEInterface::shape(dim, base_fe_type, parent,
01128                                               old_local_dof, point));
01129                       }
01130                   }
01131                 else
01132                   {
01133                     // Get the old global DOF index
01134                     const dof_id_type old_global_dof =
01135                       old_dof_indices[new_local_dof];
01136 
01137                     Ue(new_local_dof) = old_vector(old_global_dof);
01138                   }
01139               } // end local DOF loop
01140 
01141             // We may have to clean up a parent's p_level
01142             if (elem->refinement_flag() == Elem::JUST_REFINED)
01143               (const_cast<Elem *>(parent))->hack_p_level(old_parent_level);
01144           }  // end fe_type if()
01145 
01146           // Lock the new_vector since it is shared among threads.
01147           {
01148             Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
01149 
01150             for (unsigned int i = 0; i < new_n_dofs; i++)
01151               if (Ue(i) != 0.)
01152                 new_vector.set(new_dof_indices[i], Ue(i));
01153           }
01154         }  // end elem loop
01155     } // end variables loop
01156 
01157   STOP_LOG ("operator()","ProjectVector");
01158 }
01159 #endif // LIBMESH_ENABLE_AMR
01160 
01161 
01162 
01163 void BuildProjectionList::unique()
01164 {
01165   // Sort the send list.  After this duplicated
01166   // elements will be adjacent in the vector
01167   std::sort(this->send_list.begin(),
01168             this->send_list.end());
01169 
01170   // Now use std::unique to remove duplicate entries
01171   std::vector<dof_id_type>::iterator new_end =
01172     std::unique (this->send_list.begin(),
01173                  this->send_list.end());
01174 
01175   // Remove the end of the send_list.  Use the "swap trick"
01176   // from Effective STL
01177   std::vector<dof_id_type>
01178     (this->send_list.begin(), new_end).swap (this->send_list);
01179 }
01180 
01181 
01182 
01183 #ifndef LIBMESH_ENABLE_AMR
01184 void BuildProjectionList::operator()(const ConstElemRange &)
01185 {
01186   libmesh_not_implemented();
01187 }
01188 #else
01189 void BuildProjectionList::operator()(const ConstElemRange &range)
01190 {
01191   // The DofMap for this system
01192   const DofMap& dof_map = system.get_dof_map();
01193 
01194   const dof_id_type first_old_dof = dof_map.first_old_dof();
01195   const dof_id_type end_old_dof   = dof_map.end_old_dof();
01196 
01197   // We can handle all the variables at once.
01198   // The old global DOF indices
01199   std::vector<dof_id_type> di;
01200 
01201   // Iterate over the elements in the range
01202   for (ConstElemRange::const_iterator elem_it=range.begin(); elem_it != range.end(); ++elem_it)
01203     {
01204       const Elem* elem = *elem_it;
01205       // If this element doesn't have an old_dof_object with dofs for the
01206       // current system, then it must be newly added, so the user
01207       // is responsible for setting the new dofs.
01208 
01209       // ... but we need a better way to test for that; the code
01210       // below breaks on any FE type for which the elem stores no
01211       // dofs.
01212       // if (!elem->old_dof_object || !elem->old_dof_object->has_dofs(system.number()))
01213       //  continue;
01214       const Elem* parent = elem->parent();
01215 
01216       if (elem->refinement_flag() == Elem::JUST_REFINED)
01217         {
01218           libmesh_assert(parent);
01219           unsigned int old_parent_level = parent->p_level();
01220 
01221           if (elem->p_refinement_flag() == Elem::JUST_REFINED)
01222             {
01223               // We may have done p refinement or coarsening as well;
01224               // if so then we need to reset the parent's p level
01225               // so we can get the right DoFs from it
01226               libmesh_assert_greater (elem->p_level(), 0);
01227               (const_cast<Elem *>(parent))->hack_p_level(elem->p_level() - 1);
01228             }
01229           else if (elem->p_refinement_flag() == Elem::JUST_COARSENED)
01230             {
01231               (const_cast<Elem *>(parent))->hack_p_level(elem->p_level() + 1);
01232             }
01233 
01234           dof_map.old_dof_indices (parent, di);
01235 
01236           // Fix up the parent's p level in case we changed it
01237           (const_cast<Elem *>(parent))->hack_p_level(old_parent_level);
01238         }
01239       else if (elem->refinement_flag() == Elem::JUST_COARSENED)
01240         {
01241           std::vector<dof_id_type> di_child;
01242           di.clear();
01243           for (unsigned int c=0; c != elem->n_children(); ++c)
01244             {
01245               dof_map.old_dof_indices (elem->child(c), di_child);
01246               di.insert(di.end(), di_child.begin(), di_child.end());
01247             }
01248         }
01249       else
01250         dof_map.old_dof_indices (elem, di);
01251 
01252       for (unsigned int i=0; i != di.size(); ++i)
01253         if (di[i] < first_old_dof || di[i] >= end_old_dof)
01254           this->send_list.push_back(di[i]);
01255     }  // end elem loop
01256 }
01257 #endif // LIBMESH_ENABLE_AMR
01258 
01259 
01260 
01261 void BuildProjectionList::join(const BuildProjectionList &other)
01262 {
01263   // Joining simply requires I add the dof indices from the other object
01264   this->send_list.insert(this->send_list.end(),
01265                          other.send_list.begin(),
01266                          other.send_list.end());
01267 }
01268 
01269 
01270 void ProjectSolution::operator()(const ConstElemRange &range) const
01271 {
01272   // We need data to project
01273   libmesh_assert(f.get());
01274 
01282   // The number of variables in this system
01283   const unsigned int n_variables = system.n_vars();
01284 
01285   // The dimensionality of the current mesh
01286   const unsigned int dim = system.get_mesh().mesh_dimension();
01287 
01288   // The DofMap for this system
01289   const DofMap& dof_map = system.get_dof_map();
01290 
01291   // The element matrix and RHS for projections.
01292   // Note that Ke is always real-valued, whereas
01293   // Fe may be complex valued if complex number
01294   // support is enabled
01295   DenseMatrix<Real> Ke;
01296   DenseVector<Number> Fe;
01297   // The new element coefficients
01298   DenseVector<Number> Ue;
01299 
01300 
01301   // Loop over all the variables in the system
01302   for (unsigned int var=0; var<n_variables; var++)
01303     {
01304       const Variable& variable = dof_map.variable(var);
01305 
01306       const FEType& fe_type = variable.type();
01307 
01308       if (fe_type.family == SCALAR)
01309         continue;
01310 
01311       const unsigned int var_component =
01312         system.variable_scalar_number(var, 0);
01313 
01314       // Get FE objects of the appropriate type
01315       UniquePtr<FEBase> fe (FEBase::build(dim, fe_type));
01316 
01317       // Prepare variables for projection
01318       UniquePtr<QBase> qrule     (fe_type.default_quadrature_rule(dim));
01319       UniquePtr<QBase> qedgerule (fe_type.default_quadrature_rule(1));
01320       UniquePtr<QBase> qsiderule (fe_type.default_quadrature_rule(dim-1));
01321 
01322       // The values of the shape functions at the quadrature
01323       // points
01324       const std::vector<std::vector<Real> >& phi = fe->get_phi();
01325 
01326       // The gradients of the shape functions at the quadrature
01327       // points on the child element.
01328       const std::vector<std::vector<RealGradient> > *dphi = NULL;
01329 
01330       const FEContinuity cont = fe->get_continuity();
01331 
01332       if (cont == C_ONE)
01333         {
01334           // We'll need gradient data for a C1 projection
01335           libmesh_assert(g.get());
01336 
01337           const std::vector<std::vector<RealGradient> >&
01338             ref_dphi = fe->get_dphi();
01339           dphi = &ref_dphi;
01340         }
01341 
01342       // The Jacobian * quadrature weight at the quadrature points
01343       const std::vector<Real>& JxW =
01344         fe->get_JxW();
01345 
01346       // The XYZ locations of the quadrature points
01347       const std::vector<Point>& xyz_values =
01348         fe->get_xyz();
01349 
01350       // The global DOF indices
01351       std::vector<dof_id_type> dof_indices;
01352       // Side/edge DOF indices
01353       std::vector<unsigned int> side_dofs;
01354 
01355       // Iterate over all the elements in the range
01356       for (ConstElemRange::const_iterator elem_it=range.begin(); elem_it != range.end(); ++elem_it)
01357         {
01358           const Elem* elem = *elem_it;
01359 
01360           // Per-subdomain variables don't need to be projected on
01361           // elements where they're not active
01362           if (!variable.active_on_subdomain(elem->subdomain_id()))
01363             continue;
01364 
01365           // Update the DOF indices for this element based on
01366           // the current mesh
01367           dof_map.dof_indices (elem, dof_indices, var);
01368 
01369           // The number of DOFs on the element
01370           const unsigned int n_dofs =
01371             cast_int<unsigned int>(dof_indices.size());
01372 
01373           // Fixed vs. free DoFs on edge/face projections
01374           std::vector<char> dof_is_fixed(n_dofs, false); // bools
01375           std::vector<int> free_dof(n_dofs, 0);
01376 
01377           // The element type
01378           const ElemType elem_type = elem->type();
01379 
01380           // The number of nodes on the new element
01381           const unsigned int n_nodes = elem->n_nodes();
01382 
01383           // Zero the interpolated values
01384           Ue.resize (n_dofs); Ue.zero();
01385 
01386           // In general, we need a series of
01387           // projections to ensure a unique and continuous
01388           // solution.  We start by interpolating nodes, then
01389           // hold those fixed and project edges, then
01390           // hold those fixed and project faces, then
01391           // hold those fixed and project interiors
01392 
01393           // Interpolate node values first
01394           unsigned int current_dof = 0;
01395           for (unsigned int n=0; n!= n_nodes; ++n)
01396             {
01397               // FIXME: this should go through the DofMap,
01398               // not duplicate dof_indices code badly!
01399               const unsigned int nc =
01400                 FEInterface::n_dofs_at_node (dim, fe_type, elem_type,
01401                                              n);
01402               if (!elem->is_vertex(n))
01403                 {
01404                   current_dof += nc;
01405                   continue;
01406                 }
01407               if (cont == DISCONTINUOUS)
01408                 {
01409                   libmesh_assert_equal_to (nc, 0);
01410                 }
01411               // Assume that C_ZERO elements have a single nodal
01412               // value shape function
01413               else if (cont == C_ZERO)
01414                 {
01415                   libmesh_assert_equal_to (nc, 1);
01416                   Ue(current_dof) = f->component(var_component,
01417                                                  elem->point(n),
01418                                                  system.time);
01419                   dof_is_fixed[current_dof] = true;
01420                   current_dof++;
01421                 }
01422               // The hermite element vertex shape functions are weird
01423               else if (fe_type.family == HERMITE)
01424                 {
01425                   Ue(current_dof) = f->component(var_component,
01426                                                  elem->point(n),
01427                                                  system.time);
01428                   dof_is_fixed[current_dof] = true;
01429                   current_dof++;
01430                   Gradient grad = g->component(var_component,
01431                                                elem->point(n),
01432                                                system.time);
01433                   // x derivative
01434                   Ue(current_dof) = grad(0);
01435                   dof_is_fixed[current_dof] = true;
01436                   current_dof++;
01437                   if (dim > 1)
01438                     {
01439                       // We'll finite difference mixed derivatives
01440                       Point nxminus = elem->point(n),
01441                         nxplus = elem->point(n);
01442                       nxminus(0) -= TOLERANCE;
01443                       nxplus(0) += TOLERANCE;
01444                       Gradient gxminus = g->component(var_component,
01445                                                       nxminus,
01446                                                       system.time);
01447                       Gradient gxplus = g->component(var_component,
01448                                                      nxplus,
01449                                                      system.time);
01450                       // y derivative
01451                       Ue(current_dof) = grad(1);
01452                       dof_is_fixed[current_dof] = true;
01453                       current_dof++;
01454                       // xy derivative
01455                       Ue(current_dof) = (gxplus(1) - gxminus(1))
01456                         / 2. / TOLERANCE;
01457                       dof_is_fixed[current_dof] = true;
01458                       current_dof++;
01459 
01460                       if (dim > 2)
01461                         {
01462                           // z derivative
01463                           Ue(current_dof) = grad(2);
01464                           dof_is_fixed[current_dof] = true;
01465                           current_dof++;
01466                           // xz derivative
01467                           Ue(current_dof) = (gxplus(2) - gxminus(2))
01468                             / 2. / TOLERANCE;
01469                           dof_is_fixed[current_dof] = true;
01470                           current_dof++;
01471                           // We need new points for yz
01472                           Point nyminus = elem->point(n),
01473                             nyplus = elem->point(n);
01474                           nyminus(1) -= TOLERANCE;
01475                           nyplus(1) += TOLERANCE;
01476                           Gradient gyminus = g->component(var_component,
01477                                                           nyminus,
01478                                                           system.time);
01479                           Gradient gyplus = g->component(var_component,
01480                                                          nyplus,
01481                                                          system.time);
01482                           // xz derivative
01483                           Ue(current_dof) = (gyplus(2) - gyminus(2))
01484                             / 2. / TOLERANCE;
01485                           dof_is_fixed[current_dof] = true;
01486                           current_dof++;
01487                           // Getting a 2nd order xyz is more tedious
01488                           Point nxmym = elem->point(n),
01489                             nxmyp = elem->point(n),
01490                             nxpym = elem->point(n),
01491                             nxpyp = elem->point(n);
01492                           nxmym(0) -= TOLERANCE;
01493                           nxmym(1) -= TOLERANCE;
01494                           nxmyp(0) -= TOLERANCE;
01495                           nxmyp(1) += TOLERANCE;
01496                           nxpym(0) += TOLERANCE;
01497                           nxpym(1) -= TOLERANCE;
01498                           nxpyp(0) += TOLERANCE;
01499                           nxpyp(1) += TOLERANCE;
01500                           Gradient gxmym = g->component(var_component,
01501                                                         nxmym,
01502                                                         system.time);
01503                           Gradient gxmyp = g->component(var_component,
01504                                                         nxmyp,
01505                                                         system.time);
01506                           Gradient gxpym = g->component(var_component,
01507                                                         nxpym,
01508                                                         system.time);
01509                           Gradient gxpyp = g->component(var_component,
01510                                                         nxpyp,
01511                                                         system.time);
01512                           Number gxzplus = (gxpyp(2) - gxmyp(2))
01513                             / 2. / TOLERANCE;
01514                           Number gxzminus = (gxpym(2) - gxmym(2))
01515                             / 2. / TOLERANCE;
01516                           // xyz derivative
01517                           Ue(current_dof) = (gxzplus - gxzminus)
01518                             / 2. / TOLERANCE;
01519                           dof_is_fixed[current_dof] = true;
01520                           current_dof++;
01521                         }
01522                     }
01523                 }
01524               // Assume that other C_ONE elements have a single nodal
01525               // value shape function and nodal gradient component
01526               // shape functions
01527               else if (cont == C_ONE)
01528                 {
01529                   libmesh_assert_equal_to (nc, 1 + dim);
01530                   Ue(current_dof) = f->component(var_component,
01531                                                  elem->point(n),
01532                                                  system.time);
01533                   dof_is_fixed[current_dof] = true;
01534                   current_dof++;
01535                   Gradient grad = g->component(var_component,
01536                                                elem->point(n),
01537                                                system.time);
01538                   for (unsigned int i=0; i!= dim; ++i)
01539                     {
01540                       Ue(current_dof) = grad(i);
01541                       dof_is_fixed[current_dof] = true;
01542                       current_dof++;
01543                     }
01544                 }
01545               else
01546                 libmesh_error_msg("Unknown continuity " << cont);
01547             }
01548 
01549           // In 3D, project any edge values next
01550           if (dim > 2 && cont != DISCONTINUOUS)
01551             for (unsigned int e=0; e != elem->n_edges(); ++e)
01552               {
01553                 FEInterface::dofs_on_edge(elem, dim, fe_type, e,
01554                                           side_dofs);
01555 
01556                 // Some edge dofs are on nodes and already
01557                 // fixed, others are free to calculate
01558                 unsigned int free_dofs = 0;
01559                 for (unsigned int i=0; i != side_dofs.size(); ++i)
01560                   if (!dof_is_fixed[side_dofs[i]])
01561                     free_dof[free_dofs++] = i;
01562 
01563                 // There may be nothing to project
01564                 if (!free_dofs)
01565                   continue;
01566 
01567                 Ke.resize (free_dofs, free_dofs); Ke.zero();
01568                 Fe.resize (free_dofs); Fe.zero();
01569                 // The new edge coefficients
01570                 DenseVector<Number> Uedge(free_dofs);
01571 
01572                 // Initialize FE data on the edge
01573                 fe->attach_quadrature_rule (qedgerule.get());
01574                 fe->edge_reinit (elem, e);
01575                 const unsigned int n_qp = qedgerule->n_points();
01576 
01577                 // Loop over the quadrature points
01578                 for (unsigned int qp=0; qp<n_qp; qp++)
01579                   {
01580                     // solution at the quadrature point
01581                     Number fineval = f->component(var_component,
01582                                                   xyz_values[qp],
01583                                                   system.time);
01584                     // solution grad at the quadrature point
01585                     Gradient finegrad;
01586                     if (cont == C_ONE)
01587                       finegrad = g->component(var_component,
01588                                               xyz_values[qp],
01589                                               system.time);
01590 
01591                     // Form edge projection matrix
01592                     for (unsigned int sidei=0, freei=0;
01593                          sidei != side_dofs.size(); ++sidei)
01594                       {
01595                         unsigned int i = side_dofs[sidei];
01596                         // fixed DoFs aren't test functions
01597                         if (dof_is_fixed[i])
01598                           continue;
01599                         for (unsigned int sidej=0, freej=0;
01600                              sidej != side_dofs.size(); ++sidej)
01601                           {
01602                             unsigned int j = side_dofs[sidej];
01603                             if (dof_is_fixed[j])
01604                               Fe(freei) -= phi[i][qp] * phi[j][qp] *
01605                                 JxW[qp] * Ue(j);
01606                             else
01607                               Ke(freei,freej) += phi[i][qp] *
01608                                 phi[j][qp] * JxW[qp];
01609                             if (cont == C_ONE)
01610                               {
01611                                 if (dof_is_fixed[j])
01612                                   Fe(freei) -= ((*dphi)[i][qp] *
01613                                                 (*dphi)[j][qp]) *
01614                                     JxW[qp] * Ue(j);
01615                                 else
01616                                   Ke(freei,freej) += ((*dphi)[i][qp] *
01617                                                       (*dphi)[j][qp])
01618                                     * JxW[qp];
01619                               }
01620                             if (!dof_is_fixed[j])
01621                               freej++;
01622                           }
01623                         Fe(freei) += phi[i][qp] * fineval * JxW[qp];
01624                         if (cont == C_ONE)
01625                           Fe(freei) += (finegrad * (*dphi)[i][qp]) *
01626                             JxW[qp];
01627                         freei++;
01628                       }
01629                   }
01630 
01631                 Ke.cholesky_solve(Fe, Uedge);
01632 
01633                 // Transfer new edge solutions to element
01634                 for (unsigned int i=0; i != free_dofs; ++i)
01635                   {
01636                     Number &ui = Ue(side_dofs[free_dof[i]]);
01637                     libmesh_assert(std::abs(ui) < TOLERANCE ||
01638                                    std::abs(ui - Uedge(i)) < TOLERANCE);
01639                     ui = Uedge(i);
01640                     dof_is_fixed[side_dofs[free_dof[i]]] = true;
01641                   }
01642               }
01643 
01644           // Project any side values (edges in 2D, faces in 3D)
01645           if (dim > 1 && cont != DISCONTINUOUS)
01646             for (unsigned int s=0; s != elem->n_sides(); ++s)
01647               {
01648                 FEInterface::dofs_on_side(elem, dim, fe_type, s,
01649                                           side_dofs);
01650 
01651                 // Some side dofs are on nodes/edges and already
01652                 // fixed, others are free to calculate
01653                 unsigned int free_dofs = 0;
01654                 for (unsigned int i=0; i != side_dofs.size(); ++i)
01655                   if (!dof_is_fixed[side_dofs[i]])
01656                     free_dof[free_dofs++] = i;
01657 
01658                 // There may be nothing to project
01659                 if (!free_dofs)
01660                   continue;
01661 
01662                 Ke.resize (free_dofs, free_dofs); Ke.zero();
01663                 Fe.resize (free_dofs); Fe.zero();
01664                 // The new side coefficients
01665                 DenseVector<Number> Uside(free_dofs);
01666 
01667                 // Initialize FE data on the side
01668                 fe->attach_quadrature_rule (qsiderule.get());
01669                 fe->reinit (elem, s);
01670                 const unsigned int n_qp = qsiderule->n_points();
01671 
01672                 // Loop over the quadrature points
01673                 for (unsigned int qp=0; qp<n_qp; qp++)
01674                   {
01675                     // solution at the quadrature point
01676                     Number fineval = f->component(var_component,
01677                                                   xyz_values[qp],
01678                                                   system.time);
01679                     // solution grad at the quadrature point
01680                     Gradient finegrad;
01681                     if (cont == C_ONE)
01682                       finegrad = g->component(var_component,
01683                                               xyz_values[qp],
01684                                               system.time);
01685 
01686                     // Form side projection matrix
01687                     for (unsigned int sidei=0, freei=0;
01688                          sidei != side_dofs.size(); ++sidei)
01689                       {
01690                         unsigned int i = side_dofs[sidei];
01691                         // fixed DoFs aren't test functions
01692                         if (dof_is_fixed[i])
01693                           continue;
01694                         for (unsigned int sidej=0, freej=0;
01695                              sidej != side_dofs.size(); ++sidej)
01696                           {
01697                             unsigned int j = side_dofs[sidej];
01698                             if (dof_is_fixed[j])
01699                               Fe(freei) -= phi[i][qp] * phi[j][qp] *
01700                                 JxW[qp] * Ue(j);
01701                             else
01702                               Ke(freei,freej) += phi[i][qp] *
01703                                 phi[j][qp] * JxW[qp];
01704                             if (cont == C_ONE)
01705                               {
01706                                 if (dof_is_fixed[j])
01707                                   Fe(freei) -= ((*dphi)[i][qp] *
01708                                                 (*dphi)[j][qp]) *
01709                                     JxW[qp] * Ue(j);
01710                                 else
01711                                   Ke(freei,freej) += ((*dphi)[i][qp] *
01712                                                       (*dphi)[j][qp])
01713                                     * JxW[qp];
01714                               }
01715                             if (!dof_is_fixed[j])
01716                               freej++;
01717                           }
01718                         Fe(freei) += (fineval * phi[i][qp]) * JxW[qp];
01719                         if (cont == C_ONE)
01720                           Fe(freei) += (finegrad * (*dphi)[i][qp]) *
01721                             JxW[qp];
01722                         freei++;
01723                       }
01724                   }
01725 
01726                 Ke.cholesky_solve(Fe, Uside);
01727 
01728                 // Transfer new side solutions to element
01729                 for (unsigned int i=0; i != free_dofs; ++i)
01730                   {
01731                     Number &ui = Ue(side_dofs[free_dof[i]]);
01732                     libmesh_assert(std::abs(ui) < TOLERANCE ||
01733                                    std::abs(ui - Uside(i)) < TOLERANCE);
01734                     ui = Uside(i);
01735                     dof_is_fixed[side_dofs[free_dof[i]]] = true;
01736                   }
01737               }
01738 
01739           // Project the interior values, finally
01740 
01741           // Some interior dofs are on nodes/edges/sides and
01742           // already fixed, others are free to calculate
01743           unsigned int free_dofs = 0;
01744           for (unsigned int i=0; i != n_dofs; ++i)
01745             if (!dof_is_fixed[i])
01746               free_dof[free_dofs++] = i;
01747 
01748           // There may be nothing to project
01749           if (free_dofs)
01750             {
01751 
01752               Ke.resize (free_dofs, free_dofs); Ke.zero();
01753               Fe.resize (free_dofs); Fe.zero();
01754               // The new interior coefficients
01755               DenseVector<Number> Uint(free_dofs);
01756 
01757               // Initialize FE data
01758               fe->attach_quadrature_rule (qrule.get());
01759               fe->reinit (elem);
01760               const unsigned int n_qp = qrule->n_points();
01761 
01762               // Loop over the quadrature points
01763               for (unsigned int qp=0; qp<n_qp; qp++)
01764                 {
01765                   // solution at the quadrature point
01766                   Number fineval = f->component(var_component,
01767                                                 xyz_values[qp],
01768                                                 system.time);
01769                   // solution grad at the quadrature point
01770                   Gradient finegrad;
01771                   if (cont == C_ONE)
01772                     finegrad = g->component(var_component,
01773                                             xyz_values[qp],
01774                                             system.time);
01775 
01776                   // Form interior projection matrix
01777                   for (unsigned int i=0, freei=0; i != n_dofs; ++i)
01778                     {
01779                       // fixed DoFs aren't test functions
01780                       if (dof_is_fixed[i])
01781                         continue;
01782                       for (unsigned int j=0, freej=0; j != n_dofs; ++j)
01783                         {
01784                           if (dof_is_fixed[j])
01785                             Fe(freei) -= phi[i][qp] * phi[j][qp] * JxW[qp]
01786                               * Ue(j);
01787                           else
01788                             Ke(freei,freej) += phi[i][qp] * phi[j][qp] *
01789                               JxW[qp];
01790                           if (cont == C_ONE)
01791                             {
01792                               if (dof_is_fixed[j])
01793                                 Fe(freei) -= ((*dphi)[i][qp] *
01794                                               (*dphi)[j][qp]) * JxW[qp] *
01795                                   Ue(j);
01796                               else
01797                                 Ke(freei,freej) += ((*dphi)[i][qp] *
01798                                                     (*dphi)[j][qp]) *
01799                                   JxW[qp];
01800                             }
01801                           if (!dof_is_fixed[j])
01802                             freej++;
01803                         }
01804                       Fe(freei) += phi[i][qp] * fineval * JxW[qp];
01805                       if (cont == C_ONE)
01806                         Fe(freei) += (finegrad * (*dphi)[i][qp]) * JxW[qp];
01807                       freei++;
01808                     }
01809                 }
01810               Ke.cholesky_solve(Fe, Uint);
01811 
01812               // Transfer new interior solutions to element
01813               for (unsigned int i=0; i != free_dofs; ++i)
01814                 {
01815                   Number &ui = Ue(free_dof[i]);
01816                   libmesh_assert(std::abs(ui) < TOLERANCE ||
01817                                  std::abs(ui - Uint(i)) < TOLERANCE);
01818                   ui = Uint(i);
01819                   dof_is_fixed[free_dof[i]] = true;
01820                 }
01821 
01822             } // if there are free interior dofs
01823 
01824           // Make sure every DoF got reached!
01825           for (unsigned int i=0; i != n_dofs; ++i)
01826             libmesh_assert(dof_is_fixed[i]);
01827 
01828           const dof_id_type
01829             first = new_vector.first_local_index(),
01830             last  = new_vector.last_local_index();
01831 
01832           // Lock the new_vector since it is shared among threads.
01833           {
01834             Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
01835 
01836             for (unsigned int i = 0; i < n_dofs; i++)
01837               // We may be projecting a new zero value onto
01838               // an old nonzero approximation - RHS
01839               // if (Ue(i) != 0.)
01840               if ((dof_indices[i] >= first) &&
01841                   (dof_indices[i] <  last))
01842                 {
01843                   new_vector.set(dof_indices[i], Ue(i));
01844                 }
01845           }
01846         }  // end elem loop
01847     } // end variables loop
01848 }
01849 
01850 
01851 void ProjectFEMSolution::operator()(const ConstElemRange &range) const
01852 {
01853   // We need data to project
01854   libmesh_assert(f.get());
01855 
01863   FEMContext context( system );
01864 
01865   // The number of variables in this system
01866   const unsigned int n_variables = context.n_vars();
01867 
01868   // The dimensionality of the current mesh
01869   const unsigned int dim = context.get_dim();
01870 
01871   // The DofMap for this system
01872   const DofMap& dof_map = system.get_dof_map();
01873 
01874   // The element matrix and RHS for projections.
01875   // Note that Ke is always real-valued, whereas
01876   // Fe may be complex valued if complex number
01877   // support is enabled
01878   DenseMatrix<Real> Ke;
01879   DenseVector<Number> Fe;
01880   // The new element coefficients
01881   DenseVector<Number> Ue;
01882 
01883   // FIXME: Need to generalize this to vector-valued elements. [PB]
01884   FEBase* fe = NULL;
01885   FEBase* side_fe = NULL;
01886   FEBase* edge_fe = NULL;
01887 
01888   // First, loop over all variables and make sure the shape functions phi will
01889   // get built as well as the shape function gradients if the gradient functor
01890   // is supplied.
01891   for (unsigned int var=0; var<n_variables; var++)
01892     {
01893       context.get_element_fe( var, fe );
01894       if (fe->get_fe_type().family == SCALAR)
01895         continue;
01896       if( dim > 1 )
01897         context.get_side_fe( var, side_fe );
01898       if( dim > 2 )
01899         context.get_edge_fe( var, edge_fe );
01900 
01901       fe->get_phi();
01902       if( dim > 1 )
01903         side_fe->get_phi();
01904       if( dim > 2 )
01905         edge_fe->get_phi();
01906 
01907       const FEContinuity cont = fe->get_continuity();
01908       if(cont == C_ONE)
01909         {
01910           libmesh_assert(g.get());
01911           fe->get_dphi();
01912           side_fe->get_dphi();
01913           if( dim > 2 )
01914             edge_fe->get_dphi();
01915         }
01916     }
01917 
01918   // Now initialize any user requested shape functions
01919   f->init_context(context);
01920   if(g.get())
01921     g->init_context(context);
01922 
01923   std::vector<unsigned int> side_dofs;
01924 
01925   // Iterate over all the elements in the range
01926   for (ConstElemRange::const_iterator elem_it=range.begin(); elem_it != range.end(); ++elem_it)
01927     {
01928       const Elem* elem = *elem_it;
01929 
01930       context.pre_fe_reinit(system, elem);
01931 
01932       // Loop over all the variables in the system
01933       for (unsigned int var=0; var<n_variables; var++)
01934         {
01935           const Variable& variable = dof_map.variable(var);
01936 
01937           const FEType& fe_type = variable.type();
01938 
01939           if (fe_type.family == SCALAR)
01940             continue;
01941 
01942           // Per-subdomain variables don't need to be projected on
01943           // elements where they're not active
01944           if (!variable.active_on_subdomain(elem->subdomain_id()))
01945             continue;
01946 
01947           const FEContinuity cont = fe->get_continuity();
01948 
01949           const unsigned int var_component =
01950             system.variable_scalar_number(var, 0);
01951 
01952           const std::vector<dof_id_type>& dof_indices =
01953             context.get_dof_indices(var);
01954 
01955           // The number of DOFs on the element
01956           const unsigned int n_dofs =
01957             cast_int<unsigned int>(dof_indices.size());
01958 
01959           // Fixed vs. free DoFs on edge/face projections
01960           std::vector<char> dof_is_fixed(n_dofs, false); // bools
01961           std::vector<int> free_dof(n_dofs, 0);
01962 
01963           // The element type
01964           const ElemType elem_type = elem->type();
01965 
01966           // The number of nodes on the new element
01967           const unsigned int n_nodes = elem->n_nodes();
01968 
01969           // Zero the interpolated values
01970           Ue.resize (n_dofs); Ue.zero();
01971 
01972           // In general, we need a series of
01973           // projections to ensure a unique and continuous
01974           // solution.  We start by interpolating nodes, then
01975           // hold those fixed and project edges, then
01976           // hold those fixed and project faces, then
01977           // hold those fixed and project interiors
01978 
01979           // Interpolate node values first
01980           unsigned int current_dof = 0;
01981           for (unsigned int n=0; n!= n_nodes; ++n)
01982             {
01983               // FIXME: this should go through the DofMap,
01984               // not duplicate dof_indices code badly!
01985               const unsigned int nc =
01986                 FEInterface::n_dofs_at_node (dim, fe_type, elem_type,
01987                                              n);
01988               if (!elem->is_vertex(n))
01989                 {
01990                   current_dof += nc;
01991                   continue;
01992                 }
01993               if (cont == DISCONTINUOUS)
01994                 {
01995                   libmesh_assert_equal_to (nc, 0);
01996                 }
01997               // Assume that C_ZERO elements have a single nodal
01998               // value shape function
01999               else if (cont == C_ZERO)
02000                 {
02001                   libmesh_assert_equal_to (nc, 1);
02002                   Ue(current_dof) = f->component(context,
02003                                                  var_component,
02004                                                  elem->point(n),
02005                                                  system.time);
02006                   dof_is_fixed[current_dof] = true;
02007                   current_dof++;
02008                 }
02009               // The hermite element vertex shape functions are weird
02010               else if (fe_type.family == HERMITE)
02011                 {
02012                   Ue(current_dof) = f->component(context,
02013                                                  var_component,
02014                                                  elem->point(n),
02015                                                  system.time);
02016                   dof_is_fixed[current_dof] = true;
02017                   current_dof++;
02018                   Gradient grad = g->component(context,
02019                                                var_component,
02020                                                elem->point(n),
02021                                                system.time);
02022                   // x derivative
02023                   Ue(current_dof) = grad(0);
02024                   dof_is_fixed[current_dof] = true;
02025                   current_dof++;
02026                   if (dim > 1)
02027                     {
02028                       // We'll finite difference mixed derivatives
02029                       Point nxminus = elem->point(n),
02030                         nxplus = elem->point(n);
02031                       nxminus(0) -= TOLERANCE;
02032                       nxplus(0) += TOLERANCE;
02033                       Gradient gxminus = g->component(context,
02034                                                       var_component,
02035                                                       nxminus,
02036                                                       system.time);
02037                       Gradient gxplus = g->component(context,
02038                                                      var_component,
02039                                                      nxplus,
02040                                                      system.time);
02041                       // y derivative
02042                       Ue(current_dof) = grad(1);
02043                       dof_is_fixed[current_dof] = true;
02044                       current_dof++;
02045                       // xy derivative
02046                       Ue(current_dof) = (gxplus(1) - gxminus(1))
02047                         / 2. / TOLERANCE;
02048                       dof_is_fixed[current_dof] = true;
02049                       current_dof++;
02050 
02051                       if (dim > 2)
02052                         {
02053                           // z derivative
02054                           Ue(current_dof) = grad(2);
02055                           dof_is_fixed[current_dof] = true;
02056                           current_dof++;
02057                           // xz derivative
02058                           Ue(current_dof) = (gxplus(2) - gxminus(2))
02059                             / 2. / TOLERANCE;
02060                           dof_is_fixed[current_dof] = true;
02061                           current_dof++;
02062                           // We need new points for yz
02063                           Point nyminus = elem->point(n),
02064                             nyplus = elem->point(n);
02065                           nyminus(1) -= TOLERANCE;
02066                           nyplus(1) += TOLERANCE;
02067                           Gradient gyminus = g->component(context,
02068                                                           var_component,
02069                                                           nyminus,
02070                                                           system.time);
02071                           Gradient gyplus = g->component(context,
02072                                                          var_component,
02073                                                          nyplus,
02074                                                          system.time);
02075                           // xz derivative
02076                           Ue(current_dof) = (gyplus(2) - gyminus(2))
02077                             / 2. / TOLERANCE;
02078                           dof_is_fixed[current_dof] = true;
02079                           current_dof++;
02080                           // Getting a 2nd order xyz is more tedious
02081                           Point nxmym = elem->point(n),
02082                             nxmyp = elem->point(n),
02083                             nxpym = elem->point(n),
02084                             nxpyp = elem->point(n);
02085                           nxmym(0) -= TOLERANCE;
02086                           nxmym(1) -= TOLERANCE;
02087                           nxmyp(0) -= TOLERANCE;
02088                           nxmyp(1) += TOLERANCE;
02089                           nxpym(0) += TOLERANCE;
02090                           nxpym(1) -= TOLERANCE;
02091                           nxpyp(0) += TOLERANCE;
02092                           nxpyp(1) += TOLERANCE;
02093                           Gradient gxmym = g->component(context,
02094                                                         var_component,
02095                                                         nxmym,
02096                                                         system.time);
02097                           Gradient gxmyp = g->component(context,
02098                                                         var_component,
02099                                                         nxmyp,
02100                                                         system.time);
02101                           Gradient gxpym = g->component(context,
02102                                                         var_component,
02103                                                         nxpym,
02104                                                         system.time);
02105                           Gradient gxpyp = g->component(context,
02106                                                         var_component,
02107                                                         nxpyp,
02108                                                         system.time);
02109                           Number gxzplus = (gxpyp(2) - gxmyp(2))
02110                             / 2. / TOLERANCE;
02111                           Number gxzminus = (gxpym(2) - gxmym(2))
02112                             / 2. / TOLERANCE;
02113                           // xyz derivative
02114                           Ue(current_dof) = (gxzplus - gxzminus)
02115                             / 2. / TOLERANCE;
02116                           dof_is_fixed[current_dof] = true;
02117                           current_dof++;
02118                         }
02119                     }
02120                 }
02121               // Assume that other C_ONE elements have a single nodal
02122               // value shape function and nodal gradient component
02123               // shape functions
02124               else if (cont == C_ONE)
02125                 {
02126                   libmesh_assert_equal_to (nc, 1 + dim);
02127                   Ue(current_dof) = f->component(context,
02128                                                  var_component,
02129                                                  elem->point(n),
02130                                                  system.time);
02131                   dof_is_fixed[current_dof] = true;
02132                   current_dof++;
02133                   Gradient grad = g->component(context,
02134                                                var_component,
02135                                                elem->point(n),
02136                                                system.time);
02137                   for (unsigned int i=0; i!= dim; ++i)
02138                     {
02139                       Ue(current_dof) = grad(i);
02140                       dof_is_fixed[current_dof] = true;
02141                       current_dof++;
02142                     }
02143                 }
02144               else
02145                 libmesh_error_msg("Unknown continuity " << cont);
02146             }
02147 
02148           // In 3D, project any edge values next
02149           if (dim > 2 && cont != DISCONTINUOUS)
02150             {
02151               const std::vector<Point>& xyz_values = edge_fe->get_xyz();
02152               const std::vector<Real>& JxW = edge_fe->get_JxW();
02153 
02154               const std::vector<std::vector<Real> >& phi = edge_fe->get_phi();
02155               const std::vector<std::vector<RealGradient> >* dphi = NULL;
02156               if (cont == C_ONE)
02157                 dphi = &(edge_fe->get_dphi());
02158 
02159               for (unsigned char e=0; e != elem->n_edges(); ++e)
02160                 {
02161                   context.edge = e;
02162                   context.edge_fe_reinit();
02163 
02164                   const QBase& qedgerule = context.get_edge_qrule();
02165                   const unsigned int n_qp = qedgerule.n_points();
02166 
02167                   FEInterface::dofs_on_edge(elem, dim, fe_type, e,
02168                                             side_dofs);
02169 
02170                   // Some edge dofs are on nodes and already
02171                   // fixed, others are free to calculate
02172                   unsigned int free_dofs = 0;
02173                   for (unsigned int i=0; i != side_dofs.size(); ++i)
02174                     if (!dof_is_fixed[side_dofs[i]])
02175                       free_dof[free_dofs++] = i;
02176 
02177                   // There may be nothing to project
02178                   if (!free_dofs)
02179                     continue;
02180 
02181                   Ke.resize (free_dofs, free_dofs); Ke.zero();
02182                   Fe.resize (free_dofs); Fe.zero();
02183                   // The new edge coefficients
02184                   DenseVector<Number> Uedge(free_dofs);
02185 
02186                   // Loop over the quadrature points
02187                   for (unsigned int qp=0; qp<n_qp; qp++)
02188                     {
02189                       // solution at the quadrature point
02190                       Number fineval = f->component(context,
02191                                                     var_component,
02192                                                     xyz_values[qp],
02193                                                     system.time);
02194                       // solution grad at the quadrature point
02195                       Gradient finegrad;
02196                       if (cont == C_ONE)
02197                         finegrad = g->component(context,
02198                                                 var_component,
02199                                                 xyz_values[qp],
02200                                                 system.time);
02201 
02202                       // Form edge projection matrix
02203                       for (unsigned int sidei=0, freei=0;
02204                            sidei != side_dofs.size(); ++sidei)
02205                         {
02206                           unsigned int i = side_dofs[sidei];
02207                           // fixed DoFs aren't test functions
02208                           if (dof_is_fixed[i])
02209                             continue;
02210                           for (unsigned int sidej=0, freej=0;
02211                                sidej != side_dofs.size(); ++sidej)
02212                             {
02213                               unsigned int j = side_dofs[sidej];
02214                               if (dof_is_fixed[j])
02215                                 Fe(freei) -= phi[i][qp] * phi[j][qp] *
02216                                   JxW[qp] * Ue(j);
02217                               else
02218                                 Ke(freei,freej) += phi[i][qp] *
02219                                   phi[j][qp] * JxW[qp];
02220                               if (cont == C_ONE)
02221                                 {
02222                                   if (dof_is_fixed[j])
02223                                     Fe(freei) -= ( (*dphi)[i][qp] *
02224                                                    (*dphi)[j][qp] ) *
02225                                       JxW[qp] * Ue(j);
02226                                   else
02227                                     Ke(freei,freej) += ( (*dphi)[i][qp] *
02228                                                          (*dphi)[j][qp] )
02229                                       * JxW[qp];
02230                                 }
02231                               if (!dof_is_fixed[j])
02232                                 freej++;
02233                             }
02234                           Fe(freei) += phi[i][qp] * fineval * JxW[qp];
02235                           if (cont == C_ONE)
02236                             Fe(freei) += (finegrad * (*dphi)[i][qp] ) *
02237                               JxW[qp];
02238                           freei++;
02239                         }
02240                     }
02241 
02242                   Ke.cholesky_solve(Fe, Uedge);
02243 
02244                   // Transfer new edge solutions to element
02245                   for (unsigned int i=0; i != free_dofs; ++i)
02246                     {
02247                       Number &ui = Ue(side_dofs[free_dof[i]]);
02248                       libmesh_assert(std::abs(ui) < TOLERANCE ||
02249                                      std::abs(ui - Uedge(i)) < TOLERANCE);
02250                       ui = Uedge(i);
02251                       dof_is_fixed[side_dofs[free_dof[i]]] = true;
02252                     }
02253                 }
02254             } // end if (dim > 2 && cont != DISCONTINUOUS)
02255 
02256           // Project any side values (edges in 2D, faces in 3D)
02257           if (dim > 1 && cont != DISCONTINUOUS)
02258             {
02259               const std::vector<Point>& xyz_values = side_fe->get_xyz();
02260               const std::vector<Real>& JxW = side_fe->get_JxW();
02261 
02262               const std::vector<std::vector<Real> >& phi = side_fe->get_phi();
02263               const std::vector<std::vector<RealGradient> >* dphi = NULL;
02264               if (cont == C_ONE)
02265                 dphi = &(side_fe->get_dphi());
02266 
02267               for (unsigned char s=0; s != elem->n_sides(); ++s)
02268                 {
02269                   FEInterface::dofs_on_side(elem, dim, fe_type, s,
02270                                             side_dofs);
02271 
02272                   // Some side dofs are on nodes/edges and already
02273                   // fixed, others are free to calculate
02274                   unsigned int free_dofs = 0;
02275                   for (unsigned int i=0; i != side_dofs.size(); ++i)
02276                     if (!dof_is_fixed[side_dofs[i]])
02277                       free_dof[free_dofs++] = i;
02278 
02279                   // There may be nothing to project
02280                   if (!free_dofs)
02281                     continue;
02282 
02283                   Ke.resize (free_dofs, free_dofs); Ke.zero();
02284                   Fe.resize (free_dofs); Fe.zero();
02285                   // The new side coefficients
02286                   DenseVector<Number> Uside(free_dofs);
02287 
02288                   context.side = s;
02289                   context.side_fe_reinit();
02290 
02291                   const QBase& qsiderule = context.get_side_qrule();
02292                   const unsigned int n_qp = qsiderule.n_points();
02293 
02294                   // Loop over the quadrature points
02295                   for (unsigned int qp=0; qp<n_qp; qp++)
02296                     {
02297                       // solution at the quadrature point
02298                       Number fineval = f->component(context,
02299                                                     var_component,
02300                                                     xyz_values[qp],
02301                                                     system.time);
02302                       // solution grad at the quadrature point
02303                       Gradient finegrad;
02304                       if (cont == C_ONE)
02305                         finegrad = g->component(context,
02306                                                 var_component,
02307                                                 xyz_values[qp],
02308                                                 system.time);
02309 
02310                       // Form side projection matrix
02311                       for (unsigned int sidei=0, freei=0;
02312                            sidei != side_dofs.size(); ++sidei)
02313                         {
02314                           unsigned int i = side_dofs[sidei];
02315                           // fixed DoFs aren't test functions
02316                           if (dof_is_fixed[i])
02317                             continue;
02318                           for (unsigned int sidej=0, freej=0;
02319                                sidej != side_dofs.size(); ++sidej)
02320                             {
02321                               unsigned int j = side_dofs[sidej];
02322                               if (dof_is_fixed[j])
02323                                 Fe(freei) -= phi[i][qp] * phi[j][qp] *
02324                                   JxW[qp] * Ue(j);
02325                               else
02326                                 Ke(freei,freej) += phi[i][qp] *
02327                                   phi[j][qp] * JxW[qp];
02328                               if (cont == C_ONE)
02329                                 {
02330                                   if (dof_is_fixed[j])
02331                                     Fe(freei) -= ( (*dphi)[i][qp] *
02332                                                    (*dphi)[j][qp] ) *
02333                                       JxW[qp] * Ue(j);
02334                                   else
02335                                     Ke(freei,freej) += ( (*dphi)[i][qp] *
02336                                                          (*dphi)[j][qp] )
02337                                       * JxW[qp];
02338                                 }
02339                               if (!dof_is_fixed[j])
02340                                 freej++;
02341                             }
02342                           Fe(freei) += (fineval * phi[i][qp]) * JxW[qp];
02343                           if (cont == C_ONE)
02344                             Fe(freei) += (finegrad * (*dphi)[i][qp] ) *
02345                               JxW[qp];
02346                           freei++;
02347                         }
02348                     }
02349 
02350                   Ke.cholesky_solve(Fe, Uside);
02351 
02352                   // Transfer new side solutions to element
02353                   for (unsigned int i=0; i != free_dofs; ++i)
02354                     {
02355                       Number &ui = Ue(side_dofs[free_dof[i]]);
02356                       libmesh_assert(std::abs(ui) < TOLERANCE ||
02357                                      std::abs(ui - Uside(i)) < TOLERANCE);
02358                       ui = Uside(i);
02359                       dof_is_fixed[side_dofs[free_dof[i]]] = true;
02360                     }
02361                 }
02362             }// end if (dim > 1 && cont != DISCONTINUOUS)
02363 
02364           // Project the interior values, finally
02365 
02366           // Some interior dofs are on nodes/edges/sides and
02367           // already fixed, others are free to calculate
02368           unsigned int free_dofs = 0;
02369           for (unsigned int i=0; i != n_dofs; ++i)
02370             if (!dof_is_fixed[i])
02371               free_dof[free_dofs++] = i;
02372 
02373           // There may be nothing to project
02374           if (free_dofs)
02375             {
02376               context.elem_fe_reinit();
02377 
02378               const QBase& qrule = context.get_element_qrule();
02379               const unsigned int n_qp = qrule.n_points();
02380               const std::vector<Point>& xyz_values = fe->get_xyz();
02381               const std::vector<Real>& JxW = fe->get_JxW();
02382 
02383               const std::vector<std::vector<Real> >& phi = fe->get_phi();
02384               const std::vector<std::vector<RealGradient> >* dphi = NULL;
02385               if (cont == C_ONE)
02386                 dphi = &(side_fe->get_dphi());
02387 
02388               Ke.resize (free_dofs, free_dofs); Ke.zero();
02389               Fe.resize (free_dofs); Fe.zero();
02390               // The new interior coefficients
02391               DenseVector<Number> Uint(free_dofs);
02392 
02393               // Loop over the quadrature points
02394               for (unsigned int qp=0; qp<n_qp; qp++)
02395                 {
02396                   // solution at the quadrature point
02397                   Number fineval = f->component(context,
02398                                                 var_component,
02399                                                 xyz_values[qp],
02400                                                 system.time);
02401                   // solution grad at the quadrature point
02402                   Gradient finegrad;
02403                   if (cont == C_ONE)
02404                     finegrad = g->component(context,
02405                                             var_component,
02406                                             xyz_values[qp],
02407                                             system.time);
02408 
02409                   // Form interior projection matrix
02410                   for (unsigned int i=0, freei=0; i != n_dofs; ++i)
02411                     {
02412                       // fixed DoFs aren't test functions
02413                       if (dof_is_fixed[i])
02414                         continue;
02415                       for (unsigned int j=0, freej=0; j != n_dofs; ++j)
02416                         {
02417                           if (dof_is_fixed[j])
02418                             Fe(freei) -= phi[i][qp] * phi[j][qp] * JxW[qp]
02419                               * Ue(j);
02420                           else
02421                             Ke(freei,freej) += phi[i][qp] * phi[j][qp] *
02422                               JxW[qp];
02423                           if (cont == C_ONE)
02424                             {
02425                               if (dof_is_fixed[j])
02426                                 Fe(freei) -= ( (*dphi)[i][qp] *
02427                                                (*dphi)[j][qp] ) * JxW[qp] *
02428                                   Ue(j);
02429                               else
02430                                 Ke(freei,freej) += ( (*dphi)[i][qp] *
02431                                                      (*dphi)[j][qp] ) *
02432                                   JxW[qp];
02433                             }
02434                           if (!dof_is_fixed[j])
02435                             freej++;
02436                         }
02437                       Fe(freei) += phi[i][qp] * fineval * JxW[qp];
02438                       if (cont == C_ONE)
02439                         Fe(freei) += (finegrad * (*dphi)[i][qp] ) * JxW[qp];
02440                       freei++;
02441                     }
02442                 }
02443               Ke.cholesky_solve(Fe, Uint);
02444 
02445               // Transfer new interior solutions to element
02446               for (unsigned int i=0; i != free_dofs; ++i)
02447                 {
02448                   Number &ui = Ue(free_dof[i]);
02449                   libmesh_assert(std::abs(ui) < TOLERANCE ||
02450                                  std::abs(ui - Uint(i)) < TOLERANCE);
02451                   ui = Uint(i);
02452                   dof_is_fixed[free_dof[i]] = true;
02453                 }
02454 
02455             } // if there are free interior dofs
02456 
02457           // Make sure every DoF got reached!
02458           for (unsigned int i=0; i != n_dofs; ++i)
02459             libmesh_assert(dof_is_fixed[i]);
02460 
02461           const numeric_index_type
02462             first = new_vector.first_local_index(),
02463             last  = new_vector.last_local_index();
02464 
02465           // Lock the new_vector since it is shared among threads.
02466           {
02467             Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
02468 
02469             for (unsigned int i = 0; i < n_dofs; i++)
02470               // We may be projecting a new zero value onto
02471               // an old nonzero approximation - RHS
02472               // if (Ue(i) != 0.)
02473               if ((dof_indices[i] >= first) &&
02474                   (dof_indices[i] <  last))
02475                 {
02476                   new_vector.set(dof_indices[i], Ue(i));
02477                 }
02478           }
02479         }  // end variables loop
02480     } // end elem loop
02481 }
02482 
02483 
02484 
02485 void BoundaryProjectSolution::operator()(const ConstElemRange &range) const
02486 {
02487   // We need data to project
02488   libmesh_assert(f.get());
02489 
02497   // The dimensionality of the current mesh
02498   const unsigned int dim = system.get_mesh().mesh_dimension();
02499 
02500   // The DofMap for this system
02501   const DofMap& dof_map = system.get_dof_map();
02502 
02503   // Boundary info for the current mesh
02504   const BoundaryInfo& boundary_info =
02505     system.get_mesh().get_boundary_info();
02506 
02507   // The element matrix and RHS for projections.
02508   // Note that Ke is always real-valued, whereas
02509   // Fe may be complex valued if complex number
02510   // support is enabled
02511   DenseMatrix<Real> Ke;
02512   DenseVector<Number> Fe;
02513   // The new element coefficients
02514   DenseVector<Number> Ue;
02515 
02516 
02517   // Loop over all the variables we've been requested to project
02518   for (unsigned int v=0; v!=variables.size(); v++)
02519     {
02520       const unsigned int var = variables[v];
02521 
02522       const Variable& variable = dof_map.variable(var);
02523 
02524       const FEType& fe_type = variable.type();
02525 
02526       if (fe_type.family == SCALAR)
02527         continue;
02528 
02529       const unsigned int var_component =
02530         system.variable_scalar_number(var, 0);
02531 
02532       // Get FE objects of the appropriate type
02533       UniquePtr<FEBase> fe (FEBase::build(dim, fe_type));
02534 
02535       // Prepare variables for projection
02536       UniquePtr<QBase> qedgerule (fe_type.default_quadrature_rule(1));
02537       UniquePtr<QBase> qsiderule (fe_type.default_quadrature_rule(dim-1));
02538 
02539       // The values of the shape functions at the quadrature
02540       // points
02541       const std::vector<std::vector<Real> >& phi = fe->get_phi();
02542 
02543       // The gradients of the shape functions at the quadrature
02544       // points on the child element.
02545       const std::vector<std::vector<RealGradient> > *dphi = NULL;
02546 
02547       const FEContinuity cont = fe->get_continuity();
02548 
02549       if (cont == C_ONE)
02550         {
02551           // We'll need gradient data for a C1 projection
02552           libmesh_assert(g.get());
02553 
02554           const std::vector<std::vector<RealGradient> >&
02555             ref_dphi = fe->get_dphi();
02556           dphi = &ref_dphi;
02557         }
02558 
02559       // The Jacobian * quadrature weight at the quadrature points
02560       const std::vector<Real>& JxW =
02561         fe->get_JxW();
02562 
02563       // The XYZ locations of the quadrature points
02564       const std::vector<Point>& xyz_values =
02565         fe->get_xyz();
02566 
02567       // The global DOF indices
02568       std::vector<dof_id_type> dof_indices;
02569       // Side/edge DOF indices
02570       std::vector<unsigned int> side_dofs;
02571 
02572       // Iterate over all the elements in the range
02573       for (ConstElemRange::const_iterator elem_it=range.begin(); elem_it != range.end(); ++elem_it)
02574         {
02575           const Elem* elem = *elem_it;
02576 
02577           // Per-subdomain variables don't need to be projected on
02578           // elements where they're not active
02579           if (!variable.active_on_subdomain(elem->subdomain_id()))
02580             continue;
02581 
02582           // Find out which nodes, edges and sides are on a requested
02583           // boundary:
02584           std::vector<bool> is_boundary_node(elem->n_nodes(), false),
02585             is_boundary_edge(elem->n_edges(), false),
02586             is_boundary_side(elem->n_sides(), false);
02587           for (unsigned char s=0; s != elem->n_sides(); ++s)
02588             {
02589               // First see if this side has been requested
02590               const std::vector<boundary_id_type>& bc_ids =
02591                 boundary_info.boundary_ids (elem, s);
02592               bool do_this_side = false;
02593               for (unsigned int i=0; i != bc_ids.size(); ++i)
02594                 if (b.count(bc_ids[i]))
02595                   {
02596                     do_this_side = true;
02597                     break;
02598                   }
02599               if (!do_this_side)
02600                 continue;
02601 
02602               is_boundary_side[s] = true;
02603 
02604               // Then see what nodes and what edges are on it
02605               for (unsigned int n=0; n != elem->n_nodes(); ++n)
02606                 if (elem->is_node_on_side(n,s))
02607                   is_boundary_node[n] = true;
02608               for (unsigned int e=0; e != elem->n_edges(); ++e)
02609                 if (elem->is_edge_on_side(e,s))
02610                   is_boundary_edge[e] = true;
02611             }
02612 
02613           // Update the DOF indices for this element based on
02614           // the current mesh
02615           dof_map.dof_indices (elem, dof_indices, var);
02616 
02617           // The number of DOFs on the element
02618           const unsigned int n_dofs =
02619             cast_int<unsigned int>(dof_indices.size());
02620 
02621           // Fixed vs. free DoFs on edge/face projections
02622           std::vector<char> dof_is_fixed(n_dofs, false); // bools
02623           std::vector<int> free_dof(n_dofs, 0);
02624 
02625           // The element type
02626           const ElemType elem_type = elem->type();
02627 
02628           // The number of nodes on the new element
02629           const unsigned int n_nodes = elem->n_nodes();
02630 
02631           // Zero the interpolated values
02632           Ue.resize (n_dofs); Ue.zero();
02633 
02634           // In general, we need a series of
02635           // projections to ensure a unique and continuous
02636           // solution.  We start by interpolating boundary nodes, then
02637           // hold those fixed and project boundary edges, then hold
02638           // those fixed and project boundary faces,
02639 
02640           // Interpolate node values first
02641           unsigned int current_dof = 0;
02642           for (unsigned int n=0; n!= n_nodes; ++n)
02643             {
02644               // FIXME: this should go through the DofMap,
02645               // not duplicate dof_indices code badly!
02646               const unsigned int nc =
02647                 FEInterface::n_dofs_at_node (dim, fe_type, elem_type,
02648                                              n);
02649               if (!elem->is_vertex(n) || !is_boundary_node[n])
02650                 {
02651                   current_dof += nc;
02652                   continue;
02653                 }
02654               if (cont == DISCONTINUOUS)
02655                 {
02656                   libmesh_assert_equal_to (nc, 0);
02657                 }
02658               // Assume that C_ZERO elements have a single nodal
02659               // value shape function
02660               else if (cont == C_ZERO)
02661                 {
02662                   libmesh_assert_equal_to (nc, 1);
02663                   Ue(current_dof) = f->component(var_component,
02664                                                  elem->point(n),
02665                                                  system.time);
02666                   dof_is_fixed[current_dof] = true;
02667                   current_dof++;
02668                 }
02669               // The hermite element vertex shape functions are weird
02670               else if (fe_type.family == HERMITE)
02671                 {
02672                   Ue(current_dof) = f->component(var_component,
02673                                                  elem->point(n),
02674                                                  system.time);
02675                   dof_is_fixed[current_dof] = true;
02676                   current_dof++;
02677                   Gradient grad = g->component(var_component,
02678                                                elem->point(n),
02679                                                system.time);
02680                   // x derivative
02681                   Ue(current_dof) = grad(0);
02682                   dof_is_fixed[current_dof] = true;
02683                   current_dof++;
02684                   if (dim > 1)
02685                     {
02686                       // We'll finite difference mixed derivatives
02687                       Point nxminus = elem->point(n),
02688                         nxplus = elem->point(n);
02689                       nxminus(0) -= TOLERANCE;
02690                       nxplus(0) += TOLERANCE;
02691                       Gradient gxminus = g->component(var_component,
02692                                                       nxminus,
02693                                                       system.time);
02694                       Gradient gxplus = g->component(var_component,
02695                                                      nxplus,
02696                                                      system.time);
02697                       // y derivative
02698                       Ue(current_dof) = grad(1);
02699                       dof_is_fixed[current_dof] = true;
02700                       current_dof++;
02701                       // xy derivative
02702                       Ue(current_dof) = (gxplus(1) - gxminus(1))
02703                         / 2. / TOLERANCE;
02704                       dof_is_fixed[current_dof] = true;
02705                       current_dof++;
02706 
02707                       if (dim > 2)
02708                         {
02709                           // z derivative
02710                           Ue(current_dof) = grad(2);
02711                           dof_is_fixed[current_dof] = true;
02712                           current_dof++;
02713                           // xz derivative
02714                           Ue(current_dof) = (gxplus(2) - gxminus(2))
02715                             / 2. / TOLERANCE;
02716                           dof_is_fixed[current_dof] = true;
02717                           current_dof++;
02718                           // We need new points for yz
02719                           Point nyminus = elem->point(n),
02720                             nyplus = elem->point(n);
02721                           nyminus(1) -= TOLERANCE;
02722                           nyplus(1) += TOLERANCE;
02723                           Gradient gyminus = g->component(var_component,
02724                                                           nyminus,
02725                                                           system.time);
02726                           Gradient gyplus = g->component(var_component,
02727                                                          nyplus,
02728                                                          system.time);
02729                           // xz derivative
02730                           Ue(current_dof) = (gyplus(2) - gyminus(2))
02731                             / 2. / TOLERANCE;
02732                           dof_is_fixed[current_dof] = true;
02733                           current_dof++;
02734                           // Getting a 2nd order xyz is more tedious
02735                           Point nxmym = elem->point(n),
02736                             nxmyp = elem->point(n),
02737                             nxpym = elem->point(n),
02738                             nxpyp = elem->point(n);
02739                           nxmym(0) -= TOLERANCE;
02740                           nxmym(1) -= TOLERANCE;
02741                           nxmyp(0) -= TOLERANCE;
02742                           nxmyp(1) += TOLERANCE;
02743                           nxpym(0) += TOLERANCE;
02744                           nxpym(1) -= TOLERANCE;
02745                           nxpyp(0) += TOLERANCE;
02746                           nxpyp(1) += TOLERANCE;
02747                           Gradient gxmym = g->component(var_component,
02748                                                         nxmym,
02749                                                         system.time);
02750                           Gradient gxmyp = g->component(var_component,
02751                                                         nxmyp,
02752                                                         system.time);
02753                           Gradient gxpym = g->component(var_component,
02754                                                         nxpym,
02755                                                         system.time);
02756                           Gradient gxpyp = g->component(var_component,
02757                                                         nxpyp,
02758                                                         system.time);
02759                           Number gxzplus = (gxpyp(2) - gxmyp(2))
02760                             / 2. / TOLERANCE;
02761                           Number gxzminus = (gxpym(2) - gxmym(2))
02762                             / 2. / TOLERANCE;
02763                           // xyz derivative
02764                           Ue(current_dof) = (gxzplus - gxzminus)
02765                             / 2. / TOLERANCE;
02766                           dof_is_fixed[current_dof] = true;
02767                           current_dof++;
02768                         }
02769                     }
02770                 }
02771               // Assume that other C_ONE elements have a single nodal
02772               // value shape function and nodal gradient component
02773               // shape functions
02774               else if (cont == C_ONE)
02775                 {
02776                   libmesh_assert_equal_to (nc, 1 + dim);
02777                   Ue(current_dof) = f->component(var_component,
02778                                                  elem->point(n),
02779                                                  system.time);
02780                   dof_is_fixed[current_dof] = true;
02781                   current_dof++;
02782                   Gradient grad = g->component(var_component,
02783                                                elem->point(n),
02784                                                system.time);
02785                   for (unsigned int i=0; i!= dim; ++i)
02786                     {
02787                       Ue(current_dof) = grad(i);
02788                       dof_is_fixed[current_dof] = true;
02789                       current_dof++;
02790                     }
02791                 }
02792               else
02793                 libmesh_error_msg("Unknown continuity " << cont);
02794             }
02795 
02796           // In 3D, project any edge values next
02797           if (dim > 2 && cont != DISCONTINUOUS)
02798             for (unsigned int e=0; e != elem->n_edges(); ++e)
02799               {
02800                 if (!is_boundary_edge[e])
02801                   continue;
02802 
02803                 FEInterface::dofs_on_edge(elem, dim, fe_type, e,
02804                                           side_dofs);
02805 
02806                 // Some edge dofs are on nodes and already
02807                 // fixed, others are free to calculate
02808                 unsigned int free_dofs = 0;
02809                 for (unsigned int i=0; i != side_dofs.size(); ++i)
02810                   if (!dof_is_fixed[side_dofs[i]])
02811                     free_dof[free_dofs++] = i;
02812 
02813                 // There may be nothing to project
02814                 if (!free_dofs)
02815                   continue;
02816 
02817                 Ke.resize (free_dofs, free_dofs); Ke.zero();
02818                 Fe.resize (free_dofs); Fe.zero();
02819                 // The new edge coefficients
02820                 DenseVector<Number> Uedge(free_dofs);
02821 
02822                 // Initialize FE data on the edge
02823                 fe->attach_quadrature_rule (qedgerule.get());
02824                 fe->edge_reinit (elem, e);
02825                 const unsigned int n_qp = qedgerule->n_points();
02826 
02827                 // Loop over the quadrature points
02828                 for (unsigned int qp=0; qp<n_qp; qp++)
02829                   {
02830                     // solution at the quadrature point
02831                     Number fineval = f->component(var_component,
02832                                                   xyz_values[qp],
02833                                                   system.time);
02834                     // solution grad at the quadrature point
02835                     Gradient finegrad;
02836                     if (cont == C_ONE)
02837                       finegrad = g->component(var_component,
02838                                               xyz_values[qp],
02839                                               system.time);
02840 
02841                     // Form edge projection matrix
02842                     for (unsigned int sidei=0, freei=0;
02843                          sidei != side_dofs.size(); ++sidei)
02844                       {
02845                         unsigned int i = side_dofs[sidei];
02846                         // fixed DoFs aren't test functions
02847                         if (dof_is_fixed[i])
02848                           continue;
02849                         for (unsigned int sidej=0, freej=0;
02850                              sidej != side_dofs.size(); ++sidej)
02851                           {
02852                             unsigned int j = side_dofs[sidej];
02853                             if (dof_is_fixed[j])
02854                               Fe(freei) -= phi[i][qp] * phi[j][qp] *
02855                                 JxW[qp] * Ue(j);
02856                             else
02857                               Ke(freei,freej) += phi[i][qp] *
02858                                 phi[j][qp] * JxW[qp];
02859                             if (cont == C_ONE)
02860                               {
02861                                 if (dof_is_fixed[j])
02862                                   Fe(freei) -= ((*dphi)[i][qp] *
02863                                                 (*dphi)[j][qp]) *
02864                                     JxW[qp] * Ue(j);
02865                                 else
02866                                   Ke(freei,freej) += ((*dphi)[i][qp] *
02867                                                       (*dphi)[j][qp])
02868                                     * JxW[qp];
02869                               }
02870                             if (!dof_is_fixed[j])
02871                               freej++;
02872                           }
02873                         Fe(freei) += phi[i][qp] * fineval * JxW[qp];
02874                         if (cont == C_ONE)
02875                           Fe(freei) += (finegrad * (*dphi)[i][qp]) *
02876                             JxW[qp];
02877                         freei++;
02878                       }
02879                   }
02880 
02881                 Ke.cholesky_solve(Fe, Uedge);
02882 
02883                 // Transfer new edge solutions to element
02884                 for (unsigned int i=0; i != free_dofs; ++i)
02885                   {
02886                     Number &ui = Ue(side_dofs[free_dof[i]]);
02887                     libmesh_assert(std::abs(ui) < TOLERANCE ||
02888                                    std::abs(ui - Uedge(i)) < TOLERANCE);
02889                     ui = Uedge(i);
02890                     dof_is_fixed[side_dofs[free_dof[i]]] = true;
02891                   }
02892               }
02893 
02894           // Project any side values (edges in 2D, faces in 3D)
02895           if (dim > 1 && cont != DISCONTINUOUS)
02896             for (unsigned int s=0; s != elem->n_sides(); ++s)
02897               {
02898                 if (!is_boundary_side[s])
02899                   continue;
02900 
02901                 FEInterface::dofs_on_side(elem, dim, fe_type, s,
02902                                           side_dofs);
02903 
02904                 // Some side dofs are on nodes/edges and already
02905                 // fixed, others are free to calculate
02906                 unsigned int free_dofs = 0;
02907                 for (unsigned int i=0; i != side_dofs.size(); ++i)
02908                   if (!dof_is_fixed[side_dofs[i]])
02909                     free_dof[free_dofs++] = i;
02910 
02911                 // There may be nothing to project
02912                 if (!free_dofs)
02913                   continue;
02914 
02915                 Ke.resize (free_dofs, free_dofs); Ke.zero();
02916                 Fe.resize (free_dofs); Fe.zero();
02917                 // The new side coefficients
02918                 DenseVector<Number> Uside(free_dofs);
02919 
02920                 // Initialize FE data on the side
02921                 fe->attach_quadrature_rule (qsiderule.get());
02922                 fe->reinit (elem, s);
02923                 const unsigned int n_qp = qsiderule->n_points();
02924 
02925                 // Loop over the quadrature points
02926                 for (unsigned int qp=0; qp<n_qp; qp++)
02927                   {
02928                     // solution at the quadrature point
02929                     Number fineval = f->component(var_component,
02930                                                   xyz_values[qp],
02931                                                   system.time);
02932                     // solution grad at the quadrature point
02933                     Gradient finegrad;
02934                     if (cont == C_ONE)
02935                       finegrad = g->component(var_component,
02936                                               xyz_values[qp],
02937                                               system.time);
02938 
02939                     // Form side projection matrix
02940                     for (unsigned int sidei=0, freei=0;
02941                          sidei != side_dofs.size(); ++sidei)
02942                       {
02943                         unsigned int i = side_dofs[sidei];
02944                         // fixed DoFs aren't test functions
02945                         if (dof_is_fixed[i])
02946                           continue;
02947                         for (unsigned int sidej=0, freej=0;
02948                              sidej != side_dofs.size(); ++sidej)
02949                           {
02950                             unsigned int j = side_dofs[sidej];
02951                             if (dof_is_fixed[j])
02952                               Fe(freei) -= phi[i][qp] * phi[j][qp] *
02953                                 JxW[qp] * Ue(j);
02954                             else
02955                               Ke(freei,freej) += phi[i][qp] *
02956                                 phi[j][qp] * JxW[qp];
02957                             if (cont == C_ONE)
02958                               {
02959                                 if (dof_is_fixed[j])
02960                                   Fe(freei) -= ((*dphi)[i][qp] *
02961                                                 (*dphi)[j][qp]) *
02962                                     JxW[qp] * Ue(j);
02963                                 else
02964                                   Ke(freei,freej) += ((*dphi)[i][qp] *
02965                                                       (*dphi)[j][qp])
02966                                     * JxW[qp];
02967                               }
02968                             if (!dof_is_fixed[j])
02969                               freej++;
02970                           }
02971                         Fe(freei) += (fineval * phi[i][qp]) * JxW[qp];
02972                         if (cont == C_ONE)
02973                           Fe(freei) += (finegrad * (*dphi)[i][qp]) *
02974                             JxW[qp];
02975                         freei++;
02976                       }
02977                   }
02978 
02979                 Ke.cholesky_solve(Fe, Uside);
02980 
02981                 // Transfer new side solutions to element
02982                 for (unsigned int i=0; i != free_dofs; ++i)
02983                   {
02984                     Number &ui = Ue(side_dofs[free_dof[i]]);
02985                     libmesh_assert(std::abs(ui) < TOLERANCE ||
02986                                    std::abs(ui - Uside(i)) < TOLERANCE);
02987                     ui = Uside(i);
02988                     dof_is_fixed[side_dofs[free_dof[i]]] = true;
02989                   }
02990               }
02991 
02992           const dof_id_type
02993             first = new_vector.first_local_index(),
02994             last  = new_vector.last_local_index();
02995 
02996           // Lock the new_vector since it is shared among threads.
02997           {
02998             Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
02999 
03000             for (unsigned int i = 0; i < n_dofs; i++)
03001               if (dof_is_fixed[i] &&
03002                   (dof_indices[i] >= first) &&
03003                   (dof_indices[i] <  last))
03004                 {
03005                   new_vector.set(dof_indices[i], Ue(i));
03006                 }
03007           }
03008         }  // end elem loop
03009     } // end variables loop
03010 }
03011 
03012 
03013 } // namespace libMesh