$extrastylesheet
00001 // The libMesh Finite Element Library. 00002 // Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner 00003 00004 // This library is free software; you can redistribute it and/or 00005 // modify it under the terms of the GNU Lesser General Public 00006 // License as published by the Free Software Foundation; either 00007 // version 2.1 of the License, or (at your option) any later version. 00008 00009 // This library is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 // Lesser General Public License for more details. 00013 00014 // You should have received a copy of the GNU Lesser General Public 00015 // License along with this library; if not, write to the Free Software 00016 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00017 00018 // C++ Includes ------------------------------------- 00019 #include <set> 00020 #include <algorithm> // for std::count, std::fill 00021 #include <sstream> 00022 #include <cstdlib> // *must* precede <cmath> for proper std:abs() on PGI, Sun Studio CC 00023 #include <cmath> 00024 00025 // Local Includes ----------------------------------- 00026 #include "libmesh/boundary_info.h" // needed for dirichlet constraints 00027 #include "libmesh/dense_matrix.h" 00028 #include "libmesh/dense_vector.h" 00029 #include "libmesh/dirichlet_boundaries.h" 00030 #include "libmesh/dof_map.h" 00031 #include "libmesh/elem.h" 00032 #include "libmesh/elem_range.h" 00033 #include "libmesh/fe_base.h" 00034 #include "libmesh/fe_interface.h" 00035 #include "libmesh/fe_type.h" 00036 #include "libmesh/libmesh_logging.h" 00037 #include "libmesh/mesh_base.h" 00038 #include "libmesh/mesh_inserter_iterator.h" 00039 #include "libmesh/numeric_vector.h" // for enforce_constraints_exactly() 00040 #include "libmesh/parallel.h" 00041 #include "libmesh/parallel_algebra.h" 00042 #include "libmesh/parallel_elem.h" 00043 #include "libmesh/parallel_node.h" 00044 #include "libmesh/periodic_boundaries.h" 00045 #include "libmesh/periodic_boundary.h" 00046 #include "libmesh/periodic_boundary_base.h" 00047 #include "libmesh/point_locator_base.h" 00048 #include "libmesh/quadrature.h" // for dirichlet constraints 00049 #include "libmesh/raw_accessor.h" 00050 #include "libmesh/sparse_matrix.h" // needed to constrain adjoint rhs 00051 #include "libmesh/system.h" // needed by enforce_constraints_exactly() 00052 #include "libmesh/threads.h" 00053 #include "libmesh/tensor_tools.h" 00054 00055 00056 // Anonymous namespace to hold helper classes 00057 namespace { 00058 00059 using namespace libMesh; 00060 00061 class ComputeConstraints 00062 { 00063 public: 00064 ComputeConstraints (DofConstraints &constraints, 00065 DofMap &dof_map, 00066 #ifdef LIBMESH_ENABLE_PERIODIC 00067 PeriodicBoundaries &periodic_boundaries, 00068 #endif 00069 const MeshBase &mesh, 00070 const unsigned int variable_number) : 00071 _constraints(constraints), 00072 _dof_map(dof_map), 00073 #ifdef LIBMESH_ENABLE_PERIODIC 00074 _periodic_boundaries(periodic_boundaries), 00075 #endif 00076 _mesh(mesh), 00077 _variable_number(variable_number) 00078 {} 00079 00080 void operator()(const ConstElemRange &range) const 00081 { 00082 const Variable &var_description = _dof_map.variable(_variable_number); 00083 00084 #ifdef LIBMESH_ENABLE_PERIODIC 00085 UniquePtr<PointLocatorBase> point_locator; 00086 const bool have_periodic_boundaries = 00087 !_periodic_boundaries.empty(); 00088 if (have_periodic_boundaries && !range.empty()) 00089 point_locator = _mesh.sub_point_locator(); 00090 #endif 00091 00092 for (ConstElemRange::const_iterator it = range.begin(); it!=range.end(); ++it) 00093 if (var_description.active_on_subdomain((*it)->subdomain_id())) 00094 { 00095 #ifdef LIBMESH_ENABLE_AMR 00096 FEInterface::compute_constraints (_constraints, 00097 _dof_map, 00098 _variable_number, 00099 *it); 00100 #endif 00101 #ifdef LIBMESH_ENABLE_PERIODIC 00102 // FIXME: periodic constraints won't work on a non-serial 00103 // mesh unless it's kept ghost elements from opposing 00104 // boundaries! 00105 if (have_periodic_boundaries) 00106 FEInterface::compute_periodic_constraints (_constraints, 00107 _dof_map, 00108 _periodic_boundaries, 00109 _mesh, 00110 point_locator.get(), 00111 _variable_number, 00112 *it); 00113 #endif 00114 } 00115 } 00116 00117 private: 00118 DofConstraints &_constraints; 00119 DofMap &_dof_map; 00120 #ifdef LIBMESH_ENABLE_PERIODIC 00121 PeriodicBoundaries &_periodic_boundaries; 00122 #endif 00123 const MeshBase &_mesh; 00124 const unsigned int _variable_number; 00125 }; 00126 00127 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 00128 class ComputeNodeConstraints 00129 { 00130 public: 00131 ComputeNodeConstraints (NodeConstraints &node_constraints, 00132 DofMap &dof_map, 00133 #ifdef LIBMESH_ENABLE_PERIODIC 00134 PeriodicBoundaries &periodic_boundaries, 00135 #endif 00136 const MeshBase &mesh) : 00137 _node_constraints(node_constraints), 00138 _dof_map(dof_map), 00139 #ifdef LIBMESH_ENABLE_PERIODIC 00140 _periodic_boundaries(periodic_boundaries), 00141 #endif 00142 _mesh(mesh) 00143 {} 00144 00145 void operator()(const ConstElemRange &range) const 00146 { 00147 #ifdef LIBMESH_ENABLE_PERIODIC 00148 UniquePtr<PointLocatorBase> point_locator; 00149 bool have_periodic_boundaries = !_periodic_boundaries.empty(); 00150 if (have_periodic_boundaries && !range.empty()) 00151 point_locator = _mesh.sub_point_locator(); 00152 #endif 00153 00154 for (ConstElemRange::const_iterator it = range.begin(); it!=range.end(); ++it) 00155 { 00156 #ifdef LIBMESH_ENABLE_AMR 00157 FEBase::compute_node_constraints (_node_constraints, *it); 00158 #endif 00159 #ifdef LIBMESH_ENABLE_PERIODIC 00160 // FIXME: periodic constraints won't work on a non-serial 00161 // mesh unless it's kept ghost elements from opposing 00162 // boundaries! 00163 if (have_periodic_boundaries) 00164 FEBase::compute_periodic_node_constraints (_node_constraints, 00165 _periodic_boundaries, 00166 _mesh, 00167 point_locator.get(), 00168 *it); 00169 #endif 00170 } 00171 } 00172 00173 private: 00174 NodeConstraints &_node_constraints; 00175 DofMap &_dof_map; 00176 #ifdef LIBMESH_ENABLE_PERIODIC 00177 PeriodicBoundaries &_periodic_boundaries; 00178 #endif 00179 const MeshBase &_mesh; 00180 }; 00181 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS 00182 00183 00184 #ifdef LIBMESH_ENABLE_DIRICHLET 00185 00189 class AddConstraint 00190 { 00191 protected: 00192 DofMap &dof_map; 00193 00194 public: 00195 AddConstraint(DofMap &dof_map_in) : dof_map(dof_map_in) {} 00196 00197 virtual void operator()(dof_id_type dof_number, 00198 const DofConstraintRow& constraint_row, 00199 const Number constraint_rhs) const = 0; 00200 }; 00201 00202 class AddPrimalConstraint : public AddConstraint 00203 { 00204 public: 00205 AddPrimalConstraint(DofMap &dof_map_in) : AddConstraint(dof_map_in) {} 00206 00207 virtual void operator()(dof_id_type dof_number, 00208 const DofConstraintRow& constraint_row, 00209 const Number constraint_rhs) const 00210 { 00211 if (!dof_map.is_constrained_dof(dof_number)) 00212 dof_map.add_constraint_row (dof_number, constraint_row, 00213 constraint_rhs, true); 00214 } 00215 }; 00216 00217 class AddAdjointConstraint : public AddConstraint 00218 { 00219 private: 00220 const unsigned int qoi_index; 00221 00222 public: 00223 AddAdjointConstraint(DofMap &dof_map_in, unsigned int qoi_index_in) 00224 : AddConstraint(dof_map_in), qoi_index(qoi_index_in) {} 00225 00226 virtual void operator()(dof_id_type dof_number, 00227 const DofConstraintRow& constraint_row, 00228 const Number constraint_rhs) const 00229 { 00230 dof_map.add_adjoint_constraint_row 00231 (qoi_index, dof_number, constraint_row, constraint_rhs, 00232 true); 00233 } 00234 }; 00235 00236 00237 00243 class ConstrainDirichlet 00244 { 00245 private: 00246 DofMap &dof_map; 00247 const MeshBase &mesh; 00248 const Real time; 00249 const DirichletBoundary dirichlet; 00250 00251 const AddConstraint &add_fn; 00252 00253 static Number f_component (FunctionBase<Number> *f, 00254 FEMFunctionBase<Number> *f_fem, 00255 const FEMContext* c, 00256 unsigned int i, 00257 const Point& p, 00258 Real time) 00259 { 00260 if (f_fem) 00261 { 00262 if (c) 00263 return f_fem->component(*c, i, p, time); 00264 else 00265 return std::numeric_limits<Real>::quiet_NaN(); 00266 } 00267 return f->component(i, p, time); 00268 } 00269 00270 static Gradient g_component (FunctionBase<Gradient> *g, 00271 FEMFunctionBase<Gradient> *g_fem, 00272 const FEMContext* c, 00273 unsigned int i, 00274 const Point& p, 00275 Real time) 00276 { 00277 if (g_fem) 00278 { 00279 if (c) 00280 return g_fem->component(*c, i, p, time); 00281 else 00282 return std::numeric_limits<Real>::quiet_NaN(); 00283 } 00284 return g->component(i, p, time); 00285 } 00286 00287 template<typename OutputType> 00288 void apply_dirichlet_impl( const ConstElemRange &range, 00289 const unsigned int var, const Variable&variable, 00290 const FEType& fe_type ) const 00291 { 00292 typedef OutputType OutputShape; 00293 typedef typename TensorTools::IncrementRank<OutputShape>::type OutputGradient; 00294 //typedef typename TensorTools::IncrementRank<OutputGradient>::type OutputTensor; 00295 typedef typename TensorTools::MakeNumber<OutputShape>::type OutputNumber; 00296 typedef typename TensorTools::IncrementRank<OutputNumber>::type OutputNumberGradient; 00297 //typedef typename TensorTools::IncrementRank<OutputNumberGradient>::type OutputNumberTensor; 00298 00299 FunctionBase<Number> *f = dirichlet.f.get(); 00300 FunctionBase<Gradient> *g = dirichlet.g.get(); 00301 00302 FEMFunctionBase<Number> *f_fem = dirichlet.f_fem.get(); 00303 FEMFunctionBase<Gradient> *g_fem = dirichlet.g_fem.get(); 00304 00305 const System *f_system = dirichlet.f_system; 00306 00307 const std::set<boundary_id_type> &b = dirichlet.b; 00308 00309 // We need data to project 00310 libmesh_assert(f || f_fem); 00311 libmesh_assert(!(f && f_fem)); 00312 00313 // Iff our data depends on a system, we should have it. 00314 libmesh_assert(!(f && f_system)); 00315 libmesh_assert(!(f_fem && !f_system)); 00316 00317 // The element matrix and RHS for projections. 00318 // Note that Ke is always real-valued, whereas 00319 // Fe may be complex valued if complex number 00320 // support is enabled 00321 DenseMatrix<Real> Ke; 00322 DenseVector<Number> Fe; 00323 // The new element coefficients 00324 DenseVector<Number> Ue; 00325 00326 // The dimensionality of the current mesh 00327 const unsigned int dim = mesh.mesh_dimension(); 00328 00329 // Boundary info for the current mesh 00330 const BoundaryInfo& boundary_info = mesh.get_boundary_info(); 00331 00332 unsigned int n_vec_dim = FEInterface::n_vec_dim(mesh, fe_type); 00333 00334 const unsigned int var_component = 00335 variable.first_scalar_number(); 00336 00337 // Get FE objects of the appropriate type 00338 UniquePtr<FEGenericBase<OutputType> > fe = FEGenericBase<OutputType>::build(dim, fe_type); 00339 00340 // Prepare variables for projection 00341 UniquePtr<QBase> qedgerule (fe_type.default_quadrature_rule(1)); 00342 UniquePtr<QBase> qsiderule (fe_type.default_quadrature_rule(dim-1)); 00343 00344 // The values of the shape functions at the quadrature 00345 // points 00346 const std::vector<std::vector<OutputShape> >& phi = fe->get_phi(); 00347 00348 // The gradients of the shape functions at the quadrature 00349 // points on the child element. 00350 const std::vector<std::vector<OutputGradient> > *dphi = NULL; 00351 00352 const FEContinuity cont = fe->get_continuity(); 00353 00354 if (cont == C_ONE) 00355 { 00356 // We'll need gradient data for a C1 projection 00357 libmesh_assert(g || g_fem); 00358 00359 // We currently demand that either neither nor both function 00360 // object depend on current FEM data. 00361 libmesh_assert(!(g && g_fem)); 00362 libmesh_assert(!(f && g_fem)); 00363 libmesh_assert(!(f_fem && g)); 00364 00365 const std::vector<std::vector<OutputGradient> >& 00366 ref_dphi = fe->get_dphi(); 00367 dphi = &ref_dphi; 00368 } 00369 00370 // The Jacobian * quadrature weight at the quadrature points 00371 const std::vector<Real>& JxW = 00372 fe->get_JxW(); 00373 00374 // The XYZ locations of the quadrature points 00375 const std::vector<Point>& xyz_values = 00376 fe->get_xyz(); 00377 00378 // The global DOF indices 00379 std::vector<dof_id_type> dof_indices; 00380 // Side/edge local DOF indices 00381 std::vector<unsigned int> side_dofs; 00382 00383 // If our supplied functions require a FEMContext, and if we have 00384 // an initialized solution to use with that FEMContext, then 00385 // create one 00386 UniquePtr<FEMContext> context; 00387 if (f_fem) 00388 { 00389 libmesh_assert(f_system); 00390 if (f_system->current_local_solution->initialized()) 00391 { 00392 context = UniquePtr<FEMContext>(new FEMContext(*f_system)); 00393 f_fem->init_context(*context); 00394 if (g_fem) 00395 g_fem->init_context(*context); 00396 } 00397 } 00398 00399 // Iterate over all the elements in the range 00400 for (ConstElemRange::const_iterator elem_it=range.begin(); elem_it != range.end(); ++elem_it) 00401 { 00402 const Elem* elem = *elem_it; 00403 00404 // We only calculate Dirichlet constraints on active 00405 // elements 00406 if (!elem->active()) 00407 continue; 00408 00409 // Per-subdomain variables don't need to be projected on 00410 // elements where they're not active 00411 if (!variable.active_on_subdomain(elem->subdomain_id())) 00412 continue; 00413 00414 // There's a chicken-and-egg problem with FEMFunction-based 00415 // Dirichlet constraints: we can't evaluate the FEMFunction 00416 // until we have an initialized local solution vector, we 00417 // can't initialize local solution vectors until we have a 00418 // send list, and we can't generate a send list until we know 00419 // all our constraints 00420 // 00421 // We don't generate constraints on uninitialized systems; 00422 // currently user code will have to reinit() before any 00423 // FEMFunction-based constraints will be correct. This should 00424 // be fine, since user code would want to reinit() after 00425 // setting initial conditions anyway. 00426 if (f_system && context.get()) 00427 context->pre_fe_reinit(*f_system, elem); 00428 00429 // Find out which nodes, edges and sides are on a requested 00430 // boundary: 00431 std::vector<bool> is_boundary_node(elem->n_nodes(), false), 00432 is_boundary_edge(elem->n_edges(), false), 00433 is_boundary_side(elem->n_sides(), false); 00434 for (unsigned char s=0; s != elem->n_sides(); ++s) 00435 { 00436 // First see if this side has been requested 00437 const std::vector<boundary_id_type>& bc_ids = 00438 boundary_info.boundary_ids (elem, s); 00439 bool do_this_side = false; 00440 for (std::size_t i=0; i != bc_ids.size(); ++i) 00441 if (b.count(bc_ids[i])) 00442 { 00443 do_this_side = true; 00444 break; 00445 } 00446 if (!do_this_side) 00447 continue; 00448 00449 is_boundary_side[s] = true; 00450 00451 // Then see what nodes and what edges are on it 00452 for (unsigned int n=0; n != elem->n_nodes(); ++n) 00453 if (elem->is_node_on_side(n,s)) 00454 is_boundary_node[n] = true; 00455 for (unsigned int e=0; e != elem->n_edges(); ++e) 00456 if (elem->is_edge_on_side(e,s)) 00457 is_boundary_edge[e] = true; 00458 } 00459 00460 // We can also impose Dirichlet boundary conditions on nodes, so we should 00461 // also independently check whether the nodes have been requested 00462 for (unsigned int n=0; n != elem->n_nodes(); ++n) 00463 { 00464 const std::vector<boundary_id_type>& bc_ids = 00465 boundary_info.boundary_ids (elem->get_node(n)); 00466 00467 for (std::size_t i=0; i != bc_ids.size(); ++i) 00468 if (b.count(bc_ids[i])) 00469 is_boundary_node[n] = true; 00470 } 00471 00472 // We can also impose Dirichlet boundary conditions on edges, so we should 00473 // also independently check whether the edges have been requested 00474 for (unsigned short e=0; e != elem->n_edges(); ++e) 00475 { 00476 const std::vector<boundary_id_type>& bc_ids = 00477 boundary_info.edge_boundary_ids (elem, e); 00478 00479 for (std::size_t i=0; i != bc_ids.size(); ++i) 00480 if (b.count(bc_ids[i])) 00481 is_boundary_edge[e] = true; 00482 } 00483 00484 // Update the DOF indices for this element based on 00485 // the current mesh 00486 dof_map.dof_indices (elem, dof_indices, var); 00487 00488 // The number of DOFs on the element 00489 const unsigned int n_dofs = 00490 cast_int<unsigned int>(dof_indices.size()); 00491 00492 // Fixed vs. free DoFs on edge/face projections 00493 std::vector<char> dof_is_fixed(n_dofs, false); // bools 00494 std::vector<int> free_dof(n_dofs, 0); 00495 00496 // The element type 00497 const ElemType elem_type = elem->type(); 00498 00499 // The number of nodes on the new element 00500 const unsigned int n_nodes = elem->n_nodes(); 00501 00502 // Zero the interpolated values 00503 Ue.resize (n_dofs); Ue.zero(); 00504 00505 // In general, we need a series of 00506 // projections to ensure a unique and continuous 00507 // solution. We start by interpolating boundary nodes, then 00508 // hold those fixed and project boundary edges, then hold 00509 // those fixed and project boundary faces, 00510 00511 // Interpolate node values first 00512 unsigned int current_dof = 0; 00513 for (unsigned int n=0; n!= n_nodes; ++n) 00514 { 00515 // FIXME: this should go through the DofMap, 00516 // not duplicate dof_indices code badly! 00517 const unsigned int nc = 00518 FEInterface::n_dofs_at_node (dim, fe_type, elem_type, 00519 n); 00520 if (!elem->is_vertex(n) || !is_boundary_node[n]) 00521 { 00522 current_dof += nc; 00523 continue; 00524 } 00525 if (cont == DISCONTINUOUS) 00526 { 00527 libmesh_assert_equal_to (nc, 0); 00528 } 00529 // Assume that C_ZERO elements have a single nodal 00530 // value shape function 00531 else if (cont == C_ZERO) 00532 { 00533 libmesh_assert_equal_to (nc, n_vec_dim); 00534 for( unsigned int c = 0; c < n_vec_dim; c++ ) 00535 { 00536 Ue(current_dof+c) = 00537 f_component(f, f_fem, context.get(), var_component+c, 00538 elem->point(n), time); 00539 dof_is_fixed[current_dof+c] = true; 00540 } 00541 current_dof += n_vec_dim; 00542 } 00543 // The hermite element vertex shape functions are weird 00544 else if (fe_type.family == HERMITE) 00545 { 00546 Ue(current_dof) = 00547 f_component(f, f_fem, context.get(), var_component, 00548 elem->point(n), time); 00549 dof_is_fixed[current_dof] = true; 00550 current_dof++; 00551 Gradient grad = 00552 g_component(g, g_fem, context.get(), var_component, 00553 elem->point(n), time); 00554 // x derivative 00555 Ue(current_dof) = grad(0); 00556 dof_is_fixed[current_dof] = true; 00557 current_dof++; 00558 if (dim > 1) 00559 { 00560 // We'll finite difference mixed derivatives 00561 Point nxminus = elem->point(n), 00562 nxplus = elem->point(n); 00563 nxminus(0) -= TOLERANCE; 00564 nxplus(0) += TOLERANCE; 00565 Gradient gxminus = 00566 g_component(g, g_fem, context.get(), var_component, 00567 nxminus, time); 00568 Gradient gxplus = 00569 g_component(g, g_fem, context.get(), var_component, 00570 nxplus, time); 00571 // y derivative 00572 Ue(current_dof) = grad(1); 00573 dof_is_fixed[current_dof] = true; 00574 current_dof++; 00575 // xy derivative 00576 Ue(current_dof) = (gxplus(1) - gxminus(1)) 00577 / 2. / TOLERANCE; 00578 dof_is_fixed[current_dof] = true; 00579 current_dof++; 00580 00581 if (dim > 2) 00582 { 00583 // z derivative 00584 Ue(current_dof) = grad(2); 00585 dof_is_fixed[current_dof] = true; 00586 current_dof++; 00587 // xz derivative 00588 Ue(current_dof) = (gxplus(2) - gxminus(2)) 00589 / 2. / TOLERANCE; 00590 dof_is_fixed[current_dof] = true; 00591 current_dof++; 00592 // We need new points for yz 00593 Point nyminus = elem->point(n), 00594 nyplus = elem->point(n); 00595 nyminus(1) -= TOLERANCE; 00596 nyplus(1) += TOLERANCE; 00597 Gradient gyminus = 00598 g_component(g, g_fem, context.get(), var_component, 00599 nyminus, time); 00600 Gradient gyplus = 00601 g_component(g, g_fem, context.get(), var_component, 00602 nyplus, time); 00603 // xz derivative 00604 Ue(current_dof) = (gyplus(2) - gyminus(2)) 00605 / 2. / TOLERANCE; 00606 dof_is_fixed[current_dof] = true; 00607 current_dof++; 00608 // Getting a 2nd order xyz is more tedious 00609 Point nxmym = elem->point(n), 00610 nxmyp = elem->point(n), 00611 nxpym = elem->point(n), 00612 nxpyp = elem->point(n); 00613 nxmym(0) -= TOLERANCE; 00614 nxmym(1) -= TOLERANCE; 00615 nxmyp(0) -= TOLERANCE; 00616 nxmyp(1) += TOLERANCE; 00617 nxpym(0) += TOLERANCE; 00618 nxpym(1) -= TOLERANCE; 00619 nxpyp(0) += TOLERANCE; 00620 nxpyp(1) += TOLERANCE; 00621 Gradient gxmym = 00622 g_component(g, g_fem, context.get(), var_component, 00623 nxmym, time); 00624 Gradient gxmyp = 00625 g_component(g, g_fem, context.get(), var_component, 00626 nxmyp, time); 00627 Gradient gxpym = 00628 g_component(g, g_fem, context.get(), var_component, 00629 nxpym, time); 00630 Gradient gxpyp = 00631 g_component(g, g_fem, context.get(), var_component, 00632 nxpyp, time); 00633 Number gxzplus = (gxpyp(2) - gxmyp(2)) 00634 / 2. / TOLERANCE; 00635 Number gxzminus = (gxpym(2) - gxmym(2)) 00636 / 2. / TOLERANCE; 00637 // xyz derivative 00638 Ue(current_dof) = (gxzplus - gxzminus) 00639 / 2. / TOLERANCE; 00640 dof_is_fixed[current_dof] = true; 00641 current_dof++; 00642 } 00643 } 00644 } 00645 // Assume that other C_ONE elements have a single nodal 00646 // value shape function and nodal gradient component 00647 // shape functions 00648 else if (cont == C_ONE) 00649 { 00650 libmesh_assert_equal_to (nc, 1 + dim); 00651 Ue(current_dof) = 00652 f_component(f, f_fem, context.get(), var_component, 00653 elem->point(n), time); 00654 dof_is_fixed[current_dof] = true; 00655 current_dof++; 00656 Gradient grad = 00657 g_component(g, g_fem, context.get(), var_component, 00658 elem->point(n), time); 00659 for (unsigned int i=0; i!= dim; ++i) 00660 { 00661 Ue(current_dof) = grad(i); 00662 dof_is_fixed[current_dof] = true; 00663 current_dof++; 00664 } 00665 } 00666 else 00667 libmesh_error_msg("Unknown continuity cont = " << cont); 00668 } 00669 00670 // In 3D, project any edge values next 00671 if (dim > 2 && cont != DISCONTINUOUS) 00672 for (unsigned int e=0; e != elem->n_edges(); ++e) 00673 { 00674 if (!is_boundary_edge[e]) 00675 continue; 00676 00677 FEInterface::dofs_on_edge(elem, dim, fe_type, e, 00678 side_dofs); 00679 00680 // Some edge dofs are on nodes and already 00681 // fixed, others are free to calculate 00682 unsigned int free_dofs = 0; 00683 for (unsigned int i=0; i != side_dofs.size(); ++i) 00684 if (!dof_is_fixed[side_dofs[i]]) 00685 free_dof[free_dofs++] = i; 00686 00687 // There may be nothing to project 00688 if (!free_dofs) 00689 continue; 00690 00691 Ke.resize (free_dofs, free_dofs); Ke.zero(); 00692 Fe.resize (free_dofs); Fe.zero(); 00693 // The new edge coefficients 00694 DenseVector<Number> Uedge(free_dofs); 00695 00696 // Initialize FE data on the edge 00697 fe->attach_quadrature_rule (qedgerule.get()); 00698 fe->edge_reinit (elem, e); 00699 const unsigned int n_qp = qedgerule->n_points(); 00700 00701 // Loop over the quadrature points 00702 for (unsigned int qp=0; qp<n_qp; qp++) 00703 { 00704 // solution at the quadrature point 00705 OutputNumber fineval = 0; 00706 libMesh::RawAccessor<OutputNumber> f_accessor( fineval, dim ); 00707 00708 for( unsigned int c = 0; c < n_vec_dim; c++) 00709 f_accessor(c) = 00710 f_component(f, f_fem, context.get(), var_component+c, 00711 xyz_values[qp], time); 00712 00713 // solution grad at the quadrature point 00714 OutputNumberGradient finegrad; 00715 libMesh::RawAccessor<OutputNumberGradient> g_accessor( finegrad, dim ); 00716 00717 unsigned int g_rank; 00718 switch( FEInterface::field_type( fe_type ) ) 00719 { 00720 case TYPE_SCALAR: 00721 { 00722 g_rank = 1; 00723 break; 00724 } 00725 case TYPE_VECTOR: 00726 { 00727 g_rank = 2; 00728 break; 00729 } 00730 default: 00731 libmesh_error_msg("Unknown field type!"); 00732 } 00733 00734 if (cont == C_ONE) 00735 for( unsigned int c = 0; c < n_vec_dim; c++) 00736 for( unsigned int d = 0; d < g_rank; d++ ) 00737 g_accessor(c + d*dim ) = 00738 g_component(g, g_fem, context.get(), var_component, 00739 xyz_values[qp], time)(c); 00740 00741 // Form edge projection matrix 00742 for (unsigned int sidei=0, freei=0; 00743 sidei != side_dofs.size(); ++sidei) 00744 { 00745 unsigned int i = side_dofs[sidei]; 00746 // fixed DoFs aren't test functions 00747 if (dof_is_fixed[i]) 00748 continue; 00749 for (unsigned int sidej=0, freej=0; 00750 sidej != side_dofs.size(); ++sidej) 00751 { 00752 unsigned int j = side_dofs[sidej]; 00753 if (dof_is_fixed[j]) 00754 Fe(freei) -= phi[i][qp] * phi[j][qp] * 00755 JxW[qp] * Ue(j); 00756 else 00757 Ke(freei,freej) += phi[i][qp] * 00758 phi[j][qp] * JxW[qp]; 00759 if (cont == C_ONE) 00760 { 00761 if (dof_is_fixed[j]) 00762 Fe(freei) -= ((*dphi)[i][qp].contract((*dphi)[j][qp]) ) * 00763 JxW[qp] * Ue(j); 00764 else 00765 Ke(freei,freej) += ((*dphi)[i][qp].contract((*dphi)[j][qp])) 00766 * JxW[qp]; 00767 } 00768 if (!dof_is_fixed[j]) 00769 freej++; 00770 } 00771 Fe(freei) += phi[i][qp] * fineval * JxW[qp]; 00772 if (cont == C_ONE) 00773 Fe(freei) += (finegrad.contract( (*dphi)[i][qp]) ) * 00774 JxW[qp]; 00775 freei++; 00776 } 00777 } 00778 00779 Ke.cholesky_solve(Fe, Uedge); 00780 00781 // Transfer new edge solutions to element 00782 for (unsigned int i=0; i != free_dofs; ++i) 00783 { 00784 Number &ui = Ue(side_dofs[free_dof[i]]); 00785 libmesh_assert(std::abs(ui) < TOLERANCE || 00786 std::abs(ui - Uedge(i)) < TOLERANCE); 00787 ui = Uedge(i); 00788 dof_is_fixed[side_dofs[free_dof[i]]] = true; 00789 } 00790 } 00791 00792 // Project any side values (edges in 2D, faces in 3D) 00793 if (dim > 1 && cont != DISCONTINUOUS) 00794 for (unsigned int s=0; s != elem->n_sides(); ++s) 00795 { 00796 if (!is_boundary_side[s]) 00797 continue; 00798 00799 FEInterface::dofs_on_side(elem, dim, fe_type, s, 00800 side_dofs); 00801 00802 // Some side dofs are on nodes/edges and already 00803 // fixed, others are free to calculate 00804 unsigned int free_dofs = 0; 00805 for (unsigned int i=0; i != side_dofs.size(); ++i) 00806 if (!dof_is_fixed[side_dofs[i]]) 00807 free_dof[free_dofs++] = i; 00808 00809 // There may be nothing to project 00810 if (!free_dofs) 00811 continue; 00812 00813 Ke.resize (free_dofs, free_dofs); Ke.zero(); 00814 Fe.resize (free_dofs); Fe.zero(); 00815 // The new side coefficients 00816 DenseVector<Number> Uside(free_dofs); 00817 00818 // Initialize FE data on the side 00819 fe->attach_quadrature_rule (qsiderule.get()); 00820 fe->reinit (elem, s); 00821 const unsigned int n_qp = qsiderule->n_points(); 00822 00823 // Loop over the quadrature points 00824 for (unsigned int qp=0; qp<n_qp; qp++) 00825 { 00826 // solution at the quadrature point 00827 OutputNumber fineval = 0; 00828 libMesh::RawAccessor<OutputNumber> f_accessor( fineval, dim ); 00829 00830 for( unsigned int c = 0; c < n_vec_dim; c++) 00831 f_accessor(c) = 00832 f_component(f, f_fem, context.get(), var_component+c, 00833 xyz_values[qp], time); 00834 00835 // solution grad at the quadrature point 00836 OutputNumberGradient finegrad; 00837 libMesh::RawAccessor<OutputNumberGradient> g_accessor( finegrad, dim ); 00838 00839 unsigned int g_rank; 00840 switch( FEInterface::field_type( fe_type ) ) 00841 { 00842 case TYPE_SCALAR: 00843 { 00844 g_rank = 1; 00845 break; 00846 } 00847 case TYPE_VECTOR: 00848 { 00849 g_rank = 2; 00850 break; 00851 } 00852 default: 00853 libmesh_error_msg("Unknown field type!"); 00854 } 00855 00856 if (cont == C_ONE) 00857 for( unsigned int c = 0; c < n_vec_dim; c++) 00858 for( unsigned int d = 0; d < g_rank; d++ ) 00859 g_accessor(c + d*dim ) = 00860 g_component(g, g_fem, context.get(), var_component, 00861 xyz_values[qp], time)(c); 00862 00863 // Form side projection matrix 00864 for (unsigned int sidei=0, freei=0; 00865 sidei != side_dofs.size(); ++sidei) 00866 { 00867 unsigned int i = side_dofs[sidei]; 00868 // fixed DoFs aren't test functions 00869 if (dof_is_fixed[i]) 00870 continue; 00871 for (unsigned int sidej=0, freej=0; 00872 sidej != side_dofs.size(); ++sidej) 00873 { 00874 unsigned int j = side_dofs[sidej]; 00875 if (dof_is_fixed[j]) 00876 Fe(freei) -= phi[i][qp] * phi[j][qp] * 00877 JxW[qp] * Ue(j); 00878 else 00879 Ke(freei,freej) += phi[i][qp] * 00880 phi[j][qp] * JxW[qp]; 00881 if (cont == C_ONE) 00882 { 00883 if (dof_is_fixed[j]) 00884 Fe(freei) -= ((*dphi)[i][qp].contract((*dphi)[j][qp])) * 00885 JxW[qp] * Ue(j); 00886 else 00887 Ke(freei,freej) += ((*dphi)[i][qp].contract((*dphi)[j][qp])) 00888 * JxW[qp]; 00889 } 00890 if (!dof_is_fixed[j]) 00891 freej++; 00892 } 00893 Fe(freei) += (fineval * phi[i][qp]) * JxW[qp]; 00894 if (cont == C_ONE) 00895 Fe(freei) += (finegrad.contract((*dphi)[i][qp])) * 00896 JxW[qp]; 00897 freei++; 00898 } 00899 } 00900 00901 Ke.cholesky_solve(Fe, Uside); 00902 00903 // Transfer new side solutions to element 00904 for (unsigned int i=0; i != free_dofs; ++i) 00905 { 00906 Number &ui = Ue(side_dofs[free_dof[i]]); 00907 libmesh_assert(std::abs(ui) < TOLERANCE || 00908 std::abs(ui - Uside(i)) < TOLERANCE); 00909 ui = Uside(i); 00910 dof_is_fixed[side_dofs[free_dof[i]]] = true; 00911 } 00912 } 00913 00914 // Lock the DofConstraints since it is shared among threads. 00915 { 00916 Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx); 00917 00918 for (unsigned int i = 0; i < n_dofs; i++) 00919 { 00920 DofConstraintRow empty_row; 00921 if (dof_is_fixed[i] && !libmesh_isnan(Ue(i))) 00922 add_fn (dof_indices[i], empty_row, Ue(i)); 00923 } 00924 } 00925 } 00926 00927 } // apply_dirichlet_impl 00928 00929 public: 00930 ConstrainDirichlet (DofMap &dof_map_in, 00931 const MeshBase &mesh_in, 00932 const Real time_in, 00933 const DirichletBoundary &dirichlet_in, 00934 const AddConstraint& add_in) : 00935 dof_map(dof_map_in), 00936 mesh(mesh_in), 00937 time(time_in), 00938 dirichlet(dirichlet_in), 00939 add_fn(add_in) { } 00940 00941 ConstrainDirichlet (const ConstrainDirichlet &in) : 00942 dof_map(in.dof_map), 00943 mesh(in.mesh), 00944 time(in.time), 00945 dirichlet(in.dirichlet), 00946 add_fn(in.add_fn) { } 00947 00948 void operator()(const ConstElemRange &range) const 00949 { 00956 // Loop over all the variables we've been requested to project 00957 for (unsigned int v=0; v!=dirichlet.variables.size(); v++) 00958 { 00959 const unsigned int var = dirichlet.variables[v]; 00960 00961 const Variable& variable = dof_map.variable(var); 00962 00963 const FEType& fe_type = variable.type(); 00964 00965 if (fe_type.family == SCALAR) 00966 continue; 00967 00968 switch( FEInterface::field_type( fe_type ) ) 00969 { 00970 case TYPE_SCALAR: 00971 { 00972 this->apply_dirichlet_impl<Real>( range, var, variable, fe_type ); 00973 break; 00974 } 00975 case TYPE_VECTOR: 00976 { 00977 this->apply_dirichlet_impl<RealGradient>( range, var, variable, fe_type ); 00978 break; 00979 } 00980 default: 00981 libmesh_error_msg("Unknown field type!"); 00982 } 00983 00984 } 00985 } 00986 00987 }; // class ConstrainDirichlet 00988 00989 00990 #endif // LIBMESH_ENABLE_DIRICHLET 00991 00992 00993 } // anonymous namespace 00994 00995 00996 00997 namespace libMesh 00998 { 00999 01000 // ------------------------------------------------------------ 01001 // DofMap member functions 01002 01003 #ifdef LIBMESH_ENABLE_CONSTRAINTS 01004 01005 01006 dof_id_type DofMap::n_constrained_dofs() const 01007 { 01008 parallel_object_only(); 01009 01010 dof_id_type nc_dofs = this->n_local_constrained_dofs(); 01011 this->comm().sum(nc_dofs); 01012 return nc_dofs; 01013 } 01014 01015 01016 dof_id_type DofMap::n_local_constrained_dofs() const 01017 { 01018 const DofConstraints::const_iterator lower = 01019 _dof_constraints.lower_bound(this->first_dof()), 01020 upper = 01021 _dof_constraints.upper_bound(this->end_dof()-1); 01022 01023 return cast_int<dof_id_type>(std::distance(lower, upper)); 01024 } 01025 01026 01027 01028 void DofMap::create_dof_constraints(const MeshBase& mesh, Real time) 01029 { 01030 parallel_object_only(); 01031 01032 START_LOG("create_dof_constraints()", "DofMap"); 01033 01034 libmesh_assert (mesh.is_prepared()); 01035 01036 // We might get constraint equations from AMR hanging nodes in 2D/3D 01037 // or from boundary conditions in any dimension 01038 const bool possible_local_constraints = false 01039 #ifdef LIBMESH_ENABLE_AMR 01040 || mesh.mesh_dimension() > 1 01041 #endif 01042 #ifdef LIBMESH_ENABLE_PERIODIC 01043 || !_periodic_boundaries->empty() 01044 #endif 01045 #ifdef LIBMESH_ENABLE_DIRICHLET 01046 || !_dirichlet_boundaries->empty() 01047 #endif 01048 ; 01049 01050 // Even if we don't have constraints, another processor might. 01051 bool possible_global_constraints = possible_local_constraints; 01052 #if defined(LIBMESH_ENABLE_PERIODIC) || defined(LIBMESH_ENABLE_DIRICHLET) || defined(LIBMESH_ENABLE_AMR) 01053 libmesh_assert(this->comm().verify(mesh.is_serial())); 01054 01055 this->comm().max(possible_global_constraints); 01056 #endif 01057 01058 if (!possible_global_constraints) 01059 { 01060 // Clear out any old constraints; maybe the user just deleted 01061 // their last remaining dirichlet/periodic/user constraint? 01062 #ifdef LIBMESH_ENABLE_CONSTRAINTS 01063 _dof_constraints.clear(); 01064 _stashed_dof_constraints.clear(); 01065 _primal_constraint_values.clear(); 01066 _adjoint_constraint_values.clear(); 01067 #endif 01068 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 01069 _node_constraints.clear(); 01070 #endif 01071 01072 // make sure we stop logging though 01073 STOP_LOG("create_dof_constraints()", "DofMap"); 01074 return; 01075 } 01076 01077 // Here we build the hanging node constraints. This is done 01078 // by enforcing the condition u_a = u_b along hanging sides. 01079 // u_a = u_b is collocated at the nodes of side a, which gives 01080 // one row of the constraint matrix. 01081 01082 // Processors only compute their local constraints 01083 ConstElemRange range (mesh.local_elements_begin(), 01084 mesh.local_elements_end()); 01085 01086 // Global computation fails if we're using a FEMFunctionBase BC on a 01087 // SerialMesh in parallel 01088 // ConstElemRange range (mesh.elements_begin(), 01089 // mesh.elements_end()); 01090 01091 // compute_periodic_constraints requires a point_locator() from our 01092 // Mesh, but point_locator() construction is parallel and threaded. 01093 // Rather than nest threads within threads we'll make sure it's 01094 // preconstructed. 01095 #ifdef LIBMESH_ENABLE_PERIODIC 01096 bool need_point_locator = !_periodic_boundaries->empty() && !range.empty(); 01097 01098 this->comm().max(need_point_locator); 01099 01100 if (need_point_locator) 01101 mesh.sub_point_locator(); 01102 #endif 01103 01104 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 01105 // recalculate node constraints from scratch 01106 _node_constraints.clear(); 01107 01108 Threads::parallel_for (range, 01109 ComputeNodeConstraints (_node_constraints, 01110 *this, 01111 #ifdef LIBMESH_ENABLE_PERIODIC 01112 *_periodic_boundaries, 01113 #endif 01114 mesh)); 01115 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS 01116 01117 01118 // recalculate dof constraints from scratch 01119 _dof_constraints.clear(); 01120 _stashed_dof_constraints.clear(); 01121 _primal_constraint_values.clear(); 01122 _adjoint_constraint_values.clear(); 01123 01124 // Look at all the variables in the system. Reset the element 01125 // range at each iteration -- there is no need to reconstruct it. 01126 for (unsigned int variable_number=0; variable_number<this->n_variables(); 01127 ++variable_number, range.reset()) 01128 Threads::parallel_for (range, 01129 ComputeConstraints (_dof_constraints, 01130 *this, 01131 #ifdef LIBMESH_ENABLE_PERIODIC 01132 *_periodic_boundaries, 01133 #endif 01134 mesh, 01135 variable_number)); 01136 01137 #ifdef LIBMESH_ENABLE_DIRICHLET 01138 for (DirichletBoundaries::iterator 01139 i = _dirichlet_boundaries->begin(); 01140 i != _dirichlet_boundaries->end(); ++i, range.reset()) 01141 { 01142 Threads::parallel_for 01143 (range, 01144 ConstrainDirichlet(*this, mesh, time, **i, 01145 AddPrimalConstraint(*this)) 01146 ); 01147 } 01148 01149 for (unsigned int qoi_index = 0; 01150 qoi_index != _adjoint_dirichlet_boundaries.size(); 01151 ++qoi_index) 01152 { 01153 for (DirichletBoundaries::iterator 01154 i = _adjoint_dirichlet_boundaries[qoi_index]->begin(); 01155 i != _adjoint_dirichlet_boundaries[qoi_index]->end(); 01156 ++i, range.reset()) 01157 { 01158 Threads::parallel_for 01159 (range, 01160 ConstrainDirichlet(*this, mesh, time, **i, 01161 AddAdjointConstraint(*this, qoi_index)) 01162 ); 01163 } 01164 } 01165 01166 #endif // LIBMESH_ENABLE_DIRICHLET 01167 01168 STOP_LOG("create_dof_constraints()", "DofMap"); 01169 } 01170 01171 01172 01173 void DofMap::add_constraint_row (const dof_id_type dof_number, 01174 const DofConstraintRow& constraint_row, 01175 const Number constraint_rhs, 01176 const bool forbid_constraint_overwrite) 01177 { 01178 // Optionally allow the user to overwrite constraints. Defaults to false. 01179 if (forbid_constraint_overwrite) 01180 if (this->is_constrained_dof(dof_number)) 01181 libmesh_error_msg("ERROR: DOF " << dof_number << " was already constrained!"); 01182 01183 _dof_constraints.insert(std::make_pair(dof_number, constraint_row)); 01184 _primal_constraint_values.insert(std::make_pair(dof_number, constraint_rhs)); 01185 } 01186 01187 01188 void DofMap::add_adjoint_constraint_row (const unsigned int qoi_index, 01189 const dof_id_type dof_number, 01190 const DofConstraintRow& /*constraint_row*/, 01191 const Number constraint_rhs, 01192 const bool forbid_constraint_overwrite) 01193 { 01194 // Optionally allow the user to overwrite constraints. Defaults to false. 01195 if (forbid_constraint_overwrite) 01196 { 01197 if (!this->is_constrained_dof(dof_number)) 01198 libmesh_error_msg("ERROR: DOF " << dof_number << " has no corresponding primal constraint!"); 01199 #ifndef NDEBUG 01200 // No way to do this without a non-normalized tolerance? 01201 /* 01202 // If the user passed in more than just the rhs, let's check the 01203 // coefficients for consistency 01204 if (!constraint_row.empty()) 01205 { 01206 DofConstraintRow row = _dof_constraints[dof_number]; 01207 for (DofConstraintRow::const_iterator pos=row.begin(); 01208 pos != row.end(); ++pos) 01209 { 01210 libmesh_assert(constraint_row.find(pos->first)->second 01211 == pos->second); 01212 } 01213 } 01214 01215 if (_adjoint_constraint_values[qoi_index].find(dof_number) != 01216 _adjoint_constraint_values[qoi_index].end()) 01217 libmesh_assert_equal_to 01218 (_adjoint_constraint_values[qoi_index][dof_number], 01219 constraint_rhs); 01220 */ 01221 #endif 01222 } 01223 01224 // Creates the map of rhs values if it doesn't already exist; then 01225 // adds the current value to that map 01226 _adjoint_constraint_values[qoi_index].insert(std::make_pair(dof_number, constraint_rhs)); 01227 } 01228 01229 01230 01231 01232 void DofMap::print_dof_constraints(std::ostream& os, 01233 bool print_nonlocal) const 01234 { 01235 parallel_object_only(); 01236 01237 std::string local_constraints = 01238 this->get_local_constraints(print_nonlocal); 01239 01240 if (this->processor_id()) 01241 { 01242 this->comm().send(0, local_constraints); 01243 } 01244 else 01245 { 01246 os << "Processor 0:\n"; 01247 os << local_constraints; 01248 01249 for (processor_id_type i=1; i<this->n_processors(); ++i) 01250 { 01251 this->comm().receive(i, local_constraints); 01252 os << "Processor " << i << ":\n"; 01253 os << local_constraints; 01254 } 01255 } 01256 } 01257 01258 std::string DofMap::get_local_constraints(bool print_nonlocal) const 01259 { 01260 std::ostringstream os; 01261 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 01262 if (print_nonlocal) 01263 os << "All "; 01264 else 01265 os << "Local "; 01266 01267 os << "Node Constraints:" 01268 << std::endl; 01269 01270 for (NodeConstraints::const_iterator it=_node_constraints.begin(); 01271 it != _node_constraints.end(); ++it) 01272 { 01273 const Node *node = it->first; 01274 01275 // Skip non-local nodes if requested 01276 if (!print_nonlocal && 01277 node->processor_id() != this->processor_id()) 01278 continue; 01279 01280 const NodeConstraintRow& row = it->second.first; 01281 const Point& offset = it->second.second; 01282 01283 os << "Constraints for Node id " << node->id() 01284 << ": \t"; 01285 01286 for (NodeConstraintRow::const_iterator pos=row.begin(); 01287 pos != row.end(); ++pos) 01288 os << " (" << pos->first->id() << "," 01289 << pos->second << ")\t"; 01290 01291 os << "rhs: " << offset; 01292 01293 os << std::endl; 01294 } 01295 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS 01296 01297 if (print_nonlocal) 01298 os << "All "; 01299 else 01300 os << "Local "; 01301 01302 os << "DoF Constraints:" 01303 << std::endl; 01304 01305 for (DofConstraints::const_iterator it=_dof_constraints.begin(); 01306 it != _dof_constraints.end(); ++it) 01307 { 01308 const dof_id_type i = it->first; 01309 01310 // Skip non-local dofs if requested 01311 if (!print_nonlocal && 01312 ((i < this->first_dof()) || 01313 (i >= this->end_dof()))) 01314 continue; 01315 01316 const DofConstraintRow& row = it->second; 01317 DofConstraintValueMap::const_iterator rhsit = 01318 _primal_constraint_values.find(i); 01319 const Number rhs = (rhsit == _primal_constraint_values.end()) ? 01320 0 : rhsit->second; 01321 01322 os << "Constraints for DoF " << i 01323 << ": \t"; 01324 01325 for (DofConstraintRow::const_iterator pos=row.begin(); 01326 pos != row.end(); ++pos) 01327 os << " (" << pos->first << "," 01328 << pos->second << ")\t"; 01329 01330 os << "rhs: " << rhs; 01331 01332 os << std::endl; 01333 } 01334 01335 for (unsigned int qoi_index = 0; 01336 qoi_index != _adjoint_dirichlet_boundaries.size(); 01337 ++qoi_index) 01338 { 01339 os << "Adjoint " << qoi_index << " DoF rhs values:" 01340 << std::endl; 01341 01342 AdjointDofConstraintValues::const_iterator adjoint_map_it = 01343 _adjoint_constraint_values.find(qoi_index); 01344 01345 if (adjoint_map_it != _adjoint_constraint_values.end()) 01346 for (DofConstraintValueMap::const_iterator 01347 it=adjoint_map_it->second.begin(); 01348 it != adjoint_map_it->second.end(); ++it) 01349 { 01350 const dof_id_type i = it->first; 01351 01352 // Skip non-local dofs if requested 01353 if (!print_nonlocal && 01354 ((i < this->first_dof()) || 01355 (i >= this->end_dof()))) 01356 continue; 01357 01358 const Number rhs = it->second; 01359 01360 os << "RHS for DoF " << i 01361 << ": " << rhs; 01362 01363 os << std::endl; 01364 } 01365 } 01366 01367 return os.str(); 01368 } 01369 01370 01371 01372 void DofMap::constrain_element_matrix (DenseMatrix<Number>& matrix, 01373 std::vector<dof_id_type>& elem_dofs, 01374 bool asymmetric_constraint_rows) const 01375 { 01376 libmesh_assert_equal_to (elem_dofs.size(), matrix.m()); 01377 libmesh_assert_equal_to (elem_dofs.size(), matrix.n()); 01378 01379 // check for easy return 01380 if (this->_dof_constraints.empty()) 01381 return; 01382 01383 // The constrained matrix is built up as C^T K C. 01384 DenseMatrix<Number> C; 01385 01386 01387 this->build_constraint_matrix (C, elem_dofs); 01388 01389 START_LOG("constrain_elem_matrix()", "DofMap"); 01390 01391 // It is possible that the matrix is not constrained at all. 01392 if ((C.m() == matrix.m()) && 01393 (C.n() == elem_dofs.size())) // It the matrix is constrained 01394 { 01395 // Compute the matrix-matrix-matrix product C^T K C 01396 matrix.left_multiply_transpose (C); 01397 matrix.right_multiply (C); 01398 01399 01400 libmesh_assert_equal_to (matrix.m(), matrix.n()); 01401 libmesh_assert_equal_to (matrix.m(), elem_dofs.size()); 01402 libmesh_assert_equal_to (matrix.n(), elem_dofs.size()); 01403 01404 01405 for (unsigned int i=0; i<elem_dofs.size(); i++) 01406 // If the DOF is constrained 01407 if (this->is_constrained_dof(elem_dofs[i])) 01408 { 01409 for (unsigned int j=0; j<matrix.n(); j++) 01410 matrix(i,j) = 0.; 01411 01412 matrix(i,i) = 1.; 01413 01414 if (asymmetric_constraint_rows) 01415 { 01416 DofConstraints::const_iterator 01417 pos = _dof_constraints.find(elem_dofs[i]); 01418 01419 libmesh_assert (pos != _dof_constraints.end()); 01420 01421 const DofConstraintRow& constraint_row = pos->second; 01422 01423 // This is an overzealous assertion in the presence of 01424 // heterogenous constraints: we now can constrain "u_i = c" 01425 // with no other u_j terms involved. 01426 // 01427 // libmesh_assert (!constraint_row.empty()); 01428 01429 for (DofConstraintRow::const_iterator 01430 it=constraint_row.begin(); it != constraint_row.end(); 01431 ++it) 01432 for (unsigned int j=0; j<elem_dofs.size(); j++) 01433 if (elem_dofs[j] == it->first) 01434 matrix(i,j) = -it->second; 01435 } 01436 } 01437 } // end if is constrained... 01438 01439 STOP_LOG("constrain_elem_matrix()", "DofMap"); 01440 } 01441 01442 01443 01444 void DofMap::constrain_element_matrix_and_vector (DenseMatrix<Number>& matrix, 01445 DenseVector<Number>& rhs, 01446 std::vector<dof_id_type>& elem_dofs, 01447 bool asymmetric_constraint_rows) const 01448 { 01449 libmesh_assert_equal_to (elem_dofs.size(), matrix.m()); 01450 libmesh_assert_equal_to (elem_dofs.size(), matrix.n()); 01451 libmesh_assert_equal_to (elem_dofs.size(), rhs.size()); 01452 01453 // check for easy return 01454 if (this->_dof_constraints.empty()) 01455 return; 01456 01457 // The constrained matrix is built up as C^T K C. 01458 // The constrained RHS is built up as C^T F 01459 DenseMatrix<Number> C; 01460 01461 this->build_constraint_matrix (C, elem_dofs); 01462 01463 START_LOG("cnstrn_elem_mat_vec()", "DofMap"); 01464 01465 // It is possible that the matrix is not constrained at all. 01466 if ((C.m() == matrix.m()) && 01467 (C.n() == elem_dofs.size())) // It the matrix is constrained 01468 { 01469 // Compute the matrix-matrix-matrix product C^T K C 01470 matrix.left_multiply_transpose (C); 01471 matrix.right_multiply (C); 01472 01473 01474 libmesh_assert_equal_to (matrix.m(), matrix.n()); 01475 libmesh_assert_equal_to (matrix.m(), elem_dofs.size()); 01476 libmesh_assert_equal_to (matrix.n(), elem_dofs.size()); 01477 01478 01479 for (unsigned int i=0; i<elem_dofs.size(); i++) 01480 if (this->is_constrained_dof(elem_dofs[i])) 01481 { 01482 for (unsigned int j=0; j<matrix.n(); j++) 01483 matrix(i,j) = 0.; 01484 01485 // If the DOF is constrained 01486 matrix(i,i) = 1.; 01487 01488 // This will put a nonsymmetric entry in the constraint 01489 // row to ensure that the linear system produces the 01490 // correct value for the constrained DOF. 01491 if (asymmetric_constraint_rows) 01492 { 01493 DofConstraints::const_iterator 01494 pos = _dof_constraints.find(elem_dofs[i]); 01495 01496 libmesh_assert (pos != _dof_constraints.end()); 01497 01498 const DofConstraintRow& constraint_row = pos->second; 01499 01500 // p refinement creates empty constraint rows 01501 // libmesh_assert (!constraint_row.empty()); 01502 01503 for (DofConstraintRow::const_iterator 01504 it=constraint_row.begin(); it != constraint_row.end(); 01505 ++it) 01506 for (unsigned int j=0; j<elem_dofs.size(); j++) 01507 if (elem_dofs[j] == it->first) 01508 matrix(i,j) = -it->second; 01509 } 01510 } 01511 01512 01513 // Compute the matrix-vector product C^T F 01514 DenseVector<Number> old_rhs(rhs); 01515 01516 // compute matrix/vector product 01517 C.vector_mult_transpose(rhs, old_rhs); 01518 } // end if is constrained... 01519 01520 STOP_LOG("cnstrn_elem_mat_vec()", "DofMap"); 01521 } 01522 01523 01524 01525 void DofMap::heterogenously_constrain_element_matrix_and_vector 01526 (DenseMatrix<Number>& matrix, 01527 DenseVector<Number>& rhs, 01528 std::vector<dof_id_type>& elem_dofs, 01529 bool asymmetric_constraint_rows, 01530 int qoi_index) const 01531 { 01532 libmesh_assert_equal_to (elem_dofs.size(), matrix.m()); 01533 libmesh_assert_equal_to (elem_dofs.size(), matrix.n()); 01534 libmesh_assert_equal_to (elem_dofs.size(), rhs.size()); 01535 01536 // check for easy return 01537 if (this->_dof_constraints.empty()) 01538 return; 01539 01540 // The constrained matrix is built up as C^T K C. 01541 // The constrained RHS is built up as C^T (F - K H) 01542 DenseMatrix<Number> C; 01543 DenseVector<Number> H; 01544 01545 this->build_constraint_matrix_and_vector (C, H, elem_dofs, qoi_index); 01546 01547 START_LOG("hetero_cnstrn_elem_mat_vec()", "DofMap"); 01548 01549 // It is possible that the matrix is not constrained at all. 01550 if ((C.m() == matrix.m()) && 01551 (C.n() == elem_dofs.size())) // It the matrix is constrained 01552 { 01553 // We may have rhs values to use later 01554 const DofConstraintValueMap *rhs_values = NULL; 01555 if (qoi_index < 0) 01556 rhs_values = &_primal_constraint_values; 01557 else 01558 { 01559 const AdjointDofConstraintValues::const_iterator 01560 it = _adjoint_constraint_values.find(qoi_index); 01561 if (it != _adjoint_constraint_values.end()) 01562 rhs_values = &it->second; 01563 } 01564 01565 // Compute matrix/vector product K H 01566 DenseVector<Number> KH; 01567 matrix.vector_mult(KH, H); 01568 01569 // Compute the matrix-vector product C^T (F - KH) 01570 DenseVector<Number> F_minus_KH(rhs); 01571 F_minus_KH -= KH; 01572 C.vector_mult_transpose(rhs, F_minus_KH); 01573 01574 // Compute the matrix-matrix-matrix product C^T K C 01575 matrix.left_multiply_transpose (C); 01576 matrix.right_multiply (C); 01577 01578 libmesh_assert_equal_to (matrix.m(), matrix.n()); 01579 libmesh_assert_equal_to (matrix.m(), elem_dofs.size()); 01580 libmesh_assert_equal_to (matrix.n(), elem_dofs.size()); 01581 01582 for (unsigned int i=0; i<elem_dofs.size(); i++) 01583 { 01584 const dof_id_type dof_id = elem_dofs[i]; 01585 01586 if (this->is_constrained_dof(dof_id)) 01587 { 01588 for (unsigned int j=0; j<matrix.n(); j++) 01589 matrix(i,j) = 0.; 01590 01591 // If the DOF is constrained 01592 matrix(i,i) = 1.; 01593 01594 // This will put a nonsymmetric entry in the constraint 01595 // row to ensure that the linear system produces the 01596 // correct value for the constrained DOF. 01597 if (asymmetric_constraint_rows) 01598 { 01599 DofConstraints::const_iterator 01600 pos = _dof_constraints.find(dof_id); 01601 01602 libmesh_assert (pos != _dof_constraints.end()); 01603 01604 const DofConstraintRow& constraint_row = pos->second; 01605 01606 for (DofConstraintRow::const_iterator 01607 it=constraint_row.begin(); it != constraint_row.end(); 01608 ++it) 01609 for (unsigned int j=0; j<elem_dofs.size(); j++) 01610 if (elem_dofs[j] == it->first) 01611 matrix(i,j) = -it->second; 01612 01613 if (rhs_values) 01614 { 01615 const DofConstraintValueMap::const_iterator valpos = 01616 rhs_values->find(dof_id); 01617 01618 rhs(i) = (valpos == rhs_values->end()) ? 01619 0 : valpos->second; 01620 } 01621 } 01622 else 01623 rhs(i) = 0.; 01624 } 01625 } 01626 01627 } // end if is constrained... 01628 01629 STOP_LOG("hetero_cnstrn_elem_mat_vec()", "DofMap"); 01630 } 01631 01632 01633 01634 void DofMap::heterogenously_constrain_element_vector 01635 (const DenseMatrix<Number>& matrix, 01636 DenseVector<Number>& rhs, 01637 std::vector<dof_id_type>& elem_dofs, 01638 bool asymmetric_constraint_rows, 01639 int qoi_index) const 01640 { 01641 libmesh_assert_equal_to (elem_dofs.size(), matrix.m()); 01642 libmesh_assert_equal_to (elem_dofs.size(), matrix.n()); 01643 libmesh_assert_equal_to (elem_dofs.size(), rhs.size()); 01644 01645 // check for easy return 01646 if (this->_dof_constraints.empty()) 01647 return; 01648 01649 // The constrained matrix is built up as C^T K C. 01650 // The constrained RHS is built up as C^T (F - K H) 01651 DenseMatrix<Number> C; 01652 DenseVector<Number> H; 01653 01654 this->build_constraint_matrix_and_vector (C, H, elem_dofs, qoi_index); 01655 01656 START_LOG("hetero_cnstrn_elem_vec()", "DofMap"); 01657 01658 // It is possible that the matrix is not constrained at all. 01659 if ((C.m() == matrix.m()) && 01660 (C.n() == elem_dofs.size())) // It the matrix is constrained 01661 { 01662 // We may have rhs values to use later 01663 const DofConstraintValueMap *rhs_values = NULL; 01664 if (qoi_index < 0) 01665 rhs_values = &_primal_constraint_values; 01666 else 01667 { 01668 const AdjointDofConstraintValues::const_iterator 01669 it = _adjoint_constraint_values.find(qoi_index); 01670 if (it != _adjoint_constraint_values.end()) 01671 rhs_values = &it->second; 01672 } 01673 01674 // Compute matrix/vector product K H 01675 DenseVector<Number> KH; 01676 matrix.vector_mult(KH, H); 01677 01678 // Compute the matrix-vector product C^T (F - KH) 01679 DenseVector<Number> F_minus_KH(rhs); 01680 F_minus_KH -= KH; 01681 C.vector_mult_transpose(rhs, F_minus_KH); 01682 01683 for (unsigned int i=0; i<elem_dofs.size(); i++) 01684 { 01685 const dof_id_type dof_id = elem_dofs[i]; 01686 01687 if (this->is_constrained_dof(dof_id)) 01688 { 01689 // This will put a nonsymmetric entry in the constraint 01690 // row to ensure that the linear system produces the 01691 // correct value for the constrained DOF. 01692 if (asymmetric_constraint_rows && rhs_values) 01693 { 01694 const DofConstraintValueMap::const_iterator valpos = 01695 rhs_values->find(dof_id); 01696 01697 rhs(i) = (valpos == rhs_values->end()) ? 01698 0 : valpos->second; 01699 } 01700 else 01701 rhs(i) = 0.; 01702 } 01703 } 01704 01705 } // end if is constrained... 01706 01707 STOP_LOG("hetero_cnstrn_elem_vec()", "DofMap"); 01708 } 01709 01710 01711 01712 01713 void DofMap::constrain_element_matrix (DenseMatrix<Number>& matrix, 01714 std::vector<dof_id_type>& row_dofs, 01715 std::vector<dof_id_type>& col_dofs, 01716 bool asymmetric_constraint_rows) const 01717 { 01718 libmesh_assert_equal_to (row_dofs.size(), matrix.m()); 01719 libmesh_assert_equal_to (col_dofs.size(), matrix.n()); 01720 01721 // check for easy return 01722 if (this->_dof_constraints.empty()) 01723 return; 01724 01725 // The constrained matrix is built up as R^T K C. 01726 DenseMatrix<Number> R; 01727 DenseMatrix<Number> C; 01728 01729 // Safeguard against the user passing us the same 01730 // object for row_dofs and col_dofs. If that is done 01731 // the calls to build_matrix would fail 01732 std::vector<dof_id_type> orig_row_dofs(row_dofs); 01733 std::vector<dof_id_type> orig_col_dofs(col_dofs); 01734 01735 this->build_constraint_matrix (R, orig_row_dofs); 01736 this->build_constraint_matrix (C, orig_col_dofs); 01737 01738 START_LOG("constrain_elem_matrix()", "DofMap"); 01739 01740 row_dofs = orig_row_dofs; 01741 col_dofs = orig_col_dofs; 01742 01743 01744 // It is possible that the matrix is not constrained at all. 01745 if ((R.m() == matrix.m()) && 01746 (R.n() == row_dofs.size()) && 01747 (C.m() == matrix.n()) && 01748 (C.n() == col_dofs.size())) // If the matrix is constrained 01749 { 01750 // K_constrained = R^T K C 01751 matrix.left_multiply_transpose (R); 01752 matrix.right_multiply (C); 01753 01754 01755 libmesh_assert_equal_to (matrix.m(), row_dofs.size()); 01756 libmesh_assert_equal_to (matrix.n(), col_dofs.size()); 01757 01758 01759 for (unsigned int i=0; i<row_dofs.size(); i++) 01760 if (this->is_constrained_dof(row_dofs[i])) 01761 { 01762 for (unsigned int j=0; j<matrix.n(); j++) 01763 { 01764 if(row_dofs[i] != col_dofs[j]) 01765 matrix(i,j) = 0.; 01766 else // If the DOF is constrained 01767 matrix(i,j) = 1.; 01768 } 01769 01770 if (asymmetric_constraint_rows) 01771 { 01772 DofConstraints::const_iterator 01773 pos = _dof_constraints.find(row_dofs[i]); 01774 01775 libmesh_assert (pos != _dof_constraints.end()); 01776 01777 const DofConstraintRow& constraint_row = pos->second; 01778 01779 libmesh_assert (!constraint_row.empty()); 01780 01781 for (DofConstraintRow::const_iterator 01782 it=constraint_row.begin(); it != constraint_row.end(); 01783 ++it) 01784 for (unsigned int j=0; j<col_dofs.size(); j++) 01785 if (col_dofs[j] == it->first) 01786 matrix(i,j) = -it->second; 01787 } 01788 } 01789 } // end if is constrained... 01790 01791 STOP_LOG("constrain_elem_matrix()", "DofMap"); 01792 } 01793 01794 01795 01796 void DofMap::constrain_element_vector (DenseVector<Number>& rhs, 01797 std::vector<dof_id_type>& row_dofs, 01798 bool) const 01799 { 01800 libmesh_assert_equal_to (rhs.size(), row_dofs.size()); 01801 01802 // check for easy return 01803 if (this->_dof_constraints.empty()) 01804 return; 01805 01806 // The constrained RHS is built up as R^T F. 01807 DenseMatrix<Number> R; 01808 01809 this->build_constraint_matrix (R, row_dofs); 01810 01811 START_LOG("constrain_elem_vector()", "DofMap"); 01812 01813 // It is possible that the vector is not constrained at all. 01814 if ((R.m() == rhs.size()) && 01815 (R.n() == row_dofs.size())) // if the RHS is constrained 01816 { 01817 // Compute the matrix-vector product 01818 DenseVector<Number> old_rhs(rhs); 01819 R.vector_mult_transpose(rhs, old_rhs); 01820 01821 libmesh_assert_equal_to (row_dofs.size(), rhs.size()); 01822 01823 for (unsigned int i=0; i<row_dofs.size(); i++) 01824 if (this->is_constrained_dof(row_dofs[i])) 01825 { 01826 // If the DOF is constrained 01827 libmesh_assert (_dof_constraints.find(row_dofs[i]) != _dof_constraints.end()); 01828 01829 rhs(i) = 0; 01830 } 01831 } // end if the RHS is constrained. 01832 01833 STOP_LOG("constrain_elem_vector()", "DofMap"); 01834 } 01835 01836 01837 01838 void DofMap::constrain_element_dyad_matrix (DenseVector<Number>& v, 01839 DenseVector<Number>& w, 01840 std::vector<dof_id_type>& row_dofs, 01841 bool) const 01842 { 01843 libmesh_assert_equal_to (v.size(), row_dofs.size()); 01844 libmesh_assert_equal_to (w.size(), row_dofs.size()); 01845 01846 // check for easy return 01847 if (this->_dof_constraints.empty()) 01848 return; 01849 01850 // The constrained RHS is built up as R^T F. 01851 DenseMatrix<Number> R; 01852 01853 this->build_constraint_matrix (R, row_dofs); 01854 01855 START_LOG("cnstrn_elem_dyad_mat()", "DofMap"); 01856 01857 // It is possible that the vector is not constrained at all. 01858 if ((R.m() == v.size()) && 01859 (R.n() == row_dofs.size())) // if the RHS is constrained 01860 { 01861 // Compute the matrix-vector products 01862 DenseVector<Number> old_v(v); 01863 DenseVector<Number> old_w(w); 01864 01865 // compute matrix/vector product 01866 R.vector_mult_transpose(v, old_v); 01867 R.vector_mult_transpose(w, old_w); 01868 01869 libmesh_assert_equal_to (row_dofs.size(), v.size()); 01870 libmesh_assert_equal_to (row_dofs.size(), w.size()); 01871 01872 /* Constrain only v, not w. */ 01873 01874 for (unsigned int i=0; i<row_dofs.size(); i++) 01875 if (this->is_constrained_dof(row_dofs[i])) 01876 { 01877 // If the DOF is constrained 01878 libmesh_assert (_dof_constraints.find(row_dofs[i]) != _dof_constraints.end()); 01879 01880 v(i) = 0; 01881 } 01882 } // end if the RHS is constrained. 01883 01884 STOP_LOG("cnstrn_elem_dyad_mat()", "DofMap"); 01885 } 01886 01887 01888 01889 void DofMap::constrain_nothing (std::vector<dof_id_type>& dofs) const 01890 { 01891 // check for easy return 01892 if (this->_dof_constraints.empty()) 01893 return; 01894 01895 // All the work is done by \p build_constraint_matrix. We just need 01896 // a dummy matrix. 01897 DenseMatrix<Number> R; 01898 this->build_constraint_matrix (R, dofs); 01899 } 01900 01901 01902 01903 void DofMap::enforce_constraints_exactly (const System &system, 01904 NumericVector<Number> *v, 01905 bool homogeneous) const 01906 { 01907 parallel_object_only(); 01908 01909 if (!this->n_constrained_dofs()) 01910 return; 01911 01912 START_LOG("enforce_constraints_exactly()","DofMap"); 01913 01914 if (!v) 01915 v = system.solution.get(); 01916 01917 NumericVector<Number> *v_local = NULL; // will be initialized below 01918 NumericVector<Number> *v_global = NULL; // will be initialized below 01919 UniquePtr<NumericVector<Number> > v_built; 01920 if (v->type() == SERIAL) 01921 { 01922 v_built = NumericVector<Number>::build(this->comm()); 01923 v_built->init(this->n_dofs(), this->n_local_dofs(), true, PARALLEL); 01924 v_built->close(); 01925 01926 for (dof_id_type i=v_built->first_local_index(); 01927 i<v_built->last_local_index(); i++) 01928 v_built->set(i, (*v)(i)); 01929 v_built->close(); 01930 v_global = v_built.get(); 01931 01932 v_local = v; 01933 libmesh_assert (v_local->closed()); 01934 } 01935 else if (v->type() == PARALLEL) 01936 { 01937 v_built = NumericVector<Number>::build(this->comm()); 01938 v_built->init (v->size(), v->size(), true, SERIAL); 01939 v->localize(*v_built); 01940 v_built->close(); 01941 v_local = v_built.get(); 01942 01943 v_global = v; 01944 } 01945 else if (v->type() == GHOSTED) 01946 { 01947 v_local = v; 01948 v_global = v; 01949 } 01950 else // unknown v->type() 01951 libmesh_error_msg("ERROR: Unknown v->type() == " << v->type()); 01952 01953 // We should never hit these asserts because we should error-out in 01954 // else clause above. Just to be sure we don't try to use v_local 01955 // and v_global uninitialized... 01956 libmesh_assert(v_local); 01957 libmesh_assert(v_global); 01958 libmesh_assert_equal_to (this, &(system.get_dof_map())); 01959 01960 DofConstraints::const_iterator c_it = _dof_constraints.begin(); 01961 const DofConstraints::const_iterator c_end = _dof_constraints.end(); 01962 01963 for ( ; c_it != c_end; ++c_it) 01964 { 01965 dof_id_type constrained_dof = c_it->first; 01966 if (constrained_dof < this->first_dof() || 01967 constrained_dof >= this->end_dof()) 01968 continue; 01969 01970 const DofConstraintRow constraint_row = c_it->second; 01971 01972 Number exact_value = 0; 01973 if (!homogeneous) 01974 { 01975 DofConstraintValueMap::const_iterator rhsit = 01976 _primal_constraint_values.find(constrained_dof); 01977 if (rhsit != _primal_constraint_values.end()) 01978 exact_value = rhsit->second; 01979 } 01980 for (DofConstraintRow::const_iterator 01981 j=constraint_row.begin(); j != constraint_row.end(); 01982 ++j) 01983 exact_value += j->second * (*v_local)(j->first); 01984 01985 v_global->set(constrained_dof, exact_value); 01986 } 01987 01988 // If the old vector was serial, we probably need to send our values 01989 // to other processors 01990 if (v->type() == SERIAL) 01991 { 01992 #ifndef NDEBUG 01993 v_global->close(); 01994 #endif 01995 v_global->localize (*v); 01996 } 01997 v->close(); 01998 01999 STOP_LOG("enforce_constraints_exactly()","DofMap"); 02000 } 02001 02002 02003 02004 void DofMap::enforce_adjoint_constraints_exactly 02005 (NumericVector<Number> &v, 02006 unsigned int q) const 02007 { 02008 parallel_object_only(); 02009 02010 if (!this->n_constrained_dofs()) 02011 return; 02012 02013 START_LOG("enforce_adjoint_constraints_exactly()","DofMap"); 02014 02015 NumericVector<Number> *v_local = NULL; // will be initialized below 02016 NumericVector<Number> *v_global = NULL; // will be initialized below 02017 UniquePtr<NumericVector<Number> > v_built; 02018 if (v.type() == SERIAL) 02019 { 02020 v_built = NumericVector<Number>::build(this->comm()); 02021 v_built->init(this->n_dofs(), this->n_local_dofs(), true, PARALLEL); 02022 v_built->close(); 02023 02024 for (dof_id_type i=v_built->first_local_index(); 02025 i<v_built->last_local_index(); i++) 02026 v_built->set(i, v(i)); 02027 v_built->close(); 02028 v_global = v_built.get(); 02029 02030 v_local = &v; 02031 libmesh_assert (v_local->closed()); 02032 } 02033 else if (v.type() == PARALLEL) 02034 { 02035 v_built = NumericVector<Number>::build(this->comm()); 02036 v_built->init (v.size(), v.size(), true, SERIAL); 02037 v.localize(*v_built); 02038 v_built->close(); 02039 v_local = v_built.get(); 02040 02041 v_global = &v; 02042 } 02043 else if (v.type() == GHOSTED) 02044 { 02045 v_local = &v; 02046 v_global = &v; 02047 } 02048 else // unknown v.type() 02049 libmesh_error_msg("ERROR: Unknown v.type() == " << v.type()); 02050 02051 // We should never hit these asserts because we should error-out in 02052 // else clause above. Just to be sure we don't try to use v_local 02053 // and v_global uninitialized... 02054 libmesh_assert(v_local); 02055 libmesh_assert(v_global); 02056 02057 // Do we have any non_homogeneous constraints? 02058 const AdjointDofConstraintValues::const_iterator 02059 adjoint_constraint_map_it = _adjoint_constraint_values.find(q); 02060 const DofConstraintValueMap *constraint_map = 02061 (adjoint_constraint_map_it == _adjoint_constraint_values.end()) ? 02062 NULL : &adjoint_constraint_map_it->second; 02063 02064 DofConstraints::const_iterator c_it = _dof_constraints.begin(); 02065 const DofConstraints::const_iterator c_end = _dof_constraints.end(); 02066 02067 for ( ; c_it != c_end; ++c_it) 02068 { 02069 dof_id_type constrained_dof = c_it->first; 02070 if (constrained_dof < this->first_dof() || 02071 constrained_dof >= this->end_dof()) 02072 continue; 02073 02074 const DofConstraintRow constraint_row = c_it->second; 02075 02076 Number exact_value = 0; 02077 if (constraint_map) 02078 { 02079 const DofConstraintValueMap::const_iterator 02080 adjoint_constraint_it = 02081 constraint_map->find(constrained_dof); 02082 if (adjoint_constraint_it != constraint_map->end()) 02083 exact_value = adjoint_constraint_it->second; 02084 } 02085 02086 for (DofConstraintRow::const_iterator 02087 j=constraint_row.begin(); j != constraint_row.end(); 02088 ++j) 02089 exact_value += j->second * (*v_local)(j->first); 02090 02091 v_global->set(constrained_dof, exact_value); 02092 } 02093 02094 // If the old vector was serial, we probably need to send our values 02095 // to other processors 02096 if (v.type() == SERIAL) 02097 { 02098 #ifndef NDEBUG 02099 v_global->close(); 02100 #endif 02101 v_global->localize (v); 02102 } 02103 v.close(); 02104 02105 STOP_LOG("enforce_adjoint_constraints_exactly()","DofMap"); 02106 } 02107 02108 02109 02110 std::pair<Real, Real> 02111 DofMap::max_constraint_error (const System &system, 02112 NumericVector<Number> *v) const 02113 { 02114 if (!v) 02115 v = system.solution.get(); 02116 NumericVector<Number> &vec = *v; 02117 02118 // We'll assume the vector is closed 02119 libmesh_assert (vec.closed()); 02120 02121 Real max_absolute_error = 0., max_relative_error = 0.; 02122 02123 const MeshBase &mesh = system.get_mesh(); 02124 02125 libmesh_assert_equal_to (this, &(system.get_dof_map())); 02126 02127 // indices on each element 02128 std::vector<dof_id_type> local_dof_indices; 02129 02130 MeshBase::const_element_iterator elem_it = 02131 mesh.active_local_elements_begin(); 02132 const MeshBase::const_element_iterator elem_end = 02133 mesh.active_local_elements_end(); 02134 02135 for ( ; elem_it != elem_end; ++elem_it) 02136 { 02137 const Elem* elem = *elem_it; 02138 02139 this->dof_indices(elem, local_dof_indices); 02140 std::vector<dof_id_type> raw_dof_indices = local_dof_indices; 02141 02142 // Constraint matrix for each element 02143 DenseMatrix<Number> C; 02144 02145 this->build_constraint_matrix (C, local_dof_indices); 02146 02147 // Continue if the element is unconstrained 02148 if (!C.m()) 02149 continue; 02150 02151 libmesh_assert_equal_to (C.m(), raw_dof_indices.size()); 02152 libmesh_assert_equal_to (C.n(), local_dof_indices.size()); 02153 02154 for (unsigned int i=0; i!=C.m(); ++i) 02155 { 02156 // Recalculate any constrained dof owned by this processor 02157 dof_id_type global_dof = raw_dof_indices[i]; 02158 if (this->is_constrained_dof(global_dof) && 02159 global_dof >= vec.first_local_index() && 02160 global_dof < vec.last_local_index()) 02161 { 02162 DofConstraints::const_iterator 02163 pos = _dof_constraints.find(global_dof); 02164 02165 libmesh_assert (pos != _dof_constraints.end()); 02166 02167 Number exact_value = 0; 02168 DofConstraintValueMap::const_iterator rhsit = 02169 _primal_constraint_values.find(global_dof); 02170 if (rhsit != _primal_constraint_values.end()) 02171 exact_value = rhsit->second; 02172 02173 for (unsigned int j=0; j!=C.n(); ++j) 02174 { 02175 if (local_dof_indices[j] != global_dof) 02176 exact_value += C(i,j) * 02177 vec(local_dof_indices[j]); 02178 } 02179 02180 max_absolute_error = std::max(max_absolute_error, 02181 std::abs(vec(global_dof) - exact_value)); 02182 max_relative_error = std::max(max_relative_error, 02183 std::abs(vec(global_dof) - exact_value) 02184 / std::abs(exact_value)); 02185 } 02186 } 02187 } 02188 02189 return std::pair<Real, Real>(max_absolute_error, max_relative_error); 02190 } 02191 02192 02193 02194 void DofMap::build_constraint_matrix (DenseMatrix<Number>& C, 02195 std::vector<dof_id_type>& elem_dofs, 02196 const bool called_recursively) const 02197 { 02198 if (!called_recursively) START_LOG("build_constraint_matrix()", "DofMap"); 02199 02200 // Create a set containing the DOFs we already depend on 02201 typedef std::set<dof_id_type> RCSet; 02202 RCSet dof_set; 02203 02204 bool we_have_constraints = false; 02205 02206 // Next insert any other dofs the current dofs might be constrained 02207 // in terms of. Note that in this case we may not be done: Those 02208 // may in turn depend on others. So, we need to repeat this process 02209 // in that case until the system depends only on unconstrained 02210 // degrees of freedom. 02211 for (unsigned int i=0; i<elem_dofs.size(); i++) 02212 if (this->is_constrained_dof(elem_dofs[i])) 02213 { 02214 we_have_constraints = true; 02215 02216 // If the DOF is constrained 02217 DofConstraints::const_iterator 02218 pos = _dof_constraints.find(elem_dofs[i]); 02219 02220 libmesh_assert (pos != _dof_constraints.end()); 02221 02222 const DofConstraintRow& constraint_row = pos->second; 02223 02224 // Constraint rows in p refinement may be empty 02225 //libmesh_assert (!constraint_row.empty()); 02226 02227 for (DofConstraintRow::const_iterator 02228 it=constraint_row.begin(); it != constraint_row.end(); 02229 ++it) 02230 dof_set.insert (it->first); 02231 } 02232 02233 // May be safe to return at this point 02234 // (but remember to stop the perflog) 02235 if (!we_have_constraints) 02236 { 02237 STOP_LOG("build_constraint_matrix()", "DofMap"); 02238 return; 02239 } 02240 02241 for (unsigned int i=0; i != elem_dofs.size(); ++i) 02242 dof_set.erase (elem_dofs[i]); 02243 02244 // If we added any DOFS then we need to do this recursively. 02245 // It is possible that we just added a DOF that is also 02246 // constrained! 02247 // 02248 // Also, we need to handle the special case of an element having DOFs 02249 // constrained in terms of other, local DOFs 02250 if (!dof_set.empty() || // case 1: constrained in terms of other DOFs 02251 !called_recursively) // case 2: constrained in terms of our own DOFs 02252 { 02253 const unsigned int old_size = 02254 cast_int<unsigned int>(elem_dofs.size()); 02255 02256 // Add new dependency dofs to the end of the current dof set 02257 elem_dofs.insert(elem_dofs.end(), 02258 dof_set.begin(), dof_set.end()); 02259 02260 // Now we can build the constraint matrix. 02261 // Note that resize also zeros for a DenseMatrix<Number>. 02262 C.resize (old_size, 02263 cast_int<unsigned int>(elem_dofs.size())); 02264 02265 // Create the C constraint matrix. 02266 for (unsigned int i=0; i != old_size; i++) 02267 if (this->is_constrained_dof(elem_dofs[i])) 02268 { 02269 // If the DOF is constrained 02270 DofConstraints::const_iterator 02271 pos = _dof_constraints.find(elem_dofs[i]); 02272 02273 libmesh_assert (pos != _dof_constraints.end()); 02274 02275 const DofConstraintRow& constraint_row = pos->second; 02276 02277 // p refinement creates empty constraint rows 02278 // libmesh_assert (!constraint_row.empty()); 02279 02280 for (DofConstraintRow::const_iterator 02281 it=constraint_row.begin(); it != constraint_row.end(); 02282 ++it) 02283 for (unsigned int j=0; j != elem_dofs.size(); j++) 02284 if (elem_dofs[j] == it->first) 02285 C(i,j) = it->second; 02286 } 02287 else 02288 { 02289 C(i,i) = 1.; 02290 } 02291 02292 // May need to do this recursively. It is possible 02293 // that we just replaced a constrained DOF with another 02294 // constrained DOF. 02295 DenseMatrix<Number> Cnew; 02296 02297 this->build_constraint_matrix (Cnew, elem_dofs, true); 02298 02299 if ((C.n() == Cnew.m()) && 02300 (Cnew.n() == elem_dofs.size())) // If the constraint matrix 02301 C.right_multiply(Cnew); // is constrained... 02302 02303 libmesh_assert_equal_to (C.n(), elem_dofs.size()); 02304 } 02305 02306 if (!called_recursively) STOP_LOG("build_constraint_matrix()", "DofMap"); 02307 } 02308 02309 02310 02311 void DofMap::build_constraint_matrix_and_vector 02312 (DenseMatrix<Number>& C, 02313 DenseVector<Number>& H, 02314 std::vector<dof_id_type>& elem_dofs, 02315 int qoi_index, 02316 const bool called_recursively) const 02317 { 02318 if (!called_recursively) 02319 START_LOG("build_constraint_matrix_and_vector()", "DofMap"); 02320 02321 // Create a set containing the DOFs we already depend on 02322 typedef std::set<dof_id_type> RCSet; 02323 RCSet dof_set; 02324 02325 bool we_have_constraints = false; 02326 02327 // Next insert any other dofs the current dofs might be constrained 02328 // in terms of. Note that in this case we may not be done: Those 02329 // may in turn depend on others. So, we need to repeat this process 02330 // in that case until the system depends only on unconstrained 02331 // degrees of freedom. 02332 for (unsigned int i=0; i<elem_dofs.size(); i++) 02333 if (this->is_constrained_dof(elem_dofs[i])) 02334 { 02335 we_have_constraints = true; 02336 02337 // If the DOF is constrained 02338 DofConstraints::const_iterator 02339 pos = _dof_constraints.find(elem_dofs[i]); 02340 02341 libmesh_assert (pos != _dof_constraints.end()); 02342 02343 const DofConstraintRow& constraint_row = pos->second; 02344 02345 // Constraint rows in p refinement may be empty 02346 //libmesh_assert (!constraint_row.empty()); 02347 02348 for (DofConstraintRow::const_iterator 02349 it=constraint_row.begin(); it != constraint_row.end(); 02350 ++it) 02351 dof_set.insert (it->first); 02352 } 02353 02354 // May be safe to return at this point 02355 // (but remember to stop the perflog) 02356 if (!we_have_constraints) 02357 { 02358 STOP_LOG("build_constraint_matrix_and_vector()", "DofMap"); 02359 return; 02360 } 02361 02362 for (unsigned int i=0; i != elem_dofs.size(); ++i) 02363 dof_set.erase (elem_dofs[i]); 02364 02365 // If we added any DOFS then we need to do this recursively. 02366 // It is possible that we just added a DOF that is also 02367 // constrained! 02368 // 02369 // Also, we need to handle the special case of an element having DOFs 02370 // constrained in terms of other, local DOFs 02371 if (!dof_set.empty() || // case 1: constrained in terms of other DOFs 02372 !called_recursively) // case 2: constrained in terms of our own DOFs 02373 { 02374 const DofConstraintValueMap *rhs_values = NULL; 02375 if (qoi_index < 0) 02376 rhs_values = &_primal_constraint_values; 02377 else 02378 { 02379 const AdjointDofConstraintValues::const_iterator 02380 it = _adjoint_constraint_values.find(qoi_index); 02381 if (it != _adjoint_constraint_values.end()) 02382 rhs_values = &it->second; 02383 } 02384 02385 const unsigned int old_size = 02386 cast_int<unsigned int>(elem_dofs.size()); 02387 02388 // Add new dependency dofs to the end of the current dof set 02389 elem_dofs.insert(elem_dofs.end(), 02390 dof_set.begin(), dof_set.end()); 02391 02392 // Now we can build the constraint matrix and vector. 02393 // Note that resize also zeros for a DenseMatrix and DenseVector 02394 C.resize (old_size, 02395 cast_int<unsigned int>(elem_dofs.size())); 02396 H.resize (old_size); 02397 02398 // Create the C constraint matrix. 02399 for (unsigned int i=0; i != old_size; i++) 02400 if (this->is_constrained_dof(elem_dofs[i])) 02401 { 02402 // If the DOF is constrained 02403 DofConstraints::const_iterator 02404 pos = _dof_constraints.find(elem_dofs[i]); 02405 02406 libmesh_assert (pos != _dof_constraints.end()); 02407 02408 const DofConstraintRow& constraint_row = pos->second; 02409 02410 // p refinement creates empty constraint rows 02411 // libmesh_assert (!constraint_row.empty()); 02412 02413 for (DofConstraintRow::const_iterator 02414 it=constraint_row.begin(); it != constraint_row.end(); 02415 ++it) 02416 for (unsigned int j=0; j != elem_dofs.size(); j++) 02417 if (elem_dofs[j] == it->first) 02418 C(i,j) = it->second; 02419 02420 if (rhs_values) 02421 { 02422 DofConstraintValueMap::const_iterator rhsit = 02423 rhs_values->find(elem_dofs[i]); 02424 if (rhsit != rhs_values->end()) 02425 H(i) = rhsit->second; 02426 } 02427 } 02428 else 02429 { 02430 C(i,i) = 1.; 02431 } 02432 02433 // May need to do this recursively. It is possible 02434 // that we just replaced a constrained DOF with another 02435 // constrained DOF. 02436 DenseMatrix<Number> Cnew; 02437 DenseVector<Number> Hnew; 02438 02439 this->build_constraint_matrix_and_vector (Cnew, Hnew, elem_dofs, 02440 qoi_index, true); 02441 02442 if ((C.n() == Cnew.m()) && // If the constraint matrix 02443 (Cnew.n() == elem_dofs.size())) // is constrained... 02444 { 02445 C.right_multiply(Cnew); 02446 02447 // If x = Cy + h and y = Dz + g 02448 // Then x = (CD)z + (Cg + h) 02449 C.vector_mult_add(H, 1, Hnew); 02450 } 02451 02452 libmesh_assert_equal_to (C.n(), elem_dofs.size()); 02453 } 02454 02455 if (!called_recursively) 02456 STOP_LOG("build_constraint_matrix_and_vector()", "DofMap"); 02457 } 02458 02459 02460 void DofMap::allgather_recursive_constraints(MeshBase& mesh) 02461 { 02462 // This function must be run on all processors at once 02463 parallel_object_only(); 02464 02465 // Return immediately if there's nothing to gather 02466 if (this->n_processors() == 1) 02467 return; 02468 02469 // We might get to return immediately if none of the processors 02470 // found any constraints 02471 unsigned int has_constraints = !_dof_constraints.empty() 02472 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 02473 || !_node_constraints.empty() 02474 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS 02475 ; 02476 this->comm().max(has_constraints); 02477 if (!has_constraints) 02478 return; 02479 02480 // If we have heterogenous adjoint constraints we need to 02481 // communicate those too. 02482 const unsigned int max_qoi_num = 02483 _adjoint_constraint_values.empty() ? 02484 0 : _adjoint_constraint_values.rbegin()->first; 02485 02486 // We might have calculated constraints for constrained dofs 02487 // which have support on other processors. 02488 // Push these out first. 02489 { 02490 std::vector<std::set<dof_id_type> > pushed_ids(this->n_processors()); 02491 02492 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 02493 std::vector<std::set<dof_id_type> > pushed_node_ids(this->n_processors()); 02494 #endif 02495 02496 const unsigned int sys_num = this->sys_number(); 02497 02498 MeshBase::element_iterator 02499 foreign_elem_it = mesh.active_not_local_elements_begin(), 02500 foreign_elem_end = mesh.active_not_local_elements_end(); 02501 02502 // Collect the constraints to push to each processor 02503 for (; foreign_elem_it != foreign_elem_end; ++foreign_elem_it) 02504 { 02505 const Elem *elem = *foreign_elem_it; 02506 02507 // Just checking dof_indices on the foreign element isn't 02508 // enough. Consider a central hanging node between a coarse 02509 // Q2/Q1 element and its finer neighbors on a higher-ranked 02510 // processor. The coarse element's processor will own the node, 02511 // and will thereby own the pressure dof on that node, despite 02512 // the fact that that pressure dof doesn't directly exist on the 02513 // coarse element! 02514 // 02515 // So, we loop through dofs manually. 02516 02517 { 02518 const unsigned int n_vars = elem->n_vars(sys_num); 02519 for (unsigned int v=0; v != n_vars; ++v) 02520 { 02521 const unsigned int n_comp = elem->n_comp(sys_num,v); 02522 for (unsigned int c=0; c != n_comp; ++c) 02523 { 02524 const unsigned int id = 02525 elem->dof_number(sys_num,v,c); 02526 if (this->is_constrained_dof(id)) 02527 pushed_ids[elem->processor_id()].insert(id); 02528 } 02529 } 02530 } 02531 02532 for (unsigned int n=0; n != elem->n_nodes(); ++n) 02533 { 02534 const Node *node = elem->get_node(n); 02535 const unsigned int n_vars = node->n_vars(sys_num); 02536 for (unsigned int v=0; v != n_vars; ++v) 02537 { 02538 const unsigned int n_comp = node->n_comp(sys_num,v); 02539 for (unsigned int c=0; c != n_comp; ++c) 02540 { 02541 const unsigned int id = 02542 node->dof_number(sys_num,v,c); 02543 if (this->is_constrained_dof(id)) 02544 pushed_ids[elem->processor_id()].insert(id); 02545 } 02546 } 02547 } 02548 02549 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 02550 for (unsigned int n=0; n != elem->n_nodes(); ++n) 02551 if (this->is_constrained_node(elem->get_node(n))) 02552 pushed_node_ids[elem->processor_id()].insert(elem->node(n)); 02553 #endif 02554 } 02555 02556 // Now trade constraint rows 02557 for (processor_id_type p = 0; p != this->n_processors(); ++p) 02558 { 02559 // Push to processor procup while receiving from procdown 02560 processor_id_type procup = 02561 cast_int<processor_id_type>((this->processor_id() + p) % 02562 this->n_processors()); 02563 processor_id_type procdown = 02564 cast_int<processor_id_type>((this->n_processors() + 02565 this->processor_id() - p) % 02566 this->n_processors()); 02567 02568 // Pack the dof constraint rows and rhs's to push to procup 02569 const std::size_t pushed_ids_size = pushed_ids[procup].size(); 02570 std::vector<std::vector<dof_id_type> > pushed_keys(pushed_ids_size); 02571 std::vector<std::vector<Real> > pushed_vals(pushed_ids_size); 02572 std::vector<Number> pushed_rhss(pushed_ids_size); 02573 02574 std::vector<std::vector<Number> > 02575 pushed_adj_rhss(max_qoi_num, 02576 std::vector<Number>(pushed_ids_size)); 02577 std::set<dof_id_type>::const_iterator it = pushed_ids[procup].begin(); 02578 for (std::size_t i = 0; it != pushed_ids[procup].end(); 02579 ++i, ++it) 02580 { 02581 const dof_id_type pushed_id = *it; 02582 DofConstraintRow &row = _dof_constraints[pushed_id]; 02583 std::size_t row_size = row.size(); 02584 pushed_keys[i].reserve(row_size); 02585 pushed_vals[i].reserve(row_size); 02586 for (DofConstraintRow::iterator j = row.begin(); 02587 j != row.end(); ++j) 02588 { 02589 pushed_keys[i].push_back(j->first); 02590 pushed_vals[i].push_back(j->second); 02591 } 02592 02593 DofConstraintValueMap::const_iterator rhsit = 02594 _primal_constraint_values.find(pushed_id); 02595 pushed_rhss[i] = 02596 (rhsit == _primal_constraint_values.end()) ? 02597 0 : rhsit->second; 02598 02599 for (unsigned int q = 0; q != max_qoi_num; ++q) 02600 { 02601 AdjointDofConstraintValues::const_iterator adjoint_map_it = 02602 _adjoint_constraint_values.find(q); 02603 02604 if (adjoint_map_it == _adjoint_constraint_values.end()) 02605 continue; 02606 02607 const DofConstraintValueMap &constraint_map = 02608 adjoint_map_it->second; 02609 02610 DofConstraintValueMap::const_iterator rhsit = 02611 constraint_map.find(pushed_id); 02612 02613 pushed_adj_rhss[q][i] = 02614 (rhsit == constraint_map.end()) ? 02615 0 : rhsit->second; 02616 } 02617 } 02618 02619 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 02620 // Pack the node constraint rows to push to procup 02621 const std::size_t pushed_nodes_size = pushed_node_ids[procup].size(); 02622 std::vector<std::vector<dof_id_type> > pushed_node_keys(pushed_nodes_size); 02623 std::vector<std::vector<Real> > pushed_node_vals(pushed_nodes_size); 02624 std::vector<Point> pushed_node_offsets(pushed_nodes_size); 02625 std::set<dof_id_type>::const_iterator node_it = pushed_node_ids[procup].begin(); 02626 for (std::size_t i = 0; node_it != pushed_node_ids[procup].end(); 02627 ++i, ++node_it) 02628 { 02629 const Node *node = mesh.node_ptr(*node_it); 02630 NodeConstraintRow &row = _node_constraints[node].first; 02631 std::size_t row_size = row.size(); 02632 pushed_node_keys[i].reserve(row_size); 02633 pushed_node_vals[i].reserve(row_size); 02634 for (NodeConstraintRow::iterator j = row.begin(); 02635 j != row.end(); ++j) 02636 { 02637 pushed_node_keys[i].push_back(j->first->id()); 02638 pushed_node_vals[i].push_back(j->second); 02639 } 02640 pushed_node_offsets[i] = _node_constraints[node].second; 02641 } 02642 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS 02643 02644 // Trade pushed dof constraint rows 02645 std::vector<dof_id_type> pushed_ids_from_me 02646 (pushed_ids[procup].begin(), pushed_ids[procup].end()); 02647 std::vector<dof_id_type> pushed_ids_to_me; 02648 std::vector<std::vector<dof_id_type> > pushed_keys_to_me; 02649 std::vector<std::vector<Real> > pushed_vals_to_me; 02650 std::vector<Number> pushed_rhss_to_me; 02651 std::vector<std::vector<Number> > pushed_adj_rhss_to_me; 02652 this->comm().send_receive(procup, pushed_ids_from_me, 02653 procdown, pushed_ids_to_me); 02654 this->comm().send_receive(procup, pushed_keys, 02655 procdown, pushed_keys_to_me); 02656 this->comm().send_receive(procup, pushed_vals, 02657 procdown, pushed_vals_to_me); 02658 this->comm().send_receive(procup, pushed_rhss, 02659 procdown, pushed_rhss_to_me); 02660 this->comm().send_receive(procup, pushed_adj_rhss, 02661 procdown, pushed_adj_rhss_to_me); 02662 libmesh_assert_equal_to (pushed_ids_to_me.size(), pushed_keys_to_me.size()); 02663 libmesh_assert_equal_to (pushed_ids_to_me.size(), pushed_vals_to_me.size()); 02664 libmesh_assert_equal_to (pushed_ids_to_me.size(), pushed_rhss_to_me.size()); 02665 02666 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 02667 // Trade pushed node constraint rows 02668 std::vector<dof_id_type> pushed_node_ids_from_me 02669 (pushed_node_ids[procup].begin(), pushed_node_ids[procup].end()); 02670 std::vector<dof_id_type> pushed_node_ids_to_me; 02671 std::vector<std::vector<dof_id_type> > pushed_node_keys_to_me; 02672 std::vector<std::vector<Real> > pushed_node_vals_to_me; 02673 std::vector<Point> pushed_node_offsets_to_me; 02674 this->comm().send_receive(procup, pushed_node_ids_from_me, 02675 procdown, pushed_node_ids_to_me); 02676 this->comm().send_receive(procup, pushed_node_keys, 02677 procdown, pushed_node_keys_to_me); 02678 this->comm().send_receive(procup, pushed_node_vals, 02679 procdown, pushed_node_vals_to_me); 02680 this->comm().send_receive(procup, pushed_node_offsets, 02681 procdown, pushed_node_offsets_to_me); 02682 02683 // Note that we aren't doing a send_receive on the Nodes 02684 // themselves. At this point we should only be pushing out 02685 // "raw" constraints, and there should be no 02686 // constrained-by-constrained-by-etc. situations that could 02687 // involve non-semilocal nodes. 02688 02689 libmesh_assert_equal_to (pushed_node_ids_to_me.size(), pushed_node_keys_to_me.size()); 02690 libmesh_assert_equal_to (pushed_node_ids_to_me.size(), pushed_node_vals_to_me.size()); 02691 libmesh_assert_equal_to (pushed_node_ids_to_me.size(), pushed_node_offsets_to_me.size()); 02692 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS 02693 02694 // Add the dof constraints that I've been sent 02695 for (std::size_t i = 0; i != pushed_ids_to_me.size(); ++i) 02696 { 02697 libmesh_assert_equal_to (pushed_keys_to_me[i].size(), pushed_vals_to_me[i].size()); 02698 02699 dof_id_type constrained = pushed_ids_to_me[i]; 02700 02701 // If we don't already have a constraint for this dof, 02702 // add the one we were sent 02703 if (!this->is_constrained_dof(constrained)) 02704 { 02705 DofConstraintRow &row = _dof_constraints[constrained]; 02706 for (std::size_t j = 0; j != pushed_keys_to_me[i].size(); ++j) 02707 { 02708 row[pushed_keys_to_me[i][j]] = pushed_vals_to_me[i][j]; 02709 } 02710 if (libmesh_isnan(pushed_rhss_to_me[i])) 02711 libmesh_assert(pushed_keys_to_me[i].empty()); 02712 if (pushed_rhss_to_me[i] != Number(0)) 02713 _primal_constraint_values[constrained] = pushed_rhss_to_me[i]; 02714 else 02715 _primal_constraint_values.erase(constrained); 02716 02717 for (unsigned int q = 0; q != max_qoi_num; ++q) 02718 { 02719 AdjointDofConstraintValues::iterator adjoint_map_it = 02720 _adjoint_constraint_values.find(q); 02721 02722 if ((adjoint_map_it == _adjoint_constraint_values.end()) && 02723 pushed_adj_rhss_to_me[q][constrained] == Number(0)) 02724 continue; 02725 02726 if (adjoint_map_it == _adjoint_constraint_values.end()) 02727 adjoint_map_it = _adjoint_constraint_values.insert 02728 (std::make_pair(q,DofConstraintValueMap())).first; 02729 02730 DofConstraintValueMap &constraint_map = 02731 adjoint_map_it->second; 02732 02733 if (pushed_adj_rhss_to_me[q][i] != Number(0)) 02734 constraint_map[constrained] = 02735 pushed_adj_rhss_to_me[q][i]; 02736 else 02737 constraint_map.erase(constrained); 02738 } 02739 } 02740 } 02741 02742 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 02743 // Add the node constraints that I've been sent 02744 for (std::size_t i = 0; i != pushed_node_ids_to_me.size(); ++i) 02745 { 02746 libmesh_assert_equal_to (pushed_node_keys_to_me[i].size(), pushed_node_vals_to_me[i].size()); 02747 02748 dof_id_type constrained_id = pushed_node_ids_to_me[i]; 02749 02750 // If we don't already have a constraint for this node, 02751 // add the one we were sent 02752 const Node *constrained = mesh.node_ptr(constrained_id); 02753 if (!this->is_constrained_node(constrained)) 02754 { 02755 NodeConstraintRow &row = _node_constraints[constrained].first; 02756 for (std::size_t j = 0; j != pushed_node_keys_to_me[i].size(); ++j) 02757 { 02758 const Node *key_node = mesh.node_ptr(pushed_node_keys_to_me[i][j]); 02759 libmesh_assert(key_node); 02760 row[key_node] = pushed_node_vals_to_me[i][j]; 02761 } 02762 _node_constraints[constrained].second = pushed_node_offsets_to_me[i]; 02763 } 02764 } 02765 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS 02766 } 02767 } 02768 02769 // Now start checking for any other constraints we need 02770 // to know about, requesting them recursively. 02771 02772 // Create sets containing the DOFs and nodes we already depend on 02773 typedef std::set<dof_id_type> DoF_RCSet; 02774 DoF_RCSet unexpanded_dofs; 02775 02776 for (DofConstraints::iterator i = _dof_constraints.begin(); 02777 i != _dof_constraints.end(); ++i) 02778 { 02779 unexpanded_dofs.insert(i->first); 02780 } 02781 02782 typedef std::set<const Node *> Node_RCSet; 02783 Node_RCSet unexpanded_nodes; 02784 02785 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 02786 for (NodeConstraints::iterator i = _node_constraints.begin(); 02787 i != _node_constraints.end(); ++i) 02788 { 02789 unexpanded_nodes.insert(i->first); 02790 } 02791 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS 02792 02793 // We have to keep recursing while the unexpanded set is 02794 // nonempty on *any* processor 02795 bool unexpanded_set_nonempty = !unexpanded_dofs.empty() || 02796 !unexpanded_nodes.empty(); 02797 this->comm().max(unexpanded_set_nonempty); 02798 02799 while (unexpanded_set_nonempty) 02800 { 02801 // Let's make sure we don't lose sync in this loop. 02802 parallel_object_only(); 02803 02804 // Request sets 02805 DoF_RCSet dof_request_set; 02806 Node_RCSet node_request_set; 02807 02808 // Request sets to send to each processor 02809 std::vector<std::vector<dof_id_type> > 02810 requested_dof_ids(this->n_processors()), 02811 requested_node_ids(this->n_processors()); 02812 02813 // And the sizes of each 02814 std::vector<dof_id_type> 02815 dof_ids_on_proc(this->n_processors(), 0), 02816 node_ids_on_proc(this->n_processors(), 0); 02817 02818 // Fill (and thereby sort and uniq!) the main request sets 02819 for (DoF_RCSet::iterator i = unexpanded_dofs.begin(); 02820 i != unexpanded_dofs.end(); ++i) 02821 { 02822 DofConstraintRow &row = _dof_constraints[*i]; 02823 for (DofConstraintRow::iterator j = row.begin(); 02824 j != row.end(); ++j) 02825 { 02826 const dof_id_type constraining_dof = j->first; 02827 02828 // If it's non-local and we haven't already got a 02829 // constraint for it, we might need to ask for one 02830 if (((constraining_dof < this->first_dof()) || 02831 (constraining_dof >= this->end_dof())) && 02832 !_dof_constraints.count(constraining_dof)) 02833 dof_request_set.insert(constraining_dof); 02834 } 02835 } 02836 02837 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 02838 for (Node_RCSet::iterator i = unexpanded_nodes.begin(); 02839 i != unexpanded_nodes.end(); ++i) 02840 { 02841 NodeConstraintRow &row = _node_constraints[*i].first; 02842 for (NodeConstraintRow::iterator j = row.begin(); 02843 j != row.end(); ++j) 02844 { 02845 const Node* const node = j->first; 02846 libmesh_assert(node); 02847 02848 // If it's non-local and we haven't already got a 02849 // constraint for it, we might need to ask for one 02850 if ((node->processor_id() != this->processor_id()) && 02851 !_node_constraints.count(node)) 02852 node_request_set.insert(node); 02853 } 02854 } 02855 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS 02856 02857 // Clear the unexpanded constraint sets; we're about to expand 02858 // them 02859 unexpanded_dofs.clear(); 02860 unexpanded_nodes.clear(); 02861 02862 // Count requests by processor 02863 processor_id_type proc_id = 0; 02864 for (DoF_RCSet::iterator i = dof_request_set.begin(); 02865 i != dof_request_set.end(); ++i) 02866 { 02867 while (*i >= _end_df[proc_id]) 02868 proc_id++; 02869 dof_ids_on_proc[proc_id]++; 02870 } 02871 02872 for (Node_RCSet::iterator i = node_request_set.begin(); 02873 i != node_request_set.end(); ++i) 02874 { 02875 libmesh_assert(*i); 02876 libmesh_assert_less ((*i)->processor_id(), this->n_processors()); 02877 node_ids_on_proc[(*i)->processor_id()]++; 02878 } 02879 02880 for (processor_id_type p = 0; p != this->n_processors(); ++p) 02881 { 02882 requested_dof_ids[p].reserve(dof_ids_on_proc[p]); 02883 requested_node_ids[p].reserve(node_ids_on_proc[p]); 02884 } 02885 02886 // Prepare each processor's request set 02887 proc_id = 0; 02888 for (DoF_RCSet::iterator i = dof_request_set.begin(); 02889 i != dof_request_set.end(); ++i) 02890 { 02891 while (*i >= _end_df[proc_id]) 02892 proc_id++; 02893 requested_dof_ids[proc_id].push_back(*i); 02894 } 02895 02896 for (Node_RCSet::iterator i = node_request_set.begin(); 02897 i != node_request_set.end(); ++i) 02898 { 02899 requested_node_ids[(*i)->processor_id()].push_back((*i)->id()); 02900 } 02901 02902 // Now request constraint rows from other processors 02903 for (processor_id_type p=1; p != this->n_processors(); ++p) 02904 { 02905 // Trade my requests with processor procup and procdown 02906 processor_id_type procup = 02907 cast_int<processor_id_type>((this->processor_id() + p) % 02908 this->n_processors()); 02909 processor_id_type procdown = 02910 cast_int<processor_id_type>((this->n_processors() + 02911 this->processor_id() - p) % 02912 this->n_processors()); 02913 std::vector<dof_id_type> dof_request_to_fill, 02914 node_request_to_fill; 02915 02916 this->comm().send_receive(procup, requested_dof_ids[procup], 02917 procdown, dof_request_to_fill); 02918 this->comm().send_receive(procup, requested_node_ids[procup], 02919 procdown, node_request_to_fill); 02920 02921 // Fill those requests 02922 std::vector<std::vector<dof_id_type> > dof_row_keys(dof_request_to_fill.size()), 02923 node_row_keys(node_request_to_fill.size()); 02924 02925 std::vector<std::vector<Real> > dof_row_vals(dof_request_to_fill.size()), 02926 node_row_vals(node_request_to_fill.size()); 02927 std::vector<Number> dof_row_rhss(dof_request_to_fill.size()); 02928 std::vector<std::vector<Number> > 02929 dof_adj_rhss(max_qoi_num, 02930 std::vector<Number>(dof_request_to_fill.size())); 02931 std::vector<Point> node_row_rhss(node_request_to_fill.size()); 02932 for (std::size_t i=0; i != dof_request_to_fill.size(); ++i) 02933 { 02934 dof_id_type constrained = dof_request_to_fill[i]; 02935 if (_dof_constraints.count(constrained)) 02936 { 02937 DofConstraintRow &row = _dof_constraints[constrained]; 02938 std::size_t row_size = row.size(); 02939 dof_row_keys[i].reserve(row_size); 02940 dof_row_vals[i].reserve(row_size); 02941 for (DofConstraintRow::iterator j = row.begin(); 02942 j != row.end(); ++j) 02943 { 02944 dof_row_keys[i].push_back(j->first); 02945 dof_row_vals[i].push_back(j->second); 02946 02947 // We should never have a 0 constraint 02948 // coefficient; that's implicit via sparse 02949 // constraint storage 02950 libmesh_assert(j->second); 02951 } 02952 DofConstraintValueMap::const_iterator rhsit = 02953 _primal_constraint_values.find(constrained); 02954 dof_row_rhss[i] = (rhsit == _primal_constraint_values.end()) ? 02955 0 : rhsit->second; 02956 02957 for (unsigned int q = 0; q != max_qoi_num; ++q) 02958 { 02959 AdjointDofConstraintValues::const_iterator adjoint_map_it = 02960 _adjoint_constraint_values.find(q); 02961 02962 if (adjoint_map_it == _adjoint_constraint_values.end()) 02963 continue; 02964 02965 const DofConstraintValueMap &constraint_map = 02966 adjoint_map_it->second; 02967 02968 DofConstraintValueMap::const_iterator rhsit = 02969 constraint_map.find(constrained); 02970 dof_adj_rhss[q][i] = (rhsit == constraint_map.end()) ? 02971 0 : rhsit->second; 02972 } 02973 } 02974 else 02975 { 02976 // Get NaN from Real, where it should exist, not 02977 // from Number, which may be std::complex, in which 02978 // case quiet_NaN() silently returns zero, rather 02979 // than sanely returning NaN or throwing an 02980 // exception or sending Stroustrop hate mail. 02981 dof_row_rhss[i] = 02982 std::numeric_limits<Real>::quiet_NaN(); 02983 02984 // Make sure we don't get caught by "!isnan(NaN)" 02985 // bugs again. 02986 libmesh_assert(libmesh_isnan(dof_row_rhss[i])); 02987 } 02988 } 02989 02990 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 02991 // FIXME - this could be an unordered set, given a 02992 // hash<pointers> specialization 02993 std::set<const Node*> nodes_requested; 02994 02995 for (std::size_t i=0; i != node_request_to_fill.size(); ++i) 02996 { 02997 dof_id_type constrained_id = node_request_to_fill[i]; 02998 const Node *constrained_node = mesh.node_ptr(constrained_id); 02999 if (_node_constraints.count(constrained_node)) 03000 { 03001 const NodeConstraintRow &row = _node_constraints[constrained_node].first; 03002 std::size_t row_size = row.size(); 03003 node_row_keys[i].reserve(row_size); 03004 node_row_vals[i].reserve(row_size); 03005 for (NodeConstraintRow::const_iterator j = row.begin(); 03006 j != row.end(); ++j) 03007 { 03008 const Node* node = j->first; 03009 node_row_keys[i].push_back(node->id()); 03010 node_row_vals[i].push_back(j->second); 03011 03012 // If we're not sure whether our send 03013 // destination already has this node, let's give 03014 // it a copy. 03015 if (node->processor_id() != procdown) 03016 nodes_requested.insert(node); 03017 03018 // We can have 0 nodal constraint 03019 // coefficients, where no Lagrange constrant 03020 // exists but non-Lagrange basis constraints 03021 // might. 03022 // libmesh_assert(j->second); 03023 } 03024 node_row_rhss[i] = _node_constraints[constrained_node].second; 03025 } 03026 } 03027 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS 03028 03029 // Trade back the results 03030 std::vector<std::vector<dof_id_type> > dof_filled_keys, 03031 node_filled_keys; 03032 std::vector<std::vector<Real> > dof_filled_vals, 03033 node_filled_vals; 03034 std::vector<Number> dof_filled_rhss; 03035 std::vector<std::vector<Number> > adj_filled_rhss; 03036 std::vector<Point> node_filled_rhss; 03037 this->comm().send_receive(procdown, dof_row_keys, 03038 procup, dof_filled_keys); 03039 this->comm().send_receive(procdown, dof_row_vals, 03040 procup, dof_filled_vals); 03041 this->comm().send_receive(procdown, dof_row_rhss, 03042 procup, dof_filled_rhss); 03043 this->comm().send_receive(procdown, dof_adj_rhss, 03044 procup, adj_filled_rhss); 03045 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 03046 this->comm().send_receive(procdown, node_row_keys, 03047 procup, node_filled_keys); 03048 this->comm().send_receive(procdown, node_row_vals, 03049 procup, node_filled_vals); 03050 this->comm().send_receive(procdown, node_row_rhss, 03051 procup, node_filled_rhss); 03052 03053 // Constraining nodes might not even exist on our subset of 03054 // a distributed mesh, so let's make them exist. 03055 if (!mesh.is_serial()) 03056 this->comm().send_receive_packed_range 03057 (procdown, &mesh, nodes_requested.begin(), nodes_requested.end(), 03058 procup, &mesh, mesh_inserter_iterator<Node>(mesh)); 03059 03060 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS 03061 libmesh_assert_equal_to (dof_filled_keys.size(), requested_dof_ids[procup].size()); 03062 libmesh_assert_equal_to (dof_filled_vals.size(), requested_dof_ids[procup].size()); 03063 libmesh_assert_equal_to (dof_filled_rhss.size(), requested_dof_ids[procup].size()); 03064 #ifndef NDEBUG 03065 for (unsigned int q=0; q != adj_filled_rhss.size(); ++q) 03066 libmesh_assert_equal_to (adj_filled_rhss[q].size(), requested_dof_ids[procup].size()); 03067 #endif 03068 libmesh_assert_equal_to (node_filled_keys.size(), requested_node_ids[procup].size()); 03069 libmesh_assert_equal_to (node_filled_vals.size(), requested_node_ids[procup].size()); 03070 libmesh_assert_equal_to (node_filled_rhss.size(), requested_node_ids[procup].size()); 03071 03072 // Add any new constraint rows we've found 03073 for (std::size_t i=0; i != requested_dof_ids[procup].size(); ++i) 03074 { 03075 libmesh_assert_equal_to (dof_filled_keys[i].size(), dof_filled_vals[i].size()); 03076 if (!libmesh_isnan(dof_filled_rhss[i])) 03077 { 03078 dof_id_type constrained = requested_dof_ids[procup][i]; 03079 DofConstraintRow &row = _dof_constraints[constrained]; 03080 for (std::size_t j = 0; j != dof_filled_keys[i].size(); ++j) 03081 row[dof_filled_keys[i][j]] = dof_filled_vals[i][j]; 03082 if (dof_filled_rhss[i] != Number(0)) 03083 _primal_constraint_values[constrained] = dof_filled_rhss[i]; 03084 else 03085 _primal_constraint_values.erase(constrained); 03086 03087 for (unsigned int q = 0; q != max_qoi_num; ++q) 03088 { 03089 AdjointDofConstraintValues::iterator adjoint_map_it = 03090 _adjoint_constraint_values.find(q); 03091 03092 if ((adjoint_map_it == _adjoint_constraint_values.end()) && 03093 adj_filled_rhss[q][constrained] == Number(0)) 03094 continue; 03095 03096 if (adjoint_map_it == _adjoint_constraint_values.end()) 03097 adjoint_map_it = _adjoint_constraint_values.insert 03098 (std::make_pair(q,DofConstraintValueMap())).first; 03099 03100 DofConstraintValueMap &constraint_map = 03101 adjoint_map_it->second; 03102 03103 if (adj_filled_rhss[q][i] != Number(0)) 03104 constraint_map[constrained] = 03105 adj_filled_rhss[q][i]; 03106 else 03107 constraint_map.erase(constrained); 03108 } 03109 03110 // And prepare to check for more recursive constraints 03111 if (!dof_filled_keys[i].empty()) 03112 unexpanded_dofs.insert(constrained); 03113 } 03114 } 03115 03116 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 03117 for (std::size_t i=0; i != requested_node_ids[procup].size(); ++i) 03118 { 03119 libmesh_assert_equal_to (node_filled_keys[i].size(), node_filled_vals[i].size()); 03120 if (!node_filled_keys[i].empty()) 03121 { 03122 dof_id_type constrained_id = requested_node_ids[procup][i]; 03123 const Node* constrained_node = mesh.node_ptr(constrained_id); 03124 NodeConstraintRow &row = _node_constraints[constrained_node].first; 03125 for (std::size_t j = 0; j != node_filled_keys[i].size(); ++j) 03126 { 03127 const Node* key_node = 03128 mesh.node_ptr(node_filled_keys[i][j]); 03129 libmesh_assert(key_node); 03130 row[key_node] = node_filled_vals[i][j]; 03131 } 03132 _node_constraints[constrained_node].second = node_filled_rhss[i]; 03133 03134 // And prepare to check for more recursive constraints 03135 unexpanded_nodes.insert(constrained_node); 03136 } 03137 } 03138 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS 03139 } 03140 03141 // We have to keep recursing while the unexpanded set is 03142 // nonempty on *any* processor 03143 unexpanded_set_nonempty = !unexpanded_dofs.empty() || 03144 !unexpanded_nodes.empty(); 03145 this->comm().max(unexpanded_set_nonempty); 03146 } 03147 } 03148 03149 03150 03151 void DofMap::process_constraints (MeshBase& mesh) 03152 { 03153 // We've computed our local constraints, but they may depend on 03154 // non-local constraints that we'll need to take into account. 03155 this->allgather_recursive_constraints(mesh); 03156 03157 // Create a set containing the DOFs we already depend on 03158 typedef std::set<dof_id_type> RCSet; 03159 RCSet unexpanded_set; 03160 03161 for (DofConstraints::iterator i = _dof_constraints.begin(); 03162 i != _dof_constraints.end(); ++i) 03163 unexpanded_set.insert(i->first); 03164 03165 while (!unexpanded_set.empty()) 03166 for (RCSet::iterator i = unexpanded_set.begin(); 03167 i != unexpanded_set.end(); /* nothing */) 03168 { 03169 // If the DOF is constrained 03170 DofConstraints::iterator 03171 pos = _dof_constraints.find(*i); 03172 03173 libmesh_assert (pos != _dof_constraints.end()); 03174 03175 DofConstraintRow& constraint_row = pos->second; 03176 03177 DofConstraintValueMap::iterator rhsit = 03178 _primal_constraint_values.find(*i); 03179 Number constraint_rhs = (rhsit == _primal_constraint_values.end()) ? 03180 0 : rhsit->second; 03181 03182 std::vector<dof_id_type> constraints_to_expand; 03183 03184 for (DofConstraintRow::const_iterator 03185 it=constraint_row.begin(); it != constraint_row.end(); 03186 ++it) 03187 if (it->first != *i && this->is_constrained_dof(it->first)) 03188 { 03189 unexpanded_set.insert(it->first); 03190 constraints_to_expand.push_back(it->first); 03191 } 03192 03193 for (std::size_t j = 0; j != constraints_to_expand.size(); 03194 ++j) 03195 { 03196 dof_id_type expandable = constraints_to_expand[j]; 03197 03198 const Real this_coef = constraint_row[expandable]; 03199 03200 DofConstraints::const_iterator 03201 subpos = _dof_constraints.find(expandable); 03202 03203 libmesh_assert (subpos != _dof_constraints.end()); 03204 03205 const DofConstraintRow& subconstraint_row = subpos->second; 03206 03207 for (DofConstraintRow::const_iterator 03208 it=subconstraint_row.begin(); 03209 it != subconstraint_row.end(); ++it) 03210 { 03211 constraint_row[it->first] += it->second * this_coef; 03212 } 03213 DofConstraintValueMap::const_iterator subrhsit = 03214 _primal_constraint_values.find(expandable); 03215 if (subrhsit != _primal_constraint_values.end()) 03216 constraint_rhs += subrhsit->second * this_coef; 03217 03218 constraint_row.erase(expandable); 03219 } 03220 03221 if (rhsit == _primal_constraint_values.end()) 03222 { 03223 if (constraint_rhs != Number(0)) 03224 _primal_constraint_values[*i] = constraint_rhs; 03225 else 03226 _primal_constraint_values.erase(*i); 03227 } 03228 else 03229 { 03230 if (constraint_rhs != Number(0)) 03231 rhsit->second = constraint_rhs; 03232 else 03233 _primal_constraint_values.erase(rhsit); 03234 } 03235 03236 if (constraints_to_expand.empty()) 03237 unexpanded_set.erase(i++); 03238 else 03239 ++i; 03240 } 03241 03242 // In parallel we can't guarantee that nodes/dofs which constrain 03243 // others are on processors which are aware of that constraint, yet 03244 // we need such awareness for sparsity pattern generation. So send 03245 // other processors any constraints they might need to know about. 03246 this->scatter_constraints(mesh); 03247 03248 // Now that we have our root constraint dependencies sorted out, add 03249 // them to the send_list 03250 this->add_constraints_to_send_list(); 03251 } 03252 03253 03254 void DofMap::scatter_constraints(MeshBase& mesh) 03255 { 03256 // At this point each processor with a constrained node knows 03257 // the corresponding constraint row, but we also need each processor 03258 // with a constrainer node to know the corresponding row(s). 03259 03260 // This function must be run on all processors at once 03261 parallel_object_only(); 03262 03263 // Return immediately if there's nothing to gather 03264 if (this->n_processors() == 1) 03265 return; 03266 03267 // We might get to return immediately if none of the processors 03268 // found any constraints 03269 unsigned int has_constraints = !_dof_constraints.empty() 03270 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 03271 || !_node_constraints.empty() 03272 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS 03273 ; 03274 this->comm().max(has_constraints); 03275 if (!has_constraints) 03276 return; 03277 03278 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 03279 std::vector<std::set<dof_id_type> > pushed_node_ids(this->n_processors()); 03280 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS 03281 03282 std::vector<std::set<dof_id_type> > pushed_ids(this->n_processors()); 03283 03284 // Collect the dof constraints I need to push to each processor 03285 dof_id_type constrained_proc_id = 0; 03286 for (DofConstraints::iterator i = _dof_constraints.begin(); 03287 i != _dof_constraints.end(); ++i) 03288 { 03289 const dof_id_type constrained = i->first; 03290 while (constrained >= _end_df[constrained_proc_id]) 03291 constrained_proc_id++; 03292 03293 if (constrained_proc_id != this->processor_id()) 03294 continue; 03295 03296 DofConstraintRow &row = i->second; 03297 for (DofConstraintRow::iterator j = row.begin(); 03298 j != row.end(); ++j) 03299 { 03300 const dof_id_type constraining = j->first; 03301 03302 processor_id_type constraining_proc_id = 0; 03303 while (constraining >= _end_df[constraining_proc_id]) 03304 constraining_proc_id++; 03305 03306 if (constraining_proc_id != this->processor_id() && 03307 constraining_proc_id != constrained_proc_id) 03308 pushed_ids[constraining_proc_id].insert(constrained); 03309 } 03310 } 03311 03312 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 03313 // Collect the node constraints to push to each processor 03314 for (NodeConstraints::iterator i = _node_constraints.begin(); 03315 i != _node_constraints.end(); ++i) 03316 { 03317 const Node *constrained = i->first; 03318 03319 if (constrained->processor_id() != this->processor_id()) 03320 continue; 03321 03322 NodeConstraintRow &row = i->second.first; 03323 for (NodeConstraintRow::iterator j = row.begin(); 03324 j != row.end(); ++j) 03325 { 03326 const Node *constraining = j->first; 03327 03328 if (constraining->processor_id() != this->processor_id() && 03329 constraining->processor_id() != constrained->processor_id()) 03330 pushed_node_ids[constraining->processor_id()].insert(constrained->id()); 03331 } 03332 } 03333 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS 03334 03335 // Now trade constraint rows 03336 for (processor_id_type p = 0; p != this->n_processors(); ++p) 03337 { 03338 // Push to processor procup while receiving from procdown 03339 processor_id_type procup = 03340 cast_int<processor_id_type>((this->processor_id() + p) % 03341 this->n_processors()); 03342 processor_id_type procdown = 03343 cast_int<processor_id_type>((this->n_processors() + 03344 this->processor_id() - p) % 03345 this->n_processors()); 03346 03347 // Pack the dof constraint rows and rhs's to push to procup 03348 const std::size_t pushed_ids_size = pushed_ids[procup].size(); 03349 std::vector<std::vector<dof_id_type> > pushed_keys(pushed_ids_size); 03350 std::vector<std::vector<Real> > pushed_vals(pushed_ids_size); 03351 std::vector<Number> pushed_rhss(pushed_ids_size); 03352 03353 std::set<dof_id_type>::const_iterator it; 03354 std::size_t push_i; 03355 for (push_i = 0, it = pushed_ids[procup].begin(); 03356 it != pushed_ids[procup].end(); ++push_i, ++it) 03357 { 03358 const dof_id_type constrained = *it; 03359 DofConstraintRow &row = _dof_constraints[constrained]; 03360 std::size_t row_size = row.size(); 03361 pushed_keys[push_i].reserve(row_size); 03362 pushed_vals[push_i].reserve(row_size); 03363 for (DofConstraintRow::iterator j = row.begin(); 03364 j != row.end(); ++j) 03365 { 03366 pushed_keys[push_i].push_back(j->first); 03367 pushed_vals[push_i].push_back(j->second); 03368 } 03369 03370 DofConstraintValueMap::const_iterator rhsit = 03371 _primal_constraint_values.find(constrained); 03372 pushed_rhss[push_i] = (rhsit == _primal_constraint_values.end()) ? 03373 0 : rhsit->second; 03374 } 03375 03376 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 03377 // Pack the node constraint rows to push to procup 03378 const std::size_t pushed_node_ids_size = pushed_node_ids[procup].size(); 03379 std::vector<std::vector<dof_id_type> > pushed_node_keys(pushed_node_ids_size); 03380 std::vector<std::vector<Real> > pushed_node_vals(pushed_node_ids_size); 03381 std::vector<Point> pushed_node_offsets(pushed_node_ids_size); 03382 std::set<const Node*> pushed_nodes; 03383 03384 for (push_i = 0, it = pushed_node_ids[procup].begin(); 03385 it != pushed_node_ids[procup].end(); ++push_i, ++it) 03386 { 03387 const Node *constrained = mesh.node_ptr(*it); 03388 03389 if (constrained->processor_id() != procdown) 03390 pushed_nodes.insert(constrained); 03391 03392 NodeConstraintRow &row = _node_constraints[constrained].first; 03393 std::size_t row_size = row.size(); 03394 pushed_node_keys[push_i].reserve(row_size); 03395 pushed_node_vals[push_i].reserve(row_size); 03396 for (NodeConstraintRow::iterator j = row.begin(); 03397 j != row.end(); ++j) 03398 { 03399 const Node* constraining = j->first; 03400 03401 pushed_node_keys[push_i].push_back(constraining->id()); 03402 pushed_node_vals[push_i].push_back(j->second); 03403 03404 if (constraining->processor_id() != procup) 03405 pushed_nodes.insert(constraining); 03406 } 03407 pushed_node_offsets[push_i] = _node_constraints[constrained].second; 03408 } 03409 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS 03410 03411 // Trade pushed dof constraint rows 03412 std::vector<dof_id_type> pushed_ids_from_me 03413 (pushed_ids[procup].begin(), pushed_ids[procup].end()); 03414 std::vector<dof_id_type> pushed_ids_to_me; 03415 std::vector<std::vector<dof_id_type> > pushed_keys_to_me; 03416 std::vector<std::vector<Real> > pushed_vals_to_me; 03417 std::vector<Number> pushed_rhss_to_me; 03418 this->comm().send_receive(procup, pushed_ids_from_me, 03419 procdown, pushed_ids_to_me); 03420 this->comm().send_receive(procup, pushed_keys, 03421 procdown, pushed_keys_to_me); 03422 this->comm().send_receive(procup, pushed_vals, 03423 procdown, pushed_vals_to_me); 03424 this->comm().send_receive(procup, pushed_rhss, 03425 procdown, pushed_rhss_to_me); 03426 libmesh_assert_equal_to (pushed_ids_to_me.size(), pushed_keys_to_me.size()); 03427 libmesh_assert_equal_to (pushed_ids_to_me.size(), pushed_vals_to_me.size()); 03428 libmesh_assert_equal_to (pushed_ids_to_me.size(), pushed_rhss_to_me.size()); 03429 03430 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 03431 // Trade pushed node constraint rows 03432 std::vector<dof_id_type> pushed_node_ids_from_me 03433 (pushed_node_ids[procup].begin(), pushed_node_ids[procup].end()); 03434 std::vector<dof_id_type> pushed_node_ids_to_me; 03435 std::vector<std::vector<dof_id_type> > pushed_node_keys_to_me; 03436 std::vector<std::vector<Real> > pushed_node_vals_to_me; 03437 std::vector<Point> pushed_node_offsets_to_me; 03438 this->comm().send_receive(procup, pushed_node_ids_from_me, 03439 procdown, pushed_node_ids_to_me); 03440 this->comm().send_receive(procup, pushed_node_keys, 03441 procdown, pushed_node_keys_to_me); 03442 this->comm().send_receive(procup, pushed_node_vals, 03443 procdown, pushed_node_vals_to_me); 03444 this->comm().send_receive(procup, pushed_node_offsets, 03445 procdown, pushed_node_offsets_to_me); 03446 03447 // Constraining nodes might not even exist on our subset of 03448 // a distributed mesh, so let's make them exist. 03449 if (!mesh.is_serial()) 03450 this->comm().send_receive_packed_range 03451 (procup, &mesh, pushed_nodes.begin(), pushed_nodes.end(), 03452 procdown, &mesh, mesh_inserter_iterator<Node>(mesh)); 03453 03454 libmesh_assert_equal_to (pushed_node_ids_to_me.size(), pushed_node_keys_to_me.size()); 03455 libmesh_assert_equal_to (pushed_node_ids_to_me.size(), pushed_node_vals_to_me.size()); 03456 libmesh_assert_equal_to (pushed_node_ids_to_me.size(), pushed_node_offsets_to_me.size()); 03457 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS 03458 03459 // Add the dof constraints that I've been sent 03460 for (std::size_t i = 0; i != pushed_ids_to_me.size(); ++i) 03461 { 03462 libmesh_assert_equal_to (pushed_keys_to_me[i].size(), pushed_vals_to_me[i].size()); 03463 03464 dof_id_type constrained = pushed_ids_to_me[i]; 03465 03466 // If we don't already have a constraint for this dof, 03467 // add the one we were sent 03468 if (!this->is_constrained_dof(constrained)) 03469 { 03470 DofConstraintRow &row = _dof_constraints[constrained]; 03471 for (std::size_t j = 0; j != pushed_keys_to_me[i].size(); ++j) 03472 { 03473 row[pushed_keys_to_me[i][j]] = pushed_vals_to_me[i][j]; 03474 } 03475 if (pushed_rhss_to_me[i] != Number(0)) 03476 _primal_constraint_values[constrained] = pushed_rhss_to_me[i]; 03477 else 03478 _primal_constraint_values.erase(constrained); 03479 } 03480 } 03481 03482 #ifdef LIBMESH_ENABLE_NODE_CONSTRAINTS 03483 // Add the node constraints that I've been sent 03484 for (std::size_t i = 0; i != pushed_node_ids_to_me.size(); ++i) 03485 { 03486 libmesh_assert_equal_to (pushed_node_keys_to_me[i].size(), pushed_node_vals_to_me[i].size()); 03487 03488 dof_id_type constrained_id = pushed_node_ids_to_me[i]; 03489 03490 // If we don't already have a constraint for this node, 03491 // add the one we were sent 03492 const Node *constrained = mesh.node_ptr(constrained_id); 03493 if (!this->is_constrained_node(constrained)) 03494 { 03495 NodeConstraintRow &row = _node_constraints[constrained].first; 03496 for (std::size_t j = 0; j != pushed_node_keys_to_me[i].size(); ++j) 03497 { 03498 const Node *key_node = mesh.node_ptr(pushed_node_keys_to_me[i][j]); 03499 libmesh_assert(key_node); 03500 row[key_node] = pushed_node_vals_to_me[i][j]; 03501 } 03502 _node_constraints[constrained].second = pushed_node_offsets_to_me[i]; 03503 } 03504 } 03505 #endif // LIBMESH_ENABLE_NODE_CONSTRAINTS 03506 } 03507 03508 // Next we need to push constraints to processors which don't own 03509 // the constrained dof, don't own the constraining dof, but own an 03510 // element supporting the constraining dof. 03511 // 03512 // We need to be able to quickly look up constrained dof ids by what 03513 // constrains them, so that we can handle the case where we see a 03514 // foreign element containing one of our constraining DoF ids and we 03515 // need to push that constraint. 03516 // 03517 // Getting distributed adaptive sparsity patterns right is hard. 03518 03519 typedef std::map<dof_id_type, std::set<dof_id_type> > DofConstrainsMap; 03520 DofConstrainsMap dof_id_constrains; 03521 03522 for (DofConstraints::iterator i = _dof_constraints.begin(); 03523 i != _dof_constraints.end(); ++i) 03524 { 03525 const dof_id_type constrained = i->first; 03526 DofConstraintRow &row = i->second; 03527 for (DofConstraintRow::iterator j = row.begin(); 03528 j != row.end(); ++j) 03529 { 03530 const dof_id_type constraining = j->first; 03531 03532 dof_id_type constraining_proc_id = 0; 03533 while (constraining >= _end_df[constraining_proc_id]) 03534 constraining_proc_id++; 03535 03536 if (constraining_proc_id == this->processor_id()) 03537 dof_id_constrains[constraining].insert(constrained); 03538 } 03539 } 03540 03541 // Loop over all foreign elements, find any supporting our 03542 // constrained dof indices. 03543 pushed_ids.clear(); 03544 pushed_ids.resize(this->n_processors()); 03545 03546 MeshBase::const_element_iterator it = mesh.active_not_local_elements_begin(), 03547 end = mesh.active_not_local_elements_end(); 03548 for (; it != end; ++it) 03549 { 03550 const Elem *elem = *it; 03551 03552 std::vector<dof_id_type> my_dof_indices; 03553 this->dof_indices (elem, my_dof_indices); 03554 03555 for (std::size_t i=0; i != my_dof_indices.size(); ++i) 03556 { 03557 DofConstrainsMap::const_iterator dcmi = 03558 dof_id_constrains.find(my_dof_indices[i]); 03559 if (dcmi != dof_id_constrains.end()) 03560 { 03561 for (DofConstrainsMap::mapped_type::const_iterator mti = 03562 dcmi->second.begin(); 03563 mti != dcmi->second.end(); ++mti) 03564 { 03565 const dof_id_type constrained = *mti; 03566 03567 dof_id_type the_constrained_proc_id = 0; 03568 while (constrained >= _end_df[the_constrained_proc_id]) 03569 the_constrained_proc_id++; 03570 03571 const dof_id_type elemproc = elem->processor_id(); 03572 if (elemproc != the_constrained_proc_id) 03573 pushed_ids[elemproc].insert(constrained); 03574 } 03575 } 03576 } 03577 } 03578 03579 // One last trade of constraint rows 03580 for (processor_id_type p = 0; p != this->n_processors(); ++p) 03581 { 03582 // Push to processor procup while receiving from procdown 03583 processor_id_type procup = 03584 cast_int<processor_id_type>((this->processor_id() + p) % 03585 this->n_processors()); 03586 processor_id_type procdown = 03587 cast_int<processor_id_type>((this->n_processors() + 03588 this->processor_id() - p) % 03589 this->n_processors()); 03590 03591 // Pack the dof constraint rows and rhs's to push to procup 03592 const std::size_t pushed_ids_size = pushed_ids[procup].size(); 03593 std::vector<std::vector<dof_id_type> > pushed_keys(pushed_ids_size); 03594 std::vector<std::vector<Real> > pushed_vals(pushed_ids_size); 03595 std::vector<Number> pushed_rhss(pushed_ids_size); 03596 03597 // As long as we're declaring them outside the loop, let's initialize them too! 03598 std::set<dof_id_type>::const_iterator pushed_ids_iter = pushed_ids[procup].begin(); 03599 std::size_t push_i = 0; 03600 for ( ; pushed_ids_iter != pushed_ids[procup].end(); ++push_i, ++pushed_ids_iter) 03601 { 03602 const dof_id_type constrained = *pushed_ids_iter; 03603 DofConstraintRow &row = _dof_constraints[constrained]; 03604 std::size_t row_size = row.size(); 03605 pushed_keys[push_i].reserve(row_size); 03606 pushed_vals[push_i].reserve(row_size); 03607 for (DofConstraintRow::iterator j = row.begin(); 03608 j != row.end(); ++j) 03609 { 03610 pushed_keys[push_i].push_back(j->first); 03611 pushed_vals[push_i].push_back(j->second); 03612 } 03613 03614 DofConstraintValueMap::const_iterator rhsit = 03615 _primal_constraint_values.find(constrained); 03616 pushed_rhss[push_i] = (rhsit == _primal_constraint_values.end()) ? 03617 0 : rhsit->second; 03618 } 03619 03620 // Trade pushed dof constraint rows 03621 std::vector<dof_id_type> pushed_ids_from_me 03622 (pushed_ids[procup].begin(), pushed_ids[procup].end()); 03623 std::vector<dof_id_type> pushed_ids_to_me; 03624 std::vector<std::vector<dof_id_type> > pushed_keys_to_me; 03625 std::vector<std::vector<Real> > pushed_vals_to_me; 03626 std::vector<Number> pushed_rhss_to_me; 03627 this->comm().send_receive(procup, pushed_ids_from_me, 03628 procdown, pushed_ids_to_me); 03629 this->comm().send_receive(procup, pushed_keys, 03630 procdown, pushed_keys_to_me); 03631 this->comm().send_receive(procup, pushed_vals, 03632 procdown, pushed_vals_to_me); 03633 this->comm().send_receive(procup, pushed_rhss, 03634 procdown, pushed_rhss_to_me); 03635 libmesh_assert_equal_to (pushed_ids_to_me.size(), pushed_keys_to_me.size()); 03636 libmesh_assert_equal_to (pushed_ids_to_me.size(), pushed_vals_to_me.size()); 03637 libmesh_assert_equal_to (pushed_ids_to_me.size(), pushed_rhss_to_me.size()); 03638 03639 // Add the dof constraints that I've been sent 03640 for (std::size_t i = 0; i != pushed_ids_to_me.size(); ++i) 03641 { 03642 libmesh_assert_equal_to (pushed_keys_to_me[i].size(), pushed_vals_to_me[i].size()); 03643 03644 dof_id_type constrained = pushed_ids_to_me[i]; 03645 03646 // If we don't already have a constraint for this dof, 03647 // add the one we were sent 03648 if (!this->is_constrained_dof(constrained)) 03649 { 03650 DofConstraintRow &row = _dof_constraints[constrained]; 03651 for (std::size_t j = 0; j != pushed_keys_to_me[i].size(); ++j) 03652 { 03653 row[pushed_keys_to_me[i][j]] = pushed_vals_to_me[i][j]; 03654 } 03655 03656 if (pushed_rhss_to_me[i] != Number(0)) 03657 _primal_constraint_values[constrained] = pushed_rhss_to_me[i]; 03658 else 03659 _primal_constraint_values.erase(constrained); 03660 } 03661 } 03662 } 03663 } 03664 03665 03666 void DofMap::add_constraints_to_send_list() 03667 { 03668 // This function must be run on all processors at once 03669 parallel_object_only(); 03670 03671 // Return immediately if there's nothing to gather 03672 if (this->n_processors() == 1) 03673 return; 03674 03675 // We might get to return immediately if none of the processors 03676 // found any constraints 03677 unsigned int has_constraints = !_dof_constraints.empty(); 03678 this->comm().max(has_constraints); 03679 if (!has_constraints) 03680 return; 03681 03682 for (DofConstraints::iterator i = _dof_constraints.begin(); 03683 i != _dof_constraints.end(); ++i) 03684 { 03685 dof_id_type constrained_dof = i->first; 03686 03687 // We only need the dependencies of our own constrained dofs 03688 if (constrained_dof < this->first_dof() || 03689 constrained_dof >= this->end_dof()) 03690 continue; 03691 03692 DofConstraintRow& constraint_row = i->second; 03693 for (DofConstraintRow::const_iterator 03694 j=constraint_row.begin(); j != constraint_row.end(); 03695 ++j) 03696 { 03697 dof_id_type constraint_dependency = j->first; 03698 03699 // No point in adding one of our own dofs to the send_list 03700 if (constraint_dependency >= this->first_dof() && 03701 constraint_dependency < this->end_dof()) 03702 continue; 03703 03704 _send_list.push_back(constraint_dependency); 03705 } 03706 } 03707 } 03708 03709 03710 03711 #endif // LIBMESH_ENABLE_CONSTRAINTS 03712 03713 03714 #ifdef LIBMESH_ENABLE_AMR 03715 03716 void DofMap::constrain_p_dofs (unsigned int var, 03717 const Elem *elem, 03718 unsigned int s, 03719 unsigned int p) 03720 { 03721 // We're constraining dofs on elem which correspond to p refinement 03722 // levels above p - this only makes sense if elem's p refinement 03723 // level is above p. 03724 libmesh_assert_greater (elem->p_level(), p); 03725 libmesh_assert_less (s, elem->n_sides()); 03726 03727 const unsigned int sys_num = this->sys_number(); 03728 const unsigned int dim = elem->dim(); 03729 ElemType type = elem->type(); 03730 FEType low_p_fe_type = this->variable_type(var); 03731 FEType high_p_fe_type = this->variable_type(var); 03732 low_p_fe_type.order = static_cast<Order>(low_p_fe_type.order + p); 03733 high_p_fe_type.order = static_cast<Order>(high_p_fe_type.order + 03734 elem->p_level()); 03735 03736 const unsigned int n_nodes = elem->n_nodes(); 03737 for (unsigned int n = 0; n != n_nodes; ++n) 03738 if (elem->is_node_on_side(n, s)) 03739 { 03740 const Node * const node = elem->get_node(n); 03741 const unsigned int low_nc = 03742 FEInterface::n_dofs_at_node (dim, low_p_fe_type, type, n); 03743 const unsigned int high_nc = 03744 FEInterface::n_dofs_at_node (dim, high_p_fe_type, type, n); 03745 03746 // since we may be running this method concurrently 03747 // on multiple threads we need to acquire a lock 03748 // before modifying the _dof_constraints object. 03749 Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx); 03750 03751 if (elem->is_vertex(n)) 03752 { 03753 // Add "this is zero" constraint rows for high p vertex 03754 // dofs 03755 for (unsigned int i = low_nc; i != high_nc; ++i) 03756 { 03757 _dof_constraints[node->dof_number(sys_num,var,i)].clear(); 03758 _primal_constraint_values.erase(node->dof_number(sys_num,var,i)); 03759 } 03760 } 03761 else 03762 { 03763 const unsigned int total_dofs = node->n_comp(sys_num, var); 03764 libmesh_assert_greater_equal (total_dofs, high_nc); 03765 // Add "this is zero" constraint rows for high p 03766 // non-vertex dofs, which are numbered in reverse 03767 for (unsigned int j = low_nc; j != high_nc; ++j) 03768 { 03769 const unsigned int i = total_dofs - j - 1; 03770 _dof_constraints[node->dof_number(sys_num,var,i)].clear(); 03771 _primal_constraint_values.erase(node->dof_number(sys_num,var,i)); 03772 } 03773 } 03774 } 03775 } 03776 03777 #endif // LIBMESH_ENABLE_AMR 03778 03779 03780 #ifdef LIBMESH_ENABLE_DIRICHLET 03781 void DofMap::add_dirichlet_boundary (const DirichletBoundary& dirichlet_boundary) 03782 { 03783 _dirichlet_boundaries->push_back(new DirichletBoundary(dirichlet_boundary)); 03784 } 03785 03786 03787 void DofMap::add_adjoint_dirichlet_boundary 03788 (const DirichletBoundary& dirichlet_boundary, 03789 unsigned int qoi_index) 03790 { 03791 unsigned int old_size = cast_int<unsigned int> 03792 (_adjoint_dirichlet_boundaries.size()); 03793 for (unsigned int i = old_size; i <= qoi_index; ++i) 03794 _adjoint_dirichlet_boundaries.push_back(new DirichletBoundaries()); 03795 03796 _adjoint_dirichlet_boundaries[qoi_index]->push_back 03797 (new DirichletBoundary(dirichlet_boundary)); 03798 } 03799 03800 03801 bool DofMap::has_adjoint_dirichlet_boundaries(unsigned int q) const 03802 { 03803 if (_adjoint_dirichlet_boundaries.size() > q) 03804 return true; 03805 03806 return false; 03807 } 03808 03809 03810 const DirichletBoundaries * 03811 DofMap::get_adjoint_dirichlet_boundaries(unsigned int q) const 03812 { 03813 libmesh_assert_greater(_adjoint_dirichlet_boundaries.size(),q); 03814 return _adjoint_dirichlet_boundaries[q]; 03815 } 03816 03817 03818 DirichletBoundaries * 03819 DofMap::get_adjoint_dirichlet_boundaries(unsigned int q) 03820 { 03821 unsigned int old_size = cast_int<unsigned int> 03822 (_adjoint_dirichlet_boundaries.size()); 03823 for (unsigned int i = old_size; i <= q; ++i) 03824 _adjoint_dirichlet_boundaries.push_back(new DirichletBoundaries()); 03825 03826 return _adjoint_dirichlet_boundaries[q]; 03827 } 03828 03829 03830 void DofMap::remove_dirichlet_boundary (const DirichletBoundary& boundary_to_remove) 03831 { 03832 // Find a boundary condition matching the one to be removed 03833 std::vector<DirichletBoundary *>::iterator it = _dirichlet_boundaries->begin(); 03834 std::vector<DirichletBoundary *>::iterator end = _dirichlet_boundaries->end(); 03835 for (; it != end; ++it) 03836 { 03837 DirichletBoundary *bdy = *it; 03838 03839 if ((bdy->b == boundary_to_remove.b) && 03840 bdy->variables == boundary_to_remove.variables) 03841 break; 03842 } 03843 03844 // Delete it and remove it 03845 libmesh_assert (it != end); 03846 delete *it; 03847 _dirichlet_boundaries->erase(it); 03848 } 03849 03850 03851 void DofMap::remove_adjoint_dirichlet_boundary (const DirichletBoundary& boundary_to_remove, 03852 unsigned int qoi_index) 03853 { 03854 libmesh_assert_greater(_adjoint_dirichlet_boundaries.size(), 03855 qoi_index); 03856 03857 // Find a boundary condition matching the one to be removed 03858 std::vector<DirichletBoundary *>::iterator it = 03859 _adjoint_dirichlet_boundaries[qoi_index]->begin(); 03860 std::vector<DirichletBoundary *>::iterator end = 03861 _adjoint_dirichlet_boundaries[qoi_index]->end(); 03862 for (; it != end; ++it) 03863 { 03864 DirichletBoundary *bdy = *it; 03865 03866 if ((bdy->b == boundary_to_remove.b) && 03867 bdy->variables == boundary_to_remove.variables) 03868 break; 03869 } 03870 03871 // Delete it and remove it 03872 libmesh_assert (it != end); 03873 delete *it; 03874 _adjoint_dirichlet_boundaries[qoi_index]->erase(it); 03875 } 03876 03877 03878 DirichletBoundaries::~DirichletBoundaries() 03879 { 03880 for (std::vector<DirichletBoundary *>::iterator it = begin(); it != end(); ++it) 03881 delete *it; 03882 } 03883 03884 #endif // LIBMESH_ENABLE_DIRICHLET 03885 03886 03887 #ifdef LIBMESH_ENABLE_PERIODIC 03888 03889 void DofMap::add_periodic_boundary (const PeriodicBoundaryBase& periodic_boundary) 03890 { 03891 // See if we already have a periodic boundary associated myboundary... 03892 PeriodicBoundaryBase* existing_boundary = _periodic_boundaries->boundary(periodic_boundary.myboundary); 03893 03894 if ( existing_boundary == NULL ) 03895 { 03896 // ...if not, clone the input (and its inverse) and add them to the PeriodicBoundaries object 03897 PeriodicBoundaryBase* boundary = periodic_boundary.clone().release(); 03898 PeriodicBoundaryBase* inverse_boundary = periodic_boundary.clone(PeriodicBoundaryBase::INVERSE).release(); 03899 03900 // _periodic_boundaries takes ownership of the pointers 03901 _periodic_boundaries->insert(std::make_pair(boundary->myboundary, boundary)); 03902 _periodic_boundaries->insert(std::make_pair(inverse_boundary->myboundary, inverse_boundary)); 03903 } 03904 else 03905 { 03906 // ...otherwise, merge this object's variable IDs with the existing boundary object's. 03907 existing_boundary->merge(periodic_boundary); 03908 03909 // Do the same merging process for the inverse boundary. Note: the inverse better already exist! 03910 PeriodicBoundaryBase* inverse_boundary = _periodic_boundaries->boundary(periodic_boundary.pairedboundary); 03911 libmesh_assert(inverse_boundary); 03912 inverse_boundary->merge(periodic_boundary); 03913 } 03914 } 03915 03916 03917 03918 03919 void DofMap::add_periodic_boundary (const PeriodicBoundaryBase& boundary, const PeriodicBoundaryBase& inverse_boundary) 03920 { 03921 libmesh_assert_equal_to (boundary.myboundary, inverse_boundary.pairedboundary); 03922 libmesh_assert_equal_to (boundary.pairedboundary, inverse_boundary.myboundary); 03923 03924 // Allocate copies on the heap. The _periodic_boundaries object will manage this memory. 03925 // Note: this also means that the copy constructor for the PeriodicBoundary (or user class 03926 // derived therefrom) must be implemented! 03927 // PeriodicBoundary* p_boundary = new PeriodicBoundary(boundary); 03928 // PeriodicBoundary* p_inverse_boundary = new PeriodicBoundary(inverse_boundary); 03929 03930 // We can't use normal copy construction since this leads to slicing with derived classes. 03931 // Use clone() "virtual constructor" instead. But, this *requires* user to override the clone() 03932 // method. Note also that clone() allocates memory. In this case, the _periodic_boundaries object 03933 // takes responsibility for cleanup. 03934 PeriodicBoundaryBase* p_boundary = boundary.clone().release(); 03935 PeriodicBoundaryBase* p_inverse_boundary = inverse_boundary.clone().release(); 03936 03937 // Add the periodic boundary and its inverse to the PeriodicBoundaries data structure. The 03938 // PeriodicBoundaries data structure takes ownership of the pointers. 03939 _periodic_boundaries->insert(std::make_pair(p_boundary->myboundary, p_boundary)); 03940 _periodic_boundaries->insert(std::make_pair(p_inverse_boundary->myboundary, p_inverse_boundary)); 03941 } 03942 03943 03944 #endif 03945 03946 03947 } // namespace libMesh