fem_system.C
Go to the documentation of this file.00001 // The libMesh Finite Element Library. 00002 // Copyright (C) 2002-2012 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 #include "libmesh/dof_map.h" 00021 #include "libmesh/elem.h" 00022 #include "libmesh/equation_systems.h" 00023 #include "libmesh/fe_base.h" 00024 #include "libmesh/fem_context.h" 00025 #include "libmesh/fem_system.h" 00026 #include "libmesh/libmesh_logging.h" 00027 #include "libmesh/mesh_base.h" 00028 #include "libmesh/numeric_vector.h" 00029 #include "libmesh/parallel.h" 00030 #include "libmesh/parallel_algebra.h" 00031 #include "libmesh/parallel_ghost_sync.h" 00032 #include "libmesh/quadrature.h" 00033 #include "libmesh/sparse_matrix.h" 00034 #include "libmesh/time_solver.h" 00035 #include "libmesh/unsteady_solver.h" // For eulerian_residual 00036 00037 00038 namespace { 00039 using namespace libMesh; 00040 00041 // give this guy some scope since there 00042 // is underlying vector allocation upon 00043 // creation/deletion 00044 ConstElemRange elem_range; 00045 00046 typedef Threads::spin_mutex femsystem_mutex; 00047 femsystem_mutex assembly_mutex; 00048 00049 class AssemblyContributions 00050 { 00051 public: 00055 AssemblyContributions(FEMSystem &sys, 00056 bool get_residual, 00057 bool get_jacobian) : 00058 _sys(sys), 00059 _get_residual(get_residual), 00060 _get_jacobian(get_jacobian) {} 00061 00065 void operator()(const ConstElemRange &range) const 00066 { 00067 AutoPtr<DiffContext> con = _sys.build_context(); 00068 FEMContext &_femcontext = libmesh_cast_ref<FEMContext&>(*con); 00069 _sys.init_context(_femcontext); 00070 00071 for (ConstElemRange::const_iterator elem_it = range.begin(); 00072 elem_it != range.end(); ++elem_it) 00073 { 00074 Elem *el = const_cast<Elem *>(*elem_it); 00075 00076 _femcontext.pre_fe_reinit(_sys, el); 00077 _femcontext.elem_fe_reinit(); 00078 00079 bool jacobian_computed = 00080 _sys.time_solver->element_residual(_get_jacobian, _femcontext); 00081 00082 // Compute a numeric jacobian if we have to 00083 if (_get_jacobian && !jacobian_computed) 00084 { 00085 // Make sure we didn't compute a jacobian and lie about it 00086 libmesh_assert_equal_to (_femcontext.elem_jacobian.l1_norm(), 0.0); 00087 // Logging of numerical jacobians is done separately 00088 _sys.numerical_elem_jacobian(_femcontext); 00089 } 00090 00091 // Compute a numeric jacobian if we're asked to verify the 00092 // analytic jacobian we got 00093 if (_get_jacobian && jacobian_computed && 00094 _sys.verify_analytic_jacobians != 0.0) 00095 { 00096 DenseMatrix<Number> analytic_jacobian(_femcontext.elem_jacobian); 00097 00098 _femcontext.elem_jacobian.zero(); 00099 // Logging of numerical jacobians is done separately 00100 _sys.numerical_elem_jacobian(_femcontext); 00101 00102 Real analytic_norm = analytic_jacobian.l1_norm(); 00103 Real numerical_norm = _femcontext.elem_jacobian.l1_norm(); 00104 00105 // If we can continue, we'll probably prefer the analytic jacobian 00106 analytic_jacobian.swap(_femcontext.elem_jacobian); 00107 00108 // The matrix "analytic_jacobian" will now hold the error matrix 00109 analytic_jacobian.add(-1.0, _femcontext.elem_jacobian); 00110 Real error_norm = analytic_jacobian.l1_norm(); 00111 00112 Real relative_error = error_norm / 00113 std::max(analytic_norm, numerical_norm); 00114 00115 if (relative_error > _sys.verify_analytic_jacobians) 00116 { 00117 libMesh::err << "Relative error " << relative_error 00118 << " detected in analytic jacobian on element " 00119 << _femcontext.elem->id() << '!' << std::endl; 00120 00121 std::streamsize old_precision = libMesh::out.precision(); 00122 libMesh::out.precision(16); 00123 libMesh::out << "J_analytic " << _femcontext.elem->id() << " = " 00124 << _femcontext.elem_jacobian << std::endl; 00125 analytic_jacobian.add(1.0, _femcontext.elem_jacobian); 00126 libMesh::out << "J_numeric " << _femcontext.elem->id() << " = " 00127 << analytic_jacobian << std::endl; 00128 00129 libMesh::out.precision(old_precision); 00130 00131 libmesh_error(); 00132 } 00133 } 00134 00135 for (_femcontext.side = 0; 00136 _femcontext.side != _femcontext.elem->n_sides(); 00137 ++_femcontext.side) 00138 { 00139 // Don't compute on non-boundary sides unless requested 00140 if (!_sys.get_physics()->compute_internal_sides && 00141 _femcontext.elem->neighbor(_femcontext.side) != NULL) 00142 continue; 00143 00144 // Any mesh movement has already been done (and restored, 00145 // if the TimeSolver isn't broken), but 00146 // reinitializing the side FE objects is still necessary 00147 _femcontext.side_fe_reinit(); 00148 00149 DenseMatrix<Number> old_jacobian; 00150 // If we're in DEBUG mode, we should always verify that the 00151 // user's side_residual function doesn't alter our existing 00152 // jacobian and then lie about it 00153 #ifndef DEBUG 00154 // Even if we're not in DEBUG mode, when we're verifying 00155 // analytic jacobians we'll want to verify each side's 00156 // jacobian contribution separately. 00157 /* PB: We also need to account for the case when the user wants to 00158 use numerical Jacobians and not analytic Jacobians */ 00159 if ( (_sys.verify_analytic_jacobians != 0.0 && _get_jacobian) || 00160 (!jacobian_computed && _get_jacobian) ) 00161 #endif // ifndef DEBUG 00162 { 00163 old_jacobian = _femcontext.elem_jacobian; 00164 _femcontext.elem_jacobian.zero(); 00165 } 00166 jacobian_computed = 00167 _sys.time_solver->side_residual(_get_jacobian, _femcontext); 00168 00169 // Compute a numeric jacobian if we have to 00170 if (_get_jacobian && !jacobian_computed) 00171 { 00172 // In DEBUG mode, we've already set elem_jacobian == 0, 00173 // so we can make sure side_residual didn't compute a 00174 // jacobian and lie about it 00175 #ifdef DEBUG 00176 libmesh_assert_equal_to (_femcontext.elem_jacobian.l1_norm(), 0.0); 00177 #endif 00178 // Logging of numerical jacobians is done separately 00179 _sys.numerical_side_jacobian(_femcontext); 00180 00181 // Add back in element interior numerical Jacobian 00182 _femcontext.elem_jacobian += old_jacobian; 00183 } 00184 00185 // Compute a numeric jacobian if we're asked to verify the 00186 // analytic jacobian we got 00187 if (_get_jacobian && jacobian_computed && 00188 _sys.verify_analytic_jacobians != 0.0) 00189 { 00190 DenseMatrix<Number> analytic_jacobian(_femcontext.elem_jacobian); 00191 00192 _femcontext.elem_jacobian.zero(); 00193 // Logging of numerical jacobians is done separately 00194 _sys.numerical_side_jacobian(_femcontext); 00195 00196 Real analytic_norm = analytic_jacobian.l1_norm(); 00197 Real numerical_norm = _femcontext.elem_jacobian.l1_norm(); 00198 00199 // If we can continue, we'll probably prefer the analytic jacobian 00200 analytic_jacobian.swap(_femcontext.elem_jacobian); 00201 00202 // The matrix "analytic_jacobian" will now hold the error matrix 00203 analytic_jacobian.add(-1.0, _femcontext.elem_jacobian); 00204 Real error_norm = analytic_jacobian.l1_norm(); 00205 00206 Real relative_error = error_norm / 00207 std::max(analytic_norm, numerical_norm); 00208 00209 if (relative_error > _sys.verify_analytic_jacobians) 00210 { 00211 libMesh::err << "Relative error " << relative_error 00212 << " detected in analytic jacobian on element " 00213 << _femcontext.elem->id() 00214 << ", side " 00215 << static_cast<unsigned int>(_femcontext.side) << '!' << std::endl; 00216 00217 std::streamsize old_precision = libMesh::out.precision(); 00218 libMesh::out.precision(16); 00219 libMesh::out << "J_analytic " << _femcontext.elem->id() << " = " 00220 << _femcontext.elem_jacobian << std::endl; 00221 analytic_jacobian.add(1.0, _femcontext.elem_jacobian); 00222 libMesh::out << "J_numeric " << _femcontext.elem->id() << " = " 00223 << analytic_jacobian << std::endl; 00224 libMesh::out.precision(old_precision); 00225 00226 libmesh_error(); 00227 } 00228 // Once we've verified a side, we'll want to add back the 00229 // rest of the accumulated jacobian 00230 _femcontext.elem_jacobian += old_jacobian; 00231 } 00232 // In DEBUG mode, we've set elem_jacobian == 0, and we 00233 // may still need to add the old jacobian back 00234 #ifdef DEBUG 00235 if (_get_jacobian && jacobian_computed && 00236 _sys.verify_analytic_jacobians == 0.0) 00237 { 00238 _femcontext.elem_jacobian += old_jacobian; 00239 } 00240 #endif // ifdef DEBUG 00241 } 00242 00243 #ifdef LIBMESH_ENABLE_CONSTRAINTS 00244 // We turn off the asymmetric constraint application; 00245 // enforce_constraints_exactly() should be called in the solver 00246 if (_get_residual && _get_jacobian) 00247 _sys.get_dof_map().constrain_element_matrix_and_vector 00248 (_femcontext.elem_jacobian, _femcontext.elem_residual, 00249 _femcontext.dof_indices, false); 00250 else if (_get_residual) 00251 _sys.get_dof_map().constrain_element_vector 00252 (_femcontext.elem_residual, _femcontext.dof_indices, false); 00253 else if (_get_jacobian) 00254 _sys.get_dof_map().constrain_element_matrix 00255 (_femcontext.elem_jacobian, _femcontext.dof_indices, false); 00256 #endif // #ifdef LIBMESH_ENABLE_CONSTRAINTS 00257 00258 if (_get_jacobian && _sys.print_element_jacobians) 00259 { 00260 std::streamsize old_precision = libMesh::out.precision(); 00261 libMesh::out.precision(16); 00262 libMesh::out << "J_elem " << _femcontext.elem->id() << " = " 00263 << _femcontext.elem_jacobian << std::endl; 00264 libMesh::out.precision(old_precision); 00265 } 00266 00267 { // A lock is necessary around access to the global system 00268 femsystem_mutex::scoped_lock lock(assembly_mutex); 00269 00270 if (_get_jacobian) 00271 _sys.matrix->add_matrix (_femcontext.elem_jacobian, 00272 _femcontext.dof_indices); 00273 if (_get_residual) 00274 _sys.rhs->add_vector (_femcontext.elem_residual, 00275 _femcontext.dof_indices); 00276 } // Scope for assembly mutex 00277 00278 } 00279 } 00280 00281 private: 00282 00283 FEMSystem& _sys; 00284 00285 const bool _get_residual, _get_jacobian; 00286 }; 00287 00288 class PostprocessContributions 00289 { 00290 public: 00294 explicit 00295 PostprocessContributions(FEMSystem &sys) : _sys(sys) {} 00296 00300 void operator()(const ConstElemRange &range) const 00301 { 00302 AutoPtr<DiffContext> con = _sys.build_context(); 00303 FEMContext &_femcontext = libmesh_cast_ref<FEMContext&>(*con); 00304 _sys.init_context(_femcontext); 00305 00306 for (ConstElemRange::const_iterator elem_it = range.begin(); 00307 elem_it != range.end(); ++elem_it) 00308 { 00309 Elem *el = const_cast<Elem *>(*elem_it); 00310 _femcontext.pre_fe_reinit(_sys, el); 00311 00312 // Optionally initialize all the interior FE objects on elem. 00313 if (_sys.fe_reinit_during_postprocess) 00314 _femcontext.elem_fe_reinit(); 00315 00316 _sys.element_postprocess(_femcontext); 00317 00318 for (_femcontext.side = 0; 00319 _femcontext.side != _femcontext.elem->n_sides(); 00320 ++_femcontext.side) 00321 { 00322 // Don't compute on non-boundary sides unless requested 00323 if (!_sys.postprocess_sides || 00324 (!_sys.get_physics()->compute_internal_sides && 00325 _femcontext.elem->neighbor(_femcontext.side) != NULL)) 00326 continue; 00327 00328 // Optionally initialize all the FE objects on this side. 00329 if (_sys.fe_reinit_during_postprocess) 00330 _femcontext.side_fe_reinit(); 00331 00332 _sys.side_postprocess(_femcontext); 00333 } 00334 } 00335 } 00336 00337 private: 00338 00339 FEMSystem& _sys; 00340 }; 00341 00342 class QoIContributions 00343 { 00344 public: 00348 explicit 00349 QoIContributions(FEMSystem &sys, DifferentiableQoI &diff_qoi, 00350 const QoISet &qoi_indices) : 00351 qoi(sys.qoi.size(), 0.), _sys(sys), _diff_qoi(diff_qoi),_qoi_indices(qoi_indices) {} 00352 00356 QoIContributions(const QoIContributions &other, 00357 Threads::split) : 00358 qoi(other._sys.qoi.size(), 0.), _sys(other._sys), _diff_qoi(other._diff_qoi) {} 00359 00363 void operator()(const ConstElemRange &range) 00364 { 00365 AutoPtr<DiffContext> con = _sys.build_context(); 00366 FEMContext &_femcontext = libmesh_cast_ref<FEMContext&>(*con); 00367 _diff_qoi.init_context(_femcontext); 00368 00369 for (ConstElemRange::const_iterator elem_it = range.begin(); 00370 elem_it != range.end(); ++elem_it) 00371 { 00372 Elem *el = const_cast<Elem *>(*elem_it); 00373 00374 _femcontext.pre_fe_reinit(_sys, el); 00375 00376 if (_diff_qoi.assemble_qoi_elements) 00377 { 00378 _femcontext.elem_fe_reinit(); 00379 00380 _diff_qoi.element_qoi(_femcontext, _qoi_indices); 00381 } 00382 00383 for (_femcontext.side = 0; 00384 _femcontext.side != _femcontext.elem->n_sides(); 00385 ++_femcontext.side) 00386 { 00387 // Don't compute on non-boundary sides unless requested 00388 if (!_diff_qoi.assemble_qoi_sides || 00389 (!_diff_qoi.assemble_qoi_internal_sides && 00390 _femcontext.elem->neighbor(_femcontext.side) != NULL)) 00391 continue; 00392 00393 _femcontext.side_fe_reinit(); 00394 00395 _diff_qoi.side_qoi(_femcontext, _qoi_indices); 00396 } 00397 } 00398 00399 this->_diff_qoi.thread_join( this->qoi, _femcontext.elem_qoi, _qoi_indices ); 00400 } 00401 00402 void join (const QoIContributions& other) 00403 { 00404 libmesh_assert_equal_to (this->qoi.size(), other.qoi.size()); 00405 this->_diff_qoi.thread_join( this->qoi, other.qoi, _qoi_indices ); 00406 } 00407 00408 std::vector<Number> qoi; 00409 00410 private: 00411 00412 FEMSystem& _sys; 00413 DifferentiableQoI& _diff_qoi; 00414 00415 const QoISet _qoi_indices; 00416 }; 00417 00418 class QoIDerivativeContributions 00419 { 00420 public: 00424 QoIDerivativeContributions(FEMSystem &sys, const QoISet& qoi_indices, 00425 DifferentiableQoI &qoi ) : 00426 _sys(sys), _qoi_indices(qoi_indices), _qoi(qoi) {} 00427 00431 void operator()(const ConstElemRange &range) const 00432 { 00433 AutoPtr<DiffContext> con = _sys.build_context(); 00434 FEMContext &_femcontext = libmesh_cast_ref<FEMContext&>(*con); 00435 _qoi.init_context(_femcontext); 00436 00437 for (ConstElemRange::const_iterator elem_it = range.begin(); 00438 elem_it != range.end(); ++elem_it) 00439 { 00440 Elem *el = const_cast<Elem *>(*elem_it); 00441 00442 _femcontext.pre_fe_reinit(_sys, el); 00443 00444 if (_qoi.assemble_qoi_elements) 00445 { 00446 _femcontext.elem_fe_reinit(); 00447 00448 _qoi.element_qoi_derivative(_femcontext, _qoi_indices); 00449 } 00450 00451 for (_femcontext.side = 0; 00452 _femcontext.side != _femcontext.elem->n_sides(); 00453 ++_femcontext.side) 00454 { 00455 // Don't compute on non-boundary sides unless requested 00456 if (!_qoi.assemble_qoi_sides || 00457 (!_qoi.assemble_qoi_internal_sides && 00458 _femcontext.elem->neighbor(_femcontext.side) != NULL)) 00459 continue; 00460 00461 _femcontext.side_fe_reinit(); 00462 00463 _qoi.side_qoi_derivative(_femcontext, _qoi_indices); 00464 } 00465 00466 // We need some unmodified indices to use for constraining 00467 // multiple vector 00468 // FIXME - there should be a DofMap::constrain_element_vectors 00469 // to do this more efficiently 00470 std::vector<dof_id_type> original_dofs = _femcontext.dof_indices; 00471 00472 for (unsigned int i=0; i != _sys.qoi.size(); ++i) 00473 if (_qoi_indices.has_index(i)) 00474 { 00475 _femcontext.dof_indices = original_dofs; 00476 _sys.get_dof_map().constrain_element_vector 00477 (_femcontext.elem_qoi_derivative[i], _femcontext.dof_indices, false); 00478 00479 _sys.get_adjoint_rhs(i).add_vector 00480 (_femcontext.elem_qoi_derivative[i], _femcontext.dof_indices); 00481 } 00482 } 00483 } 00484 00485 private: 00486 00487 FEMSystem& _sys; 00488 const QoISet& _qoi_indices; 00489 DifferentiableQoI& _qoi; 00490 }; 00491 00492 00493 } 00494 00495 00496 namespace libMesh 00497 { 00498 00499 00500 00501 00502 00503 FEMSystem::FEMSystem (EquationSystems& es, 00504 const std::string& name_in, 00505 const unsigned int number_in) 00506 : Parent(es, name_in, number_in), 00507 fe_reinit_during_postprocess(true), 00508 numerical_jacobian_h(TOLERANCE), 00509 verify_analytic_jacobians(0.0) 00510 { 00511 } 00512 00513 00514 FEMSystem::~FEMSystem () 00515 { 00516 this->clear(); 00517 } 00518 00519 00520 00521 void FEMSystem::clear() 00522 { 00523 Parent::clear(); 00524 } 00525 00526 00527 00528 void FEMSystem::init_data () 00529 { 00530 // First initialize LinearImplicitSystem data 00531 Parent::init_data(); 00532 } 00533 00534 00535 void FEMSystem::assembly (bool get_residual, bool get_jacobian) 00536 { 00537 libmesh_assert(get_residual || get_jacobian); 00538 std::string log_name; 00539 if (get_residual && get_jacobian) 00540 log_name = "assembly()"; 00541 else if (get_residual) 00542 log_name = "assembly(get_residual)"; 00543 else 00544 log_name = "assembly(get_jacobian)"; 00545 00546 START_LOG(log_name, "FEMSystem"); 00547 00548 const MeshBase& mesh = this->get_mesh(); 00549 00550 // this->get_vector("_nonlinear_solution").localize 00551 // (*current_local_nonlinear_solution, 00552 // dof_map.get_send_list()); 00553 this->update(); 00554 00555 if (print_solution_norms) 00556 { 00557 // this->get_vector("_nonlinear_solution").close(); 00558 this->solution->close(); 00559 00560 std::streamsize old_precision = libMesh::out.precision(); 00561 libMesh::out.precision(16); 00562 libMesh::out << "|U| = " 00563 // << this->get_vector("_nonlinear_solution").l1_norm() 00564 << this->solution->l1_norm() 00565 << std::endl; 00566 libMesh::out.precision(old_precision); 00567 } 00568 if (print_solutions) 00569 { 00570 std::streamsize old_precision = libMesh::out.precision(); 00571 libMesh::out.precision(16); 00572 // libMesh::out << "U = [" << this->get_vector("_nonlinear_solution") 00573 libMesh::out << "U = [" << *(this->solution) 00574 << "];" << std::endl; 00575 libMesh::out.precision(old_precision); 00576 } 00577 00578 // Is this definitely necessary? [RHS] 00579 // Yes. [RHS 2012] 00580 if (get_jacobian) 00581 matrix->zero(); 00582 if (get_residual) 00583 rhs->zero(); 00584 00585 // Stupid C++ lets you set *Real* verify_analytic_jacobians = true! 00586 if (verify_analytic_jacobians > 0.5) 00587 { 00588 libMesh::err << "WARNING! verify_analytic_jacobians was set " 00589 << "to absurdly large value of " 00590 << verify_analytic_jacobians << std::endl; 00591 libMesh::err << "Resetting to 1e-6!" << std::endl; 00592 verify_analytic_jacobians = 1e-6; 00593 } 00594 00595 // In time-dependent problems, the nonlinear function we're trying 00596 // to solve at each timestep may depend on the particular solver 00597 // we're using 00598 libmesh_assert(time_solver.get()); 00599 00600 // Build the residual and jacobian contributions on every active 00601 // mesh element on this processor 00602 Threads::parallel_for(elem_range.reset(mesh.active_local_elements_begin(), 00603 mesh.active_local_elements_end()), 00604 AssemblyContributions(*this, get_residual, get_jacobian)); 00605 00606 00607 if (get_residual && (print_residual_norms || print_residuals)) 00608 this->rhs->close(); 00609 if (get_residual && print_residual_norms) 00610 { 00611 std::streamsize old_precision = libMesh::out.precision(); 00612 libMesh::out.precision(16); 00613 libMesh::out << "|F| = " << this->rhs->l1_norm() << std::endl; 00614 libMesh::out.precision(old_precision); 00615 } 00616 if (get_residual && print_residuals) 00617 { 00618 std::streamsize old_precision = libMesh::out.precision(); 00619 libMesh::out.precision(16); 00620 libMesh::out << "F = [" << *(this->rhs) << "];" << std::endl; 00621 libMesh::out.precision(old_precision); 00622 } 00623 00624 if (get_jacobian && (print_jacobian_norms || print_jacobians)) 00625 this->matrix->close(); 00626 if (get_jacobian && print_jacobian_norms) 00627 { 00628 std::streamsize old_precision = libMesh::out.precision(); 00629 libMesh::out.precision(16); 00630 libMesh::out << "|J| = " << this->matrix->l1_norm() << std::endl; 00631 libMesh::out.precision(old_precision); 00632 } 00633 if (get_jacobian && print_jacobians) 00634 { 00635 std::streamsize old_precision = libMesh::out.precision(); 00636 libMesh::out.precision(16); 00637 libMesh::out << "J = [" << *(this->matrix) << "];" << std::endl; 00638 libMesh::out.precision(old_precision); 00639 } 00640 STOP_LOG(log_name, "FEMSystem"); 00641 } 00642 00643 00644 00645 void FEMSystem::solve() 00646 { 00647 // We are solving the primal problem 00648 Parent::solve(); 00649 00650 // On a moving mesh we want the mesh to reflect the new solution 00651 this->mesh_position_set(); 00652 } 00653 00654 00655 00656 void FEMSystem::mesh_position_set() 00657 { 00658 // If we don't need to move the mesh, we're done 00659 if (_mesh_sys != this) 00660 return; 00661 00662 MeshBase& mesh = this->get_mesh(); 00663 00664 AutoPtr<DiffContext> con = this->build_context(); 00665 FEMContext &_femcontext = libmesh_cast_ref<FEMContext&>(*con); 00666 this->init_context(_femcontext); 00667 00668 // Move every mesh element we can 00669 MeshBase::const_element_iterator el = 00670 mesh.active_local_elements_begin(); 00671 const MeshBase::const_element_iterator end_el = 00672 mesh.active_local_elements_end(); 00673 00674 for ( ; el != end_el; ++el) 00675 { 00676 // We need the algebraic data 00677 _femcontext.pre_fe_reinit(*this, *el); 00678 // And when asserts are on, we also need the FE so 00679 // we can assert that the mesh data is of the right type. 00680 #ifndef NDEBUG 00681 _femcontext.elem_fe_reinit(); 00682 #endif 00683 00684 // This code won't handle moving subactive elements 00685 libmesh_assert(!_femcontext.elem->has_children()); 00686 00687 _femcontext.elem_position_set(1.); 00688 } 00689 00690 // We've now got positions set on all local nodes (and some 00691 // semilocal nodes); let's request positions for non-local nodes 00692 // from their processors. 00693 00694 SyncNodalPositions sync_object(mesh); 00695 Parallel::sync_dofobject_data_by_id 00696 (mesh.nodes_begin(), mesh.nodes_end(), sync_object); 00697 } 00698 00699 00700 00701 void FEMSystem::postprocess () 00702 { 00703 START_LOG("postprocess()", "FEMSystem"); 00704 00705 const MeshBase& mesh = this->get_mesh(); 00706 00707 this->update(); 00708 00709 // Get the time solver object associated with the system, and tell it that 00710 // we are not solving the adjoint problem 00711 this->get_time_solver().set_is_adjoint(false); 00712 00713 // Loop over every active mesh element on this processor 00714 Threads::parallel_for(elem_range.reset(mesh.active_local_elements_begin(), 00715 mesh.active_local_elements_end()), 00716 PostprocessContributions(*this)); 00717 00718 STOP_LOG("postprocess()", "FEMSystem"); 00719 } 00720 00721 00722 00723 void FEMSystem::assemble_qoi (const QoISet &qoi_indices) 00724 { 00725 START_LOG("assemble_qoi()", "FEMSystem"); 00726 00727 const MeshBase& mesh = this->get_mesh(); 00728 00729 this->update(); 00730 00731 const unsigned int Nq = libmesh_cast_int<unsigned int>(qoi.size()); 00732 00733 // the quantity of interest is assumed to be a sum of element and 00734 // side terms 00735 for (unsigned int i=0; i != Nq; ++i) 00736 if (qoi_indices.has_index(i)) 00737 qoi[i] = 0; 00738 00739 // Create a non-temporary qoi_contributions object, so we can query 00740 // its results after the reduction 00741 QoIContributions qoi_contributions(*this, *(this->diff_qoi), qoi_indices); 00742 00743 // Loop over every active mesh element on this processor 00744 Threads::parallel_reduce(elem_range.reset(mesh.active_local_elements_begin(), 00745 mesh.active_local_elements_end()), 00746 qoi_contributions); 00747 00748 this->diff_qoi->parallel_op( this->qoi, qoi_contributions.qoi, qoi_indices ); 00749 00750 STOP_LOG("assemble_qoi()", "FEMSystem"); 00751 } 00752 00753 00754 00755 void FEMSystem::assemble_qoi_derivative (const QoISet& qoi_indices) 00756 { 00757 START_LOG("assemble_qoi_derivative()", "FEMSystem"); 00758 00759 const MeshBase& mesh = this->get_mesh(); 00760 00761 this->update(); 00762 00763 // The quantity of interest derivative assembly accumulates on 00764 // initially zero vectors 00765 for (unsigned int i=0; i != qoi.size(); ++i) 00766 if (qoi_indices.has_index(i)) 00767 this->add_adjoint_rhs(i).zero(); 00768 00769 // Loop over every active mesh element on this processor 00770 Threads::parallel_for(elem_range.reset(mesh.active_local_elements_begin(), 00771 mesh.active_local_elements_end()), 00772 QoIDerivativeContributions(*this, qoi_indices, 00773 *(this->diff_qoi))); 00774 00775 STOP_LOG("assemble_qoi_derivative()", "FEMSystem"); 00776 } 00777 00778 00779 00780 void FEMSystem::numerical_jacobian (TimeSolverResPtr res, 00781 FEMContext &context) 00782 { 00783 // Logging is done by numerical_elem_jacobian 00784 // or numerical_side_jacobian 00785 00786 DenseVector<Number> original_residual(context.elem_residual); 00787 DenseVector<Number> backwards_residual(context.elem_residual); 00788 DenseMatrix<Number> numeric_jacobian(context.elem_jacobian); 00789 #ifdef DEBUG 00790 DenseMatrix<Number> old_jacobian(context.elem_jacobian); 00791 #endif 00792 00793 Real numerical_point_h = 0.; 00794 if (_mesh_sys == this) 00795 numerical_point_h = numerical_jacobian_h * context.elem->hmin(); 00796 00797 for (unsigned int j = 0; j != context.dof_indices.size(); ++j) 00798 { 00799 // Take the "minus" side of a central differenced first derivative 00800 Number original_solution = context.elem_solution(j); 00801 context.elem_solution(j) -= numerical_jacobian_h; 00802 00803 // Make sure to catch any moving mesh terms 00804 // FIXME - this could be less ugly 00805 Real *coord = NULL; 00806 if (_mesh_sys == this) 00807 { 00808 if (_mesh_x_var != libMesh::invalid_uint) 00809 for (unsigned int k = 0; 00810 k != context.dof_indices_var[_mesh_x_var].size(); ++k) 00811 if (context.dof_indices_var[_mesh_x_var][k] == 00812 context.dof_indices[j]) 00813 coord = &(const_cast<Elem*>(context.elem)->point(k)(0)); 00814 if (_mesh_y_var != libMesh::invalid_uint) 00815 for (unsigned int k = 0; 00816 k != context.dof_indices_var[_mesh_y_var].size(); ++k) 00817 if (context.dof_indices_var[_mesh_y_var][k] == 00818 context.dof_indices[j]) 00819 coord = &(const_cast<Elem*>(context.elem)->point(k)(1)); 00820 if (_mesh_z_var != libMesh::invalid_uint) 00821 for (unsigned int k = 0; 00822 k != context.dof_indices_var[_mesh_z_var].size(); ++k) 00823 if (context.dof_indices_var[_mesh_z_var][k] == 00824 context.dof_indices[j]) 00825 coord = &(const_cast<Elem*>(context.elem)->point(k)(2)); 00826 } 00827 if (coord) 00828 { 00829 // We have enough information to scale the perturbations 00830 // here appropriately 00831 context.elem_solution(j) = original_solution - numerical_point_h; 00832 *coord = libmesh_real(context.elem_solution(j)); 00833 } 00834 00835 context.elem_residual.zero(); 00836 ((*time_solver).*(res))(false, context); 00837 #ifdef DEBUG 00838 libmesh_assert_equal_to (old_jacobian, context.elem_jacobian); 00839 #endif 00840 backwards_residual = context.elem_residual; 00841 00842 // Take the "plus" side of a central differenced first derivative 00843 context.elem_solution(j) = original_solution + numerical_jacobian_h; 00844 if (coord) 00845 { 00846 context.elem_solution(j) = original_solution + numerical_point_h; 00847 *coord = libmesh_real(context.elem_solution(j)); 00848 } 00849 context.elem_residual.zero(); 00850 ((*time_solver).*(res))(false, context); 00851 #ifdef DEBUG 00852 libmesh_assert_equal_to (old_jacobian, context.elem_jacobian); 00853 #endif 00854 00855 context.elem_solution(j) = original_solution; 00856 if (coord) 00857 { 00858 *coord = libmesh_real(context.elem_solution(j)); 00859 for (unsigned int i = 0; i != context.dof_indices.size(); ++i) 00860 { 00861 numeric_jacobian(i,j) = 00862 (context.elem_residual(i) - backwards_residual(i)) / 00863 2. / numerical_point_h; 00864 } 00865 } 00866 else 00867 { 00868 for (unsigned int i = 0; i != context.dof_indices.size(); ++i) 00869 { 00870 numeric_jacobian(i,j) = 00871 (context.elem_residual(i) - backwards_residual(i)) / 00872 2. / numerical_jacobian_h; 00873 } 00874 } 00875 } 00876 00877 context.elem_residual = original_residual; 00878 context.elem_jacobian = numeric_jacobian; 00879 } 00880 00881 00882 00883 void FEMSystem::numerical_elem_jacobian (FEMContext &context) 00884 { 00885 START_LOG("numerical_elem_jacobian()", "FEMSystem"); 00886 this->numerical_jacobian(&TimeSolver::element_residual, context); 00887 STOP_LOG("numerical_elem_jacobian()", "FEMSystem"); 00888 } 00889 00890 00891 00892 void FEMSystem::numerical_side_jacobian (FEMContext &context) 00893 { 00894 START_LOG("numerical_side_jacobian()", "FEMSystem"); 00895 this->numerical_jacobian(&TimeSolver::side_residual, context); 00896 STOP_LOG("numerical_side_jacobian()", "FEMSystem"); 00897 } 00898 00899 00900 00901 AutoPtr<DiffContext> FEMSystem::build_context () 00902 { 00903 FEMContext* fc = new FEMContext(*this); 00904 00905 AutoPtr<DiffContext> ap(fc); 00906 00907 DifferentiablePhysics* phys = this->get_physics(); 00908 00909 libmesh_assert (phys); 00910 00911 // If we are solving a moving mesh problem, tell that to the Context 00912 fc->set_mesh_system(phys->get_mesh_system()); 00913 fc->set_mesh_x_var(phys->get_mesh_x_var()); 00914 fc->set_mesh_y_var(phys->get_mesh_y_var()); 00915 fc->set_mesh_z_var(phys->get_mesh_z_var()); 00916 00917 ap->set_deltat_pointer( &deltat ); 00918 00919 // If we are solving the adjoint problem, tell that to the Context 00920 ap->is_adjoint() = this->get_time_solver().is_adjoint(); 00921 00922 return ap; 00923 } 00924 00925 00926 00927 void FEMSystem::init_context(DiffContext &c) 00928 { 00929 // Parent::init_context(c); // may be a good idea in derived classes 00930 00931 // Although we do this in DiffSystem::build_context() and 00932 // FEMSystem::build_context() as well, we do it here just to be 00933 // extra sure that the deltat pointer gets set. Since the 00934 // intended behavior is for classes derived from FEMSystem to 00935 // call Parent::init_context() in their own init_context() 00936 // overloads, we can ensure that those classes get the correct 00937 // deltat pointers even if they have different build_context() 00938 // overloads. 00939 c.set_deltat_pointer ( &deltat ); 00940 00941 FEMContext &context = libmesh_cast_ref<FEMContext&>(c); 00942 00943 // Make sure we're prepared to do mass integration 00944 for (unsigned int var = 0; var != this->n_vars(); ++var) 00945 if (this->get_physics()->is_time_evolving(var)) 00946 { 00947 context.element_fe_var[var]->get_JxW(); 00948 context.element_fe_var[var]->get_phi(); 00949 } 00950 } 00951 00952 00953 00954 void FEMSystem::mesh_position_get() 00955 { 00956 // This function makes no sense unless we've already picked out some 00957 // variable(s) to reflect mesh position coordinates 00958 if (!_mesh_sys) 00959 libmesh_error(); 00960 00961 // We currently assume mesh variables are in our own system 00962 if (_mesh_sys != this) 00963 libmesh_not_implemented(); 00964 00965 // Loop over every active mesh element on this processor 00966 const MeshBase& mesh = this->get_mesh(); 00967 00968 MeshBase::const_element_iterator el = 00969 mesh.active_local_elements_begin(); 00970 const MeshBase::const_element_iterator end_el = 00971 mesh.active_local_elements_end(); 00972 00973 AutoPtr<DiffContext> con = this->build_context(); 00974 FEMContext &_femcontext = libmesh_cast_ref<FEMContext&>(*con); 00975 this->init_context(_femcontext); 00976 00977 // Get the solution's mesh variables from every element 00978 for ( ; el != end_el; ++el) 00979 { 00980 _femcontext.pre_fe_reinit(*this, *el); 00981 00982 _femcontext.elem_position_get(); 00983 00984 if (_mesh_x_var != libMesh::invalid_uint) 00985 this->solution->insert(*_femcontext.elem_subsolutions[_mesh_x_var], 00986 _femcontext.dof_indices_var[_mesh_x_var]); 00987 if (_mesh_y_var != libMesh::invalid_uint) 00988 this->solution->insert(*_femcontext.elem_subsolutions[_mesh_y_var], 00989 _femcontext.dof_indices_var[_mesh_y_var]); 00990 if (_mesh_z_var != libMesh::invalid_uint) 00991 this->solution->insert(*_femcontext.elem_subsolutions[_mesh_z_var], 00992 _femcontext.dof_indices_var[_mesh_z_var]); 00993 } 00994 00995 this->solution->close(); 00996 00997 // And make sure the current_local_solution is up to date too 00998 this->System::update(); 00999 } 01000 01001 } // namespace libMesh
Site Created By: libMesh Developers
Last modified: February 05 2013 19:54:47 UTC
Hosted By: