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