$extrastylesheet
00001 // The libMesh Finite Element Library. 00002 // Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner 00003 00004 // This library is free software; you can redistribute it and/or 00005 // modify it under the terms of the GNU Lesser General Public 00006 // License as published by the Free Software Foundation; either 00007 // version 2.1 of the License, or (at your option) any later version. 00008 00009 // This library is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 // Lesser General Public License for more details. 00013 00014 // You should have received a copy of the GNU Lesser General Public 00015 // License along with this library; if not, write to the Free Software 00016 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00017 00018 00019 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 ¶meters; 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 ¶meters_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 ¶meters; 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 ¶meters_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, ¶meters); 00463 WrappedFunction<Gradient> g(*this, gptr, ¶meters); 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, ¶meters); 00511 WrappedFunction<Gradient> g(*this, gptr, ¶meters); 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, ¶meters); 00667 WrappedFunction<Gradient> g(*this, gptr, ¶meters); 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, ¶meters); 00712 WrappedFunction<Gradient> g(*this, gptr, ¶meters); 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