$extrastylesheet
00001 // The libMesh Finite Element Library. 00002 // Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner 00003 00004 // This library is free software; you can redistribute it and/or 00005 // modify it under the terms of the GNU Lesser General Public 00006 // License as published by the Free Software Foundation; either 00007 // version 2.1 of the License, or (at your option) any later version. 00008 00009 // This library is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 // Lesser General Public License for more details. 00013 00014 // You should have received a copy of the GNU Lesser General Public 00015 // License along with this library; if not, write to the Free Software 00016 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00017 00018 00019 00020 // C++ includes 00021 #include <cstdlib> // *must* precede <cmath> for proper std:abs() on PGI, Sun Studio CC 00022 #include <cmath> // for std::sqrt 00023 00024 00025 // Local includes 00026 #include "libmesh/mesh_generation.h" 00027 #include "libmesh/unstructured_mesh.h" 00028 // #include "libmesh/elem.h" 00029 #include "libmesh/mesh_refinement.h" 00030 #include "libmesh/edge_edge2.h" 00031 #include "libmesh/edge_edge3.h" 00032 #include "libmesh/edge_edge4.h" 00033 #include "libmesh/face_tri3.h" 00034 #include "libmesh/face_tri6.h" 00035 #include "libmesh/face_quad4.h" 00036 #include "libmesh/face_quad8.h" 00037 #include "libmesh/face_quad9.h" 00038 #include "libmesh/cell_hex8.h" 00039 #include "libmesh/cell_hex20.h" 00040 #include "libmesh/cell_hex27.h" 00041 #include "libmesh/cell_prism6.h" 00042 #include "libmesh/cell_prism15.h" 00043 #include "libmesh/cell_prism18.h" 00044 #include "libmesh/cell_tet4.h" 00045 #include "libmesh/cell_pyramid5.h" 00046 #include "libmesh/libmesh_logging.h" 00047 #include "libmesh/boundary_info.h" 00048 #include "libmesh/sphere.h" 00049 #include "libmesh/mesh_modification.h" 00050 #include "libmesh/mesh_smoother_laplace.h" 00051 #include "libmesh/node_elem.h" 00052 #include "libmesh/vector_value.h" 00053 00054 namespace libMesh 00055 { 00056 00057 namespace MeshTools { 00058 namespace Generation { 00059 namespace Private { 00067 inline 00068 unsigned int idx(const ElemType type, 00069 const unsigned int nx, 00070 const unsigned int i, 00071 const unsigned int j) 00072 { 00073 switch(type) 00074 { 00075 case INVALID_ELEM: 00076 case QUAD4: 00077 case TRI3: 00078 { 00079 return i + j*(nx+1); 00080 } 00081 00082 case QUAD8: 00083 case QUAD9: 00084 case TRI6: 00085 { 00086 return i + j*(2*nx+1); 00087 } 00088 00089 default: 00090 libmesh_error_msg("ERROR: Unrecognized 2D element type."); 00091 } 00092 00093 return libMesh::invalid_uint; 00094 } 00095 00096 00097 00098 // Same as the function above, but for 3D elements 00099 inline 00100 unsigned int idx(const ElemType type, 00101 const unsigned int nx, 00102 const unsigned int ny, 00103 const unsigned int i, 00104 const unsigned int j, 00105 const unsigned int k) 00106 { 00107 switch(type) 00108 { 00109 case INVALID_ELEM: 00110 case HEX8: 00111 case PRISM6: 00112 { 00113 return i + (nx+1)*(j + k*(ny+1)); 00114 } 00115 00116 case HEX20: 00117 case HEX27: 00118 case TET4: // TET4's are created from an initial HEX27 discretization 00119 case TET10: // TET10's are created from an initial HEX27 discretization 00120 case PYRAMID5: // PYRAMID5's are created from an initial HEX27 discretization 00121 case PYRAMID13: 00122 case PYRAMID14: 00123 case PRISM15: 00124 case PRISM18: 00125 { 00126 return i + (2*nx+1)*(j + k*(2*ny+1)); 00127 } 00128 00129 default: 00130 libmesh_error_msg("ERROR: Unrecognized element type."); 00131 } 00132 00133 return libMesh::invalid_uint; 00134 } 00135 } // namespace Private 00136 } // namespace Generation 00137 } // namespace MeshTools 00138 00139 // ------------------------------------------------------------ 00140 // MeshTools::Generation function for mesh generation 00141 void MeshTools::Generation::build_cube(UnstructuredMesh& mesh, 00142 const unsigned int nx, 00143 const unsigned int ny, 00144 const unsigned int nz, 00145 const Real xmin, const Real xmax, 00146 const Real ymin, const Real ymax, 00147 const Real zmin, const Real zmax, 00148 const ElemType type, 00149 const bool gauss_lobatto_grid) 00150 { 00151 START_LOG("build_cube()", "MeshTools::Generation"); 00152 00153 // Declare that we are using the indexing utility routine 00154 // in the "Private" part of our current namespace. If this doesn't 00155 // work in GCC 2.95.3 we can either remove it or stop supporting 00156 // 2.95.3 altogether. 00157 // Changing this to import the whole namespace... just importing idx 00158 // causes an internal compiler error for Intel Compiler 11.0 on Linux 00159 // in debug mode. 00160 using namespace MeshTools::Generation::Private; 00161 00162 // Clear the mesh and start from scratch 00163 mesh.clear(); 00164 00165 BoundaryInfo& boundary_info = mesh.get_boundary_info(); 00166 00167 if (nz != 0) 00168 mesh.set_mesh_dimension(3); 00169 else if (ny != 0) 00170 mesh.set_mesh_dimension(2); 00171 else if (nx != 0) 00172 mesh.set_mesh_dimension(1); 00173 else 00174 mesh.set_mesh_dimension(0); 00175 00176 switch (mesh.mesh_dimension()) 00177 { 00178 //--------------------------------------------------------------------- 00179 // Build a 0D point 00180 case 0: 00181 { 00182 libmesh_assert_equal_to (nx, 0); 00183 libmesh_assert_equal_to (ny, 0); 00184 libmesh_assert_equal_to (nz, 0); 00185 00186 libmesh_assert (type == INVALID_ELEM || type == NODEELEM); 00187 00188 // Build one nodal element for the mesh 00189 mesh.add_point (Point(0, 0, 0), 0); 00190 Elem* elem = mesh.add_elem (new NodeElem); 00191 elem->set_node(0) = mesh.node_ptr(0); 00192 00193 break; 00194 } 00195 00196 00197 00198 //--------------------------------------------------------------------- 00199 // Build a 1D line 00200 case 1: 00201 { 00202 libmesh_assert_not_equal_to (nx, 0); 00203 libmesh_assert_equal_to (ny, 0); 00204 libmesh_assert_equal_to (nz, 0); 00205 libmesh_assert_less (xmin, xmax); 00206 00207 // Reserve elements 00208 switch (type) 00209 { 00210 case INVALID_ELEM: 00211 case EDGE2: 00212 case EDGE3: 00213 case EDGE4: 00214 { 00215 mesh.reserve_elem (nx); 00216 break; 00217 } 00218 00219 default: 00220 libmesh_error_msg("ERROR: Unrecognized 1D element type."); 00221 } 00222 00223 // Reserve nodes 00224 switch (type) 00225 { 00226 case INVALID_ELEM: 00227 case EDGE2: 00228 { 00229 mesh.reserve_nodes(nx+1); 00230 break; 00231 } 00232 00233 case EDGE3: 00234 { 00235 mesh.reserve_nodes(2*nx+1); 00236 break; 00237 } 00238 00239 case EDGE4: 00240 { 00241 mesh.reserve_nodes(3*nx+1); 00242 break; 00243 } 00244 00245 default: 00246 libmesh_error_msg("ERROR: Unrecognized 1D element type."); 00247 } 00248 00249 00250 // Build the nodes, depends on whether we're using linears, 00251 // quadratics or cubics and whether using uniform grid or Gauss-Lobatto 00252 unsigned int node_id = 0; 00253 switch(type) 00254 { 00255 case INVALID_ELEM: 00256 case EDGE2: 00257 { 00258 for (unsigned int i=0; i<=nx; i++) 00259 { 00260 if (gauss_lobatto_grid) 00261 mesh.add_point (Point(0.5*(std::cos(libMesh::pi*static_cast<Real>(nx-i)/static_cast<Real>(nx))+1.0), 00262 0, 00263 0), node_id++); 00264 else 00265 mesh.add_point (Point(static_cast<Real>(i)/static_cast<Real>(nx), 00266 0, 00267 0), node_id++); 00268 } 00269 break; 00270 } 00271 00272 case EDGE3: 00273 { 00274 for (unsigned int i=0; i<=2*nx; i++) 00275 { 00276 if (gauss_lobatto_grid) 00277 { 00278 // The x location of the point. 00279 Real x=0.; 00280 00281 // Shortcut quantities (do not depend on i) 00282 const Real c = std::cos( libMesh::pi*i / static_cast<Real>(2*nx) ); 00283 00284 // If i is even, compute a normal Gauss-Lobatto point 00285 if (i%2 == 0) 00286 x = 0.5*(1.0 - c); 00287 00288 // Otherwise, it is the average of the previous and next points 00289 else 00290 { 00291 Real cmin = std::cos( libMesh::pi*(i-1) / static_cast<Real>(2*nx) ); 00292 Real cmax = std::cos( libMesh::pi*(i+1) / static_cast<Real>(2*nx) ); 00293 00294 Real gl_xmin = 0.5*(1.0 - cmin); 00295 Real gl_xmax = 0.5*(1.0 - cmax); 00296 x = 0.5*(gl_xmin + gl_xmax); 00297 } 00298 00299 mesh.add_point (Point(x,0.,0.), node_id++); 00300 } 00301 else 00302 mesh.add_point (Point(static_cast<Real>(i)/static_cast<Real>(2*nx), 00303 0, 00304 0), node_id++); 00305 } 00306 break; 00307 } 00308 00309 case EDGE4: 00310 { 00311 for (unsigned int i=0; i<=3*nx; i++) 00312 { 00313 if (gauss_lobatto_grid) 00314 { 00315 // The x location of the point 00316 Real x=0.; 00317 00318 // Shortcut quantities 00319 const Real c = std::cos( libMesh::pi*i / static_cast<Real>(3*nx) ); 00320 00321 // If i is multiple of 3, compute a normal Gauss-Lobatto point 00322 if (i%3 == 0) 00323 x = 0.5*(1.0 - c); 00324 00325 // Otherwise, distribute points evenly within the element 00326 else 00327 { 00328 if(i%3 == 1) 00329 { 00330 Real cmin = std::cos( libMesh::pi*(i-1) / static_cast<Real>(3*nx) ); 00331 Real cmax = std::cos( libMesh::pi*(i+2) / static_cast<Real>(3*nx) ); 00332 00333 Real gl_xmin = 0.5*(1.0 - cmin); 00334 Real gl_xmax = 0.5*(1.0 - cmax); 00335 00336 x = (2.*gl_xmin + gl_xmax)/3.; 00337 } 00338 else 00339 if(i%3 == 2) 00340 { 00341 Real cmin = std::cos( libMesh::pi*(i-2) / static_cast<Real>(3*nx) ); 00342 Real cmax = std::cos( libMesh::pi*(i+1) / static_cast<Real>(3*nx) ); 00343 00344 Real gl_xmin = 0.5*(1.0 - cmin); 00345 Real gl_xmax = 0.5*(1.0 - cmax); 00346 00347 x = (gl_xmin + 2.*gl_xmax)/3.; 00348 } 00349 00350 } 00351 00352 mesh.add_point (Point(x,0.,0.), node_id++); 00353 } 00354 else 00355 mesh.add_point (Point(static_cast<Real>(i)/static_cast<Real>(3*nx), 00356 0, 00357 0), node_id++); 00358 } 00359 00360 00361 00362 break; 00363 } 00364 00365 default: 00366 libmesh_error_msg("ERROR: Unrecognized 1D element type."); 00367 00368 } 00369 00370 // Build the elements of the mesh 00371 switch(type) 00372 { 00373 case INVALID_ELEM: 00374 case EDGE2: 00375 { 00376 for (unsigned int i=0; i<nx; i++) 00377 { 00378 Elem* elem = mesh.add_elem (new Edge2); 00379 elem->set_node(0) = mesh.node_ptr(i); 00380 elem->set_node(1) = mesh.node_ptr(i+1); 00381 00382 if (i == 0) 00383 boundary_info.add_side(elem, 0, 0); 00384 00385 if (i == (nx-1)) 00386 boundary_info.add_side(elem, 1, 1); 00387 } 00388 break; 00389 } 00390 00391 case EDGE3: 00392 { 00393 for (unsigned int i=0; i<nx; i++) 00394 { 00395 Elem* elem = mesh.add_elem (new Edge3); 00396 elem->set_node(0) = mesh.node_ptr(2*i); 00397 elem->set_node(2) = mesh.node_ptr(2*i+1); 00398 elem->set_node(1) = mesh.node_ptr(2*i+2); 00399 00400 if (i == 0) 00401 boundary_info.add_side(elem, 0, 0); 00402 00403 if (i == (nx-1)) 00404 boundary_info.add_side(elem, 1, 1); 00405 } 00406 break; 00407 } 00408 00409 case EDGE4: 00410 { 00411 for (unsigned int i=0; i<nx; i++) 00412 { 00413 Elem* elem = mesh.add_elem (new Edge4); 00414 elem->set_node(0) = mesh.node_ptr(3*i); 00415 elem->set_node(2) = mesh.node_ptr(3*i+1); 00416 elem->set_node(3) = mesh.node_ptr(3*i+2); 00417 elem->set_node(1) = mesh.node_ptr(3*i+3); 00418 00419 if (i == 0) 00420 boundary_info.add_side(elem, 0, 0); 00421 00422 if (i == (nx-1)) 00423 boundary_info.add_side(elem, 1, 1); 00424 } 00425 break; 00426 } 00427 00428 default: 00429 libmesh_error_msg("ERROR: Unrecognized 1D element type."); 00430 } 00431 00432 // Scale the nodal positions 00433 for (unsigned int p=0; p<mesh.n_nodes(); p++) 00434 mesh.node(p)(0) = (mesh.node(p)(0))*(xmax-xmin) + xmin; 00435 00436 // Add sideset names to boundary info 00437 boundary_info.sideset_name(0) = "left"; 00438 boundary_info.sideset_name(1) = "right"; 00439 00440 // Add nodeset names to boundary info 00441 boundary_info.nodeset_name(0) = "left"; 00442 boundary_info.nodeset_name(1) = "right"; 00443 00444 break; 00445 } 00446 00447 00448 00449 00450 00451 00452 00453 00454 00455 00456 //--------------------------------------------------------------------- 00457 // Build a 2D quadrilateral 00458 case 2: 00459 { 00460 libmesh_assert_not_equal_to (nx, 0); 00461 libmesh_assert_not_equal_to (ny, 0); 00462 libmesh_assert_equal_to (nz, 0); 00463 libmesh_assert_less (xmin, xmax); 00464 libmesh_assert_less (ymin, ymax); 00465 00466 // Reserve elements. The TRI3 and TRI6 meshes 00467 // have twice as many elements... 00468 switch (type) 00469 { 00470 case INVALID_ELEM: 00471 case QUAD4: 00472 case QUAD8: 00473 case QUAD9: 00474 { 00475 mesh.reserve_elem (nx*ny); 00476 break; 00477 } 00478 00479 case TRI3: 00480 case TRI6: 00481 { 00482 mesh.reserve_elem (2*nx*ny); 00483 break; 00484 } 00485 00486 default: 00487 libmesh_error_msg("ERROR: Unrecognized 2D element type."); 00488 } 00489 00490 00491 00492 // Reserve nodes. The quadratic element types 00493 // need to reserve more nodes than the linear types. 00494 switch (type) 00495 { 00496 case INVALID_ELEM: 00497 case QUAD4: 00498 case TRI3: 00499 { 00500 mesh.reserve_nodes( (nx+1)*(ny+1) ); 00501 break; 00502 } 00503 00504 case QUAD8: 00505 case QUAD9: 00506 case TRI6: 00507 { 00508 mesh.reserve_nodes( (2*nx+1)*(2*ny+1) ); 00509 break; 00510 } 00511 00512 00513 default: 00514 libmesh_error_msg("ERROR: Unrecognized 2D element type."); 00515 } 00516 00517 00518 00519 // Build the nodes. Depends on whether you are using a linear 00520 // or quadratic element, and whether you are using a uniform 00521 // grid or the Gauss-Lobatto grid points. 00522 unsigned int node_id = 0; 00523 switch (type) 00524 { 00525 case INVALID_ELEM: 00526 case QUAD4: 00527 case TRI3: 00528 { 00529 for (unsigned int j=0; j<=ny; j++) 00530 for (unsigned int i=0; i<=nx; i++) 00531 { 00532 if (gauss_lobatto_grid) 00533 { 00534 mesh.add_point (Point(0.5*(1.0 - std::cos(libMesh::pi*static_cast<Real>(i)/static_cast<Real>(nx))), 00535 0.5*(1.0 - std::cos(libMesh::pi*static_cast<Real>(j)/static_cast<Real>(ny))), 00536 0.), node_id++); 00537 } 00538 00539 else 00540 mesh.add_point (Point(static_cast<Real>(i)/static_cast<Real>(nx), 00541 static_cast<Real>(j)/static_cast<Real>(ny), 00542 0.), node_id++); 00543 } 00544 00545 break; 00546 } 00547 00548 case QUAD8: 00549 case QUAD9: 00550 case TRI6: 00551 { 00552 for (unsigned int j=0; j<=(2*ny); j++) 00553 for (unsigned int i=0; i<=(2*nx); i++) 00554 { 00555 if (gauss_lobatto_grid) 00556 { 00557 // The x,y locations of the point. 00558 Real x=0., y=0.; 00559 00560 // Shortcut quantities (do not depend on i,j) 00561 const Real a = std::cos( libMesh::pi / static_cast<Real>(2*nx) ); 00562 const Real b = std::cos( libMesh::pi / static_cast<Real>(2*ny) ); 00563 00564 // Shortcut quantities (depend on i,j) 00565 const Real c = std::cos( libMesh::pi*i / static_cast<Real>(2*nx) ); 00566 const Real d = std::cos( libMesh::pi*j / static_cast<Real>(2*ny) ); 00567 00568 // If i is even, compute a normal Gauss-Lobatto point 00569 if (i%2 == 0) 00570 x = 0.5*(1.0 - c); 00571 00572 // Otherwise, it is the average of the previous and next points 00573 else 00574 x = 0.5*(1.0 - a*c); 00575 00576 // If j is even, compute a normal Gauss-Lobatto point 00577 if (j%2 == 0) 00578 y = 0.5*(1.0 - d); 00579 00580 // Otherwise, it is the average of the previous and next points 00581 else 00582 y = 0.5*(1.0 - b*d); 00583 00584 00585 mesh.add_point (Point(x,y,0.), node_id++); 00586 } 00587 00588 00589 else 00590 mesh.add_point (Point(static_cast<Real>(i)/static_cast<Real>(2*nx), 00591 static_cast<Real>(j)/static_cast<Real>(2*ny), 00592 0), node_id++); 00593 } 00594 00595 break; 00596 } 00597 00598 00599 default: 00600 libmesh_error_msg("ERROR: Unrecognized 2D element type."); 00601 } 00602 00603 00604 00605 00606 00607 00608 // Build the elements. Each one is a bit different. 00609 switch (type) 00610 { 00611 00612 case INVALID_ELEM: 00613 case QUAD4: 00614 { 00615 for (unsigned int j=0; j<ny; j++) 00616 for (unsigned int i=0; i<nx; i++) 00617 { 00618 Elem* elem = mesh.add_elem(new Quad4); 00619 00620 elem->set_node(0) = mesh.node_ptr(idx(type,nx,i,j) ); 00621 elem->set_node(1) = mesh.node_ptr(idx(type,nx,i+1,j) ); 00622 elem->set_node(2) = mesh.node_ptr(idx(type,nx,i+1,j+1)); 00623 elem->set_node(3) = mesh.node_ptr(idx(type,nx,i,j+1) ); 00624 00625 if (j == 0) 00626 boundary_info.add_side(elem, 0, 0); 00627 00628 if (j == (ny-1)) 00629 boundary_info.add_side(elem, 2, 2); 00630 00631 if (i == 0) 00632 boundary_info.add_side(elem, 3, 3); 00633 00634 if (i == (nx-1)) 00635 boundary_info.add_side(elem, 1, 1); 00636 } 00637 break; 00638 } 00639 00640 00641 case TRI3: 00642 { 00643 for (unsigned int j=0; j<ny; j++) 00644 for (unsigned int i=0; i<nx; i++) 00645 { 00646 Elem* elem = NULL; 00647 00648 // Add first Tri3 00649 elem = mesh.add_elem(new Tri3); 00650 00651 elem->set_node(0) = mesh.node_ptr(idx(type,nx,i,j) ); 00652 elem->set_node(1) = mesh.node_ptr(idx(type,nx,i+1,j) ); 00653 elem->set_node(2) = mesh.node_ptr(idx(type,nx,i+1,j+1)); 00654 00655 if (j == 0) 00656 boundary_info.add_side(elem, 0, 0); 00657 00658 if (i == (nx-1)) 00659 boundary_info.add_side(elem, 1, 1); 00660 00661 // Add second Tri3 00662 elem = mesh.add_elem(new Tri3); 00663 00664 elem->set_node(0) = mesh.node_ptr(idx(type,nx,i,j) ); 00665 elem->set_node(1) = mesh.node_ptr(idx(type,nx,i+1,j+1)); 00666 elem->set_node(2) = mesh.node_ptr(idx(type,nx,i,j+1) ); 00667 00668 if (j == (ny-1)) 00669 boundary_info.add_side(elem, 1, 2); 00670 00671 if (i == 0) 00672 boundary_info.add_side(elem, 2, 3); 00673 } 00674 break; 00675 } 00676 00677 00678 00679 case QUAD8: 00680 case QUAD9: 00681 { 00682 for (unsigned int j=0; j<(2*ny); j += 2) 00683 for (unsigned int i=0; i<(2*nx); i += 2) 00684 { 00685 Elem* elem = (type == QUAD8) ? 00686 mesh.add_elem(new Quad8) : 00687 mesh.add_elem(new Quad9); 00688 00689 00690 elem->set_node(0) = mesh.node_ptr(idx(type,nx,i,j) ); 00691 elem->set_node(1) = mesh.node_ptr(idx(type,nx,i+2,j) ); 00692 elem->set_node(2) = mesh.node_ptr(idx(type,nx,i+2,j+2)); 00693 elem->set_node(3) = mesh.node_ptr(idx(type,nx,i,j+2) ); 00694 elem->set_node(4) = mesh.node_ptr(idx(type,nx,i+1,j) ); 00695 elem->set_node(5) = mesh.node_ptr(idx(type,nx,i+2,j+1)); 00696 elem->set_node(6) = mesh.node_ptr(idx(type,nx,i+1,j+2)); 00697 elem->set_node(7) = mesh.node_ptr(idx(type,nx,i,j+1) ); 00698 if (type == QUAD9) 00699 elem->set_node(8) = mesh.node_ptr(idx(type,nx,i+1,j+1)); 00700 00701 00702 if (j == 0) 00703 boundary_info.add_side(elem, 0, 0); 00704 00705 if (j == 2*(ny-1)) 00706 boundary_info.add_side(elem, 2, 2); 00707 00708 if (i == 0) 00709 boundary_info.add_side(elem, 3, 3); 00710 00711 if (i == 2*(nx-1)) 00712 boundary_info.add_side(elem, 1, 1); 00713 } 00714 break; 00715 } 00716 00717 00718 case TRI6: 00719 { 00720 for (unsigned int j=0; j<(2*ny); j += 2) 00721 for (unsigned int i=0; i<(2*nx); i += 2) 00722 { 00723 Elem* elem = NULL; 00724 00725 // Add first Tri6 00726 elem = mesh.add_elem(new Tri6); 00727 00728 elem->set_node(0) = mesh.node_ptr(idx(type,nx,i,j) ); 00729 elem->set_node(1) = mesh.node_ptr(idx(type,nx,i+2,j) ); 00730 elem->set_node(2) = mesh.node_ptr(idx(type,nx,i+2,j+2)); 00731 elem->set_node(3) = mesh.node_ptr(idx(type,nx,i+1,j) ); 00732 elem->set_node(4) = mesh.node_ptr(idx(type,nx,i+2,j+1)); 00733 elem->set_node(5) = mesh.node_ptr(idx(type,nx,i+1,j+1)); 00734 00735 if (j == 0) 00736 boundary_info.add_side(elem, 0, 0); 00737 00738 if (i == 2*(nx-1)) 00739 boundary_info.add_side(elem, 1, 1); 00740 00741 // Add second Tri6 00742 elem = mesh.add_elem(new Tri6); 00743 00744 elem->set_node(0) = mesh.node_ptr(idx(type,nx,i,j) ); 00745 elem->set_node(1) = mesh.node_ptr(idx(type,nx,i+2,j+2)); 00746 elem->set_node(2) = mesh.node_ptr(idx(type,nx,i,j+2) ); 00747 elem->set_node(3) = mesh.node_ptr(idx(type,nx,i+1,j+1)); 00748 elem->set_node(4) = mesh.node_ptr(idx(type,nx,i+1,j+2)); 00749 elem->set_node(5) = mesh.node_ptr(idx(type,nx,i,j+1) ); 00750 00751 if (j == 2*(ny-1)) 00752 boundary_info.add_side(elem, 1, 2); 00753 00754 if (i == 0) 00755 boundary_info.add_side(elem, 2, 3); 00756 00757 } 00758 break; 00759 }; 00760 00761 00762 default: 00763 libmesh_error_msg("ERROR: Unrecognized 2D element type."); 00764 } 00765 00766 00767 00768 00769 // Scale the nodal positions 00770 for (unsigned int p=0; p<mesh.n_nodes(); p++) 00771 { 00772 mesh.node(p)(0) = (mesh.node(p)(0))*(xmax-xmin) + xmin; 00773 mesh.node(p)(1) = (mesh.node(p)(1))*(ymax-ymin) + ymin; 00774 } 00775 00776 // Add sideset names to boundary info 00777 boundary_info.sideset_name(0) = "bottom"; 00778 boundary_info.sideset_name(1) = "right"; 00779 boundary_info.sideset_name(2) = "top"; 00780 boundary_info.sideset_name(3) = "left"; 00781 00782 // Add nodeset names to boundary info 00783 boundary_info.nodeset_name(0) = "bottom"; 00784 boundary_info.nodeset_name(1) = "right"; 00785 boundary_info.nodeset_name(2) = "top"; 00786 boundary_info.nodeset_name(3) = "left"; 00787 00788 break; 00789 } 00790 00791 00792 00793 00794 00795 00796 00797 00798 00799 00800 00801 //--------------------------------------------------------------------- 00802 // Build a 3D mesh using hexes, tets, prisms, or pyramids. 00803 case 3: 00804 { 00805 libmesh_assert_not_equal_to (nx, 0); 00806 libmesh_assert_not_equal_to (ny, 0); 00807 libmesh_assert_not_equal_to (nz, 0); 00808 libmesh_assert_less (xmin, xmax); 00809 libmesh_assert_less (ymin, ymax); 00810 libmesh_assert_less (zmin, zmax); 00811 00812 00813 // Reserve elements. Meshes with prismatic elements require 00814 // twice as many elements. 00815 switch (type) 00816 { 00817 case INVALID_ELEM: 00818 case HEX8: 00819 case HEX20: 00820 case HEX27: 00821 case TET4: // TET4's are created from an initial HEX27 discretization 00822 case TET10: // TET10's are created from an initial HEX27 discretization 00823 case PYRAMID5: // PYRAMIDs are created from an initial HEX27 discretization 00824 case PYRAMID13: 00825 case PYRAMID14: 00826 { 00827 mesh.reserve_elem(nx*ny*nz); 00828 break; 00829 } 00830 00831 case PRISM6: 00832 case PRISM15: 00833 case PRISM18: 00834 { 00835 mesh.reserve_elem(2*nx*ny*nz); 00836 break; 00837 } 00838 00839 default: 00840 libmesh_error_msg("ERROR: Unrecognized 3D element type."); 00841 } 00842 00843 00844 00845 00846 00847 // Reserve nodes. Quadratic elements need twice as many nodes as linear elements. 00848 switch (type) 00849 { 00850 case INVALID_ELEM: 00851 case HEX8: 00852 case PRISM6: 00853 { 00854 mesh.reserve_nodes( (nx+1)*(ny+1)*(nz+1) ); 00855 break; 00856 } 00857 00858 case HEX20: 00859 case HEX27: 00860 case TET4: // TET4's are created from an initial HEX27 discretization 00861 case TET10: // TET10's are created from an initial HEX27 discretization 00862 case PYRAMID5: // PYRAMIDs are created from an initial HEX27 discretization 00863 case PYRAMID13: 00864 case PYRAMID14: 00865 case PRISM15: 00866 case PRISM18: 00867 { 00868 // FYI: The resulting TET4 mesh will have exactly 00869 // 5*(nx*ny*nz) + 2*(nx*ny + nx*nz + ny*nz) + (nx+ny+nz) + 1 00870 // nodes once the additional mid-edge nodes for the HEX27 discretization 00871 // have been deleted. 00872 mesh.reserve_nodes( (2*nx+1)*(2*ny+1)*(2*nz+1) ); 00873 break; 00874 } 00875 00876 default: 00877 libmesh_error_msg("ERROR: Unrecognized 3D element type."); 00878 } 00879 00880 00881 00882 00883 // Build the nodes. 00884 unsigned int node_id = 0; 00885 switch (type) 00886 { 00887 case INVALID_ELEM: 00888 case HEX8: 00889 case PRISM6: 00890 { 00891 for (unsigned int k=0; k<=nz; k++) 00892 for (unsigned int j=0; j<=ny; j++) 00893 for (unsigned int i=0; i<=nx; i++) 00894 { 00895 if (gauss_lobatto_grid) 00896 { 00897 mesh.add_point (Point(0.5*(1.0 - std::cos(libMesh::pi*static_cast<Real>(i)/static_cast<Real>(nx))), 00898 0.5*(1.0 - std::cos(libMesh::pi*static_cast<Real>(j)/static_cast<Real>(ny))), 00899 0.5*(1.0 - std::cos(libMesh::pi*static_cast<Real>(k)/static_cast<Real>(nz)))), node_id++); 00900 } 00901 00902 else 00903 mesh.add_point(Point(static_cast<Real>(i)/static_cast<Real>(nx), 00904 static_cast<Real>(j)/static_cast<Real>(ny), 00905 static_cast<Real>(k)/static_cast<Real>(nz)), node_id++); 00906 } 00907 break; 00908 } 00909 00910 case HEX20: 00911 case HEX27: 00912 case TET4: // TET4's are created from an initial HEX27 discretization 00913 case TET10: // TET10's are created from an initial HEX27 discretization 00914 case PYRAMID5: // PYRAMIDs are created from an initial HEX27 discretization 00915 case PYRAMID13: 00916 case PYRAMID14: 00917 case PRISM15: 00918 case PRISM18: 00919 { 00920 for (unsigned int k=0; k<=(2*nz); k++) 00921 for (unsigned int j=0; j<=(2*ny); j++) 00922 for (unsigned int i=0; i<=(2*nx); i++) 00923 { 00924 if (gauss_lobatto_grid) 00925 { 00926 // The x,y locations of the point. 00927 Real x=0., y=0., z=0.; 00928 00929 // Shortcut quantities (do not depend on i,j) 00930 const Real a = std::cos( libMesh::pi / static_cast<Real>(2*nx) ); 00931 const Real b = std::cos( libMesh::pi / static_cast<Real>(2*ny) ); 00932 00933 // Shortcut quantities (depend on i,j) 00934 const Real c = std::cos( libMesh::pi*i / static_cast<Real>(2*nx) ); 00935 const Real d = std::cos( libMesh::pi*j / static_cast<Real>(2*ny) ); 00936 00937 // Additional shortcut quantities (for 3D) 00938 const Real e = std::cos( libMesh::pi / static_cast<Real>(2*nz) ); 00939 const Real f = std::cos( libMesh::pi*k / static_cast<Real>(2*nz) ); 00940 00941 // If i is even, compute a normal Gauss-Lobatto point 00942 if (i%2 == 0) 00943 x = 0.5*(1.0 - c); 00944 00945 // Otherwise, it is the average of the previous and next points 00946 else 00947 x = 0.5*(1.0 - a*c); 00948 00949 // If j is even, compute a normal Gauss-Lobatto point 00950 if (j%2 == 0) 00951 y = 0.5*(1.0 - d); 00952 00953 // Otherwise, it is the average of the previous and next points 00954 else 00955 y = 0.5*(1.0 - b*d); 00956 00957 // If k is even, compute a normal Gauss-Lobatto point 00958 if (k%2 == 0) 00959 z = 0.5*(1.0 - f); 00960 00961 // Otherwise, it is the average of the previous and next points 00962 else 00963 z = 0.5*(1.0 - e*f); 00964 00965 00966 mesh.add_point (Point(x,y,z), node_id++); 00967 } 00968 00969 else 00970 mesh.add_point(Point(static_cast<Real>(i)/static_cast<Real>(2*nx), 00971 static_cast<Real>(j)/static_cast<Real>(2*ny), 00972 static_cast<Real>(k)/static_cast<Real>(2*nz)), node_id++); 00973 } 00974 break; 00975 } 00976 00977 00978 default: 00979 libmesh_error_msg("ERROR: Unrecognized 3D element type."); 00980 } 00981 00982 00983 00984 00985 // Build the elements. 00986 switch (type) 00987 { 00988 case INVALID_ELEM: 00989 case HEX8: 00990 { 00991 for (unsigned int k=0; k<nz; k++) 00992 for (unsigned int j=0; j<ny; j++) 00993 for (unsigned int i=0; i<nx; i++) 00994 { 00995 Elem* elem = mesh.add_elem(new Hex8); 00996 00997 elem->set_node(0) = mesh.node_ptr(idx(type,nx,ny,i,j,k) ); 00998 elem->set_node(1) = mesh.node_ptr(idx(type,nx,ny,i+1,j,k) ); 00999 elem->set_node(2) = mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k) ); 01000 elem->set_node(3) = mesh.node_ptr(idx(type,nx,ny,i,j+1,k) ); 01001 elem->set_node(4) = mesh.node_ptr(idx(type,nx,ny,i,j,k+1) ); 01002 elem->set_node(5) = mesh.node_ptr(idx(type,nx,ny,i+1,j,k+1) ); 01003 elem->set_node(6) = mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)); 01004 elem->set_node(7) = mesh.node_ptr(idx(type,nx,ny,i,j+1,k+1) ); 01005 01006 if (k == 0) 01007 boundary_info.add_side(elem, 0, 0); 01008 01009 if (k == (nz-1)) 01010 boundary_info.add_side(elem, 5, 5); 01011 01012 if (j == 0) 01013 boundary_info.add_side(elem, 1, 1); 01014 01015 if (j == (ny-1)) 01016 boundary_info.add_side(elem, 3, 3); 01017 01018 if (i == 0) 01019 boundary_info.add_side(elem, 4, 4); 01020 01021 if (i == (nx-1)) 01022 boundary_info.add_side(elem, 2, 2); 01023 } 01024 break; 01025 } 01026 01027 01028 01029 01030 case PRISM6: 01031 { 01032 for (unsigned int k=0; k<nz; k++) 01033 for (unsigned int j=0; j<ny; j++) 01034 for (unsigned int i=0; i<nx; i++) 01035 { 01036 // First Prism 01037 Elem* elem = NULL; 01038 elem = mesh.add_elem(new Prism6); 01039 01040 elem->set_node(0) = mesh.node_ptr(idx(type,nx,ny,i,j,k) ); 01041 elem->set_node(1) = mesh.node_ptr(idx(type,nx,ny,i+1,j,k) ); 01042 elem->set_node(2) = mesh.node_ptr(idx(type,nx,ny,i,j+1,k) ); 01043 elem->set_node(3) = mesh.node_ptr(idx(type,nx,ny,i,j,k+1) ); 01044 elem->set_node(4) = mesh.node_ptr(idx(type,nx,ny,i+1,j,k+1) ); 01045 elem->set_node(5) = mesh.node_ptr(idx(type,nx,ny,i,j+1,k+1) ); 01046 01047 // Add sides for first prism to boundary info object 01048 if (i==0) 01049 boundary_info.add_side(elem, 3, 4); 01050 01051 if (j==0) 01052 boundary_info.add_side(elem, 1, 1); 01053 01054 if (k==0) 01055 boundary_info.add_side(elem, 0, 0); 01056 01057 if (k == (nz-1)) 01058 boundary_info.add_side(elem, 4, 5); 01059 01060 // Second Prism 01061 elem = mesh.add_elem(new Prism6); 01062 01063 elem->set_node(0) = mesh.node_ptr(idx(type,nx,ny,i+1,j,k) ); 01064 elem->set_node(1) = mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k) ); 01065 elem->set_node(2) = mesh.node_ptr(idx(type,nx,ny,i,j+1,k) ); 01066 elem->set_node(3) = mesh.node_ptr(idx(type,nx,ny,i+1,j,k+1) ); 01067 elem->set_node(4) = mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)); 01068 elem->set_node(5) = mesh.node_ptr(idx(type,nx,ny,i,j+1,k+1) ); 01069 01070 // Add sides for second prism to boundary info object 01071 if (i == (nx-1)) 01072 boundary_info.add_side(elem, 1, 2); 01073 01074 if (j == (ny-1)) 01075 boundary_info.add_side(elem, 2, 3); 01076 01077 if (k==0) 01078 boundary_info.add_side(elem, 0, 0); 01079 01080 if (k == (nz-1)) 01081 boundary_info.add_side(elem, 4, 5); 01082 } 01083 break; 01084 } 01085 01086 01087 01088 01089 01090 01091 case HEX20: 01092 case HEX27: 01093 case TET4: // TET4's are created from an initial HEX27 discretization 01094 case TET10: // TET10's are created from an initial HEX27 discretization 01095 case PYRAMID5: // PYRAMIDs are created from an initial HEX27 discretization 01096 case PYRAMID13: 01097 case PYRAMID14: 01098 { 01099 for (unsigned int k=0; k<(2*nz); k += 2) 01100 for (unsigned int j=0; j<(2*ny); j += 2) 01101 for (unsigned int i=0; i<(2*nx); i += 2) 01102 { 01103 Elem* elem = (type == HEX20) ? 01104 mesh.add_elem(new Hex20) : 01105 mesh.add_elem(new Hex27); 01106 01107 elem->set_node(0) = mesh.node_ptr(idx(type,nx,ny,i, j, k) ); 01108 elem->set_node(1) = mesh.node_ptr(idx(type,nx,ny,i+2,j, k) ); 01109 elem->set_node(2) = mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k) ); 01110 elem->set_node(3) = mesh.node_ptr(idx(type,nx,ny,i, j+2,k) ); 01111 elem->set_node(4) = mesh.node_ptr(idx(type,nx,ny,i, j, k+2)); 01112 elem->set_node(5) = mesh.node_ptr(idx(type,nx,ny,i+2,j, k+2)); 01113 elem->set_node(6) = mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k+2)); 01114 elem->set_node(7) = mesh.node_ptr(idx(type,nx,ny,i, j+2,k+2)); 01115 elem->set_node(8) = mesh.node_ptr(idx(type,nx,ny,i+1,j, k) ); 01116 elem->set_node(9) = mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k) ); 01117 elem->set_node(10) = mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k) ); 01118 elem->set_node(11) = mesh.node_ptr(idx(type,nx,ny,i, j+1,k) ); 01119 elem->set_node(12) = mesh.node_ptr(idx(type,nx,ny,i, j, k+1)); 01120 elem->set_node(13) = mesh.node_ptr(idx(type,nx,ny,i+2,j, k+1)); 01121 elem->set_node(14) = mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k+1)); 01122 elem->set_node(15) = mesh.node_ptr(idx(type,nx,ny,i, j+2,k+1)); 01123 elem->set_node(16) = mesh.node_ptr(idx(type,nx,ny,i+1,j, k+2)); 01124 elem->set_node(17) = mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k+2)); 01125 elem->set_node(18) = mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k+2)); 01126 elem->set_node(19) = mesh.node_ptr(idx(type,nx,ny,i, j+1,k+2)); 01127 if ((type == HEX27) || (type == TET4) || (type == TET10) || 01128 (type == PYRAMID5) || (type == PYRAMID13) || (type == PYRAMID14)) 01129 { 01130 elem->set_node(20) = mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k) ); 01131 elem->set_node(21) = mesh.node_ptr(idx(type,nx,ny,i+1,j, k+1)); 01132 elem->set_node(22) = mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k+1)); 01133 elem->set_node(23) = mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k+1)); 01134 elem->set_node(24) = mesh.node_ptr(idx(type,nx,ny,i, j+1,k+1)); 01135 elem->set_node(25) = mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+2)); 01136 elem->set_node(26) = mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)); 01137 } 01138 01139 01140 if (k == 0) 01141 boundary_info.add_side(elem, 0, 0); 01142 01143 if (k == 2*(nz-1)) 01144 boundary_info.add_side(elem, 5, 5); 01145 01146 if (j == 0) 01147 boundary_info.add_side(elem, 1, 1); 01148 01149 if (j == 2*(ny-1)) 01150 boundary_info.add_side(elem, 3, 3); 01151 01152 if (i == 0) 01153 boundary_info.add_side(elem, 4, 4); 01154 01155 if (i == 2*(nx-1)) 01156 boundary_info.add_side(elem, 2, 2); 01157 } 01158 break; 01159 } 01160 01161 01162 01163 01164 case PRISM15: 01165 case PRISM18: 01166 { 01167 for (unsigned int k=0; k<(2*nz); k += 2) 01168 for (unsigned int j=0; j<(2*ny); j += 2) 01169 for (unsigned int i=0; i<(2*nx); i += 2) 01170 { 01171 // First Prism 01172 Elem* elem = NULL; 01173 elem = ((type == PRISM15) ? 01174 mesh.add_elem(new Prism15) : 01175 mesh.add_elem(new Prism18)); 01176 01177 elem->set_node(0) = mesh.node_ptr(idx(type,nx,ny,i, j, k) ); 01178 elem->set_node(1) = mesh.node_ptr(idx(type,nx,ny,i+2,j, k) ); 01179 elem->set_node(2) = mesh.node_ptr(idx(type,nx,ny,i, j+2,k) ); 01180 elem->set_node(3) = mesh.node_ptr(idx(type,nx,ny,i, j, k+2)); 01181 elem->set_node(4) = mesh.node_ptr(idx(type,nx,ny,i+2,j, k+2)); 01182 elem->set_node(5) = mesh.node_ptr(idx(type,nx,ny,i, j+2,k+2)); 01183 elem->set_node(6) = mesh.node_ptr(idx(type,nx,ny,i+1,j, k) ); 01184 elem->set_node(7) = mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k) ); 01185 elem->set_node(8) = mesh.node_ptr(idx(type,nx,ny,i, j+1,k) ); 01186 elem->set_node(9) = mesh.node_ptr(idx(type,nx,ny,i, j, k+1)); 01187 elem->set_node(10) = mesh.node_ptr(idx(type,nx,ny,i+2,j, k+1)); 01188 elem->set_node(11) = mesh.node_ptr(idx(type,nx,ny,i, j+2,k+1)); 01189 elem->set_node(12) = mesh.node_ptr(idx(type,nx,ny,i+1,j, k+2)); 01190 elem->set_node(13) = mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+2)); 01191 elem->set_node(14) = mesh.node_ptr(idx(type,nx,ny,i, j+1,k+2)); 01192 if (type == PRISM18) 01193 { 01194 elem->set_node(15) = mesh.node_ptr(idx(type,nx,ny,i+1,j, k+1)); 01195 elem->set_node(16) = mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)); 01196 elem->set_node(17) = mesh.node_ptr(idx(type,nx,ny,i, j+1,k+1)); 01197 } 01198 01199 // Add sides for first prism to boundary info object 01200 if (i==0) 01201 boundary_info.add_side(elem, 3, 4); 01202 01203 if (j==0) 01204 boundary_info.add_side(elem, 1, 1); 01205 01206 if (k==0) 01207 boundary_info.add_side(elem, 0, 0); 01208 01209 if (k == 2*(nz-1)) 01210 boundary_info.add_side(elem, 4, 5); 01211 01212 01213 // Second Prism 01214 elem = ((type == PRISM15) ? 01215 mesh.add_elem(new Prism15) : 01216 mesh.add_elem(new Prism18)); 01217 01218 elem->set_node(0) = mesh.node_ptr(idx(type,nx,ny,i+2,j,k) ); 01219 elem->set_node(1) = mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k) ); 01220 elem->set_node(2) = mesh.node_ptr(idx(type,nx,ny,i,j+2,k) ); 01221 elem->set_node(3) = mesh.node_ptr(idx(type,nx,ny,i+2,j,k+2) ); 01222 elem->set_node(4) = mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k+2) ); 01223 elem->set_node(5) = mesh.node_ptr(idx(type,nx,ny,i,j+2,k+2) ); 01224 elem->set_node(6) = mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k) ); 01225 elem->set_node(7) = mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k) ); 01226 elem->set_node(8) = mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k) ); 01227 elem->set_node(9) = mesh.node_ptr(idx(type,nx,ny,i+2,j,k+1) ); 01228 elem->set_node(10) = mesh.node_ptr(idx(type,nx,ny,i+2,j+2,k+1)); 01229 elem->set_node(11) = mesh.node_ptr(idx(type,nx,ny,i,j+2,k+1) ); 01230 elem->set_node(12) = mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k+2)); 01231 elem->set_node(13) = mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k+2)); 01232 elem->set_node(14) = mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+2)); 01233 if (type == PRISM18) 01234 { 01235 elem->set_node(15) = mesh.node_ptr(idx(type,nx,ny,i+2,j+1,k+1)); 01236 elem->set_node(16) = mesh.node_ptr(idx(type,nx,ny,i+1,j+2,k+1)); 01237 elem->set_node(17) = mesh.node_ptr(idx(type,nx,ny,i+1,j+1,k+1)); 01238 } 01239 01240 // Add sides for second prism to boundary info object 01241 if (i == 2*(nx-1)) 01242 boundary_info.add_side(elem, 1, 2); 01243 01244 if (j == 2*(ny-1)) 01245 boundary_info.add_side(elem, 2, 3); 01246 01247 if (k==0) 01248 boundary_info.add_side(elem, 0, 0); 01249 01250 if (k == 2*(nz-1)) 01251 boundary_info.add_side(elem, 4, 5); 01252 01253 } 01254 break; 01255 } 01256 01257 01258 01259 01260 01261 default: 01262 libmesh_error_msg("ERROR: Unrecognized 3D element type."); 01263 } 01264 01265 01266 01267 01268 //....................................... 01269 // Scale the nodal positions 01270 for (unsigned int p=0; p<mesh.n_nodes(); p++) 01271 { 01272 mesh.node(p)(0) = (mesh.node(p)(0))*(xmax-xmin) + xmin; 01273 mesh.node(p)(1) = (mesh.node(p)(1))*(ymax-ymin) + ymin; 01274 mesh.node(p)(2) = (mesh.node(p)(2))*(zmax-zmin) + zmin; 01275 } 01276 01277 01278 01279 01280 // Additional work for tets and pyramids: we take the existing 01281 // HEX27 discretization and split each element into 24 01282 // sub-tets or 6 sub-pyramids. 01283 // 01284 // 24 isn't the minimum-possible number of tets, but it 01285 // obviates any concerns about the edge orientations between 01286 // the various elements. 01287 if ((type == TET4) || 01288 (type == TET10) || 01289 (type == PYRAMID5) || 01290 (type == PYRAMID13) || 01291 (type == PYRAMID14)) 01292 { 01293 // Temporary storage for new elements. (24 tets per hex, 6 pyramids) 01294 std::vector<Elem*> new_elements; 01295 01296 if ((type == TET4) || (type == TET10)) 01297 new_elements.reserve(24*mesh.n_elem()); 01298 else 01299 new_elements.reserve(6*mesh.n_elem()); 01300 01301 // Create tetrahedra or pyramids 01302 { 01303 MeshBase::element_iterator el = mesh.elements_begin(); 01304 const MeshBase::element_iterator end_el = mesh.elements_end(); 01305 01306 for ( ; el != end_el; ++el) 01307 { 01308 // Get a pointer to the HEX27 element. 01309 Elem* base_hex = *el; 01310 01311 // Get a pointer to the node located at the HEX27 centroid 01312 Node* apex_node = base_hex->get_node(26); 01313 01314 for (unsigned short s=0; s<base_hex->n_sides(); ++s) 01315 { 01316 // Get the boundary ID for this side 01317 boundary_id_type b_id = boundary_info.boundary_id(*el, s); 01318 01319 // Need to build the full-ordered side! 01320 UniquePtr<Elem> side = base_hex->build_side(s); 01321 01322 if ((type == TET4) || (type == TET10)) 01323 { 01324 // Build 4 sub-tets per side 01325 for (unsigned int sub_tet=0; sub_tet<4; ++sub_tet) 01326 { 01327 new_elements.push_back( new Tet4 ); 01328 Elem* sub_elem = new_elements.back(); 01329 sub_elem->set_node(0) = side->get_node(sub_tet); 01330 sub_elem->set_node(1) = side->get_node(8); // centroid of the face 01331 sub_elem->set_node(2) = side->get_node(sub_tet==3 ? 0 : sub_tet+1 ); // wrap-around 01332 sub_elem->set_node(3) = apex_node; // apex node always used! 01333 01334 // If the original hex was a boundary hex, add the new sub_tet's side 01335 // 0 with the same b_id. Note: the tets are all aligned so that their 01336 // side 0 is on the boundary. 01337 if (b_id != BoundaryInfo::invalid_id) 01338 boundary_info.add_side(sub_elem, 0, b_id); 01339 } 01340 } // end if ((type == TET4) || (type == TET10)) 01341 01342 else // type==PYRAMID5 || type==PYRAMID13 || type==PYRAMID14 01343 { 01344 // Build 1 sub-pyramid per side. 01345 new_elements.push_back(new Pyramid5); 01346 Elem* sub_elem = new_elements.back(); 01347 01348 // Set the base. Note that since the apex is *inside* the base_hex, 01349 // and the pyramid uses a counter-clockwise base numbering, we need to 01350 // reverse the [1] and [3] node indices. 01351 sub_elem->set_node(0) = side->get_node(0); 01352 sub_elem->set_node(1) = side->get_node(3); 01353 sub_elem->set_node(2) = side->get_node(2); 01354 sub_elem->set_node(3) = side->get_node(1); 01355 01356 // Set the apex 01357 sub_elem->set_node(4) = apex_node; 01358 01359 // If the original hex was a boundary hex, add the new sub_pyr's side 01360 // 4 (the square base) with the same b_id. 01361 if (b_id != BoundaryInfo::invalid_id) 01362 boundary_info.add_side(sub_elem, 4, b_id); 01363 } // end else type==PYRAMID5 || type==PYRAMID13 || type==PYRAMID14 01364 } 01365 } 01366 } 01367 01368 01369 // Delete the original HEX27 elements from the mesh, and the boundary info structure. 01370 { 01371 MeshBase::element_iterator el = mesh.elements_begin(); 01372 const MeshBase::element_iterator end_el = mesh.elements_end(); 01373 01374 for ( ; el != end_el; ++el) 01375 { 01376 boundary_info.remove(*el); // Safe even if *el has no boundary info. 01377 mesh.delete_elem(*el); 01378 } 01379 } 01380 01381 // Add the new elements 01382 for (unsigned int i=0; i<new_elements.size(); ++i) 01383 mesh.add_elem(new_elements[i]); 01384 01385 } // end if (type == TET4,TET10,PYRAMID5,PYRAMID13,PYRAMID14 01386 01387 01388 // Use all_second_order to convert the TET4's to TET10's or PYRAMID5's to PYRAMID14's 01389 if ((type == TET10) || (type == PYRAMID14)) 01390 mesh.all_second_order(); 01391 01392 else if (type == PYRAMID13) 01393 mesh.all_second_order(/*full_ordered=*/false); 01394 01395 01396 // Add sideset names to boundary info (Z axis out of the screen) 01397 boundary_info.sideset_name(0) = "back"; 01398 boundary_info.sideset_name(1) = "bottom"; 01399 boundary_info.sideset_name(2) = "right"; 01400 boundary_info.sideset_name(3) = "top"; 01401 boundary_info.sideset_name(4) = "left"; 01402 boundary_info.sideset_name(5) = "front"; 01403 01404 // Add nodeset names to boundary info 01405 boundary_info.nodeset_name(0) = "back"; 01406 boundary_info.nodeset_name(1) = "bottom"; 01407 boundary_info.nodeset_name(2) = "right"; 01408 boundary_info.nodeset_name(3) = "top"; 01409 boundary_info.nodeset_name(4) = "left"; 01410 boundary_info.nodeset_name(5) = "front"; 01411 01412 break; 01413 } // end case dim==3 01414 01415 default: 01416 libmesh_error_msg("Unknown dimension " << mesh.mesh_dimension()); 01417 } 01418 01419 STOP_LOG("build_cube()", "MeshTools::Generation"); 01420 01421 01422 01423 // Done building the mesh. Now prepare it for use. 01424 mesh.prepare_for_use (/*skip_renumber =*/ false); 01425 } 01426 01427 01428 01429 void MeshTools::Generation::build_point (UnstructuredMesh& mesh, 01430 const ElemType type, 01431 const bool gauss_lobatto_grid) 01432 { 01433 // This method only makes sense in 0D! 01434 // But we now just turn a non-0D mesh into a 0D mesh 01435 //libmesh_assert_equal_to (mesh.mesh_dimension(), 1); 01436 01437 build_cube(mesh, 01438 0, 0, 0, 01439 0., 0., 01440 0., 0., 01441 0., 0., 01442 type, 01443 gauss_lobatto_grid); 01444 } 01445 01446 01447 void MeshTools::Generation::build_line (UnstructuredMesh& mesh, 01448 const unsigned int nx, 01449 const Real xmin, const Real xmax, 01450 const ElemType type, 01451 const bool gauss_lobatto_grid) 01452 { 01453 // This method only makes sense in 1D! 01454 // But we now just turn a non-1D mesh into a 1D mesh 01455 //libmesh_assert_equal_to (mesh.mesh_dimension(), 1); 01456 01457 build_cube(mesh, 01458 nx, 0, 0, 01459 xmin, xmax, 01460 0., 0., 01461 0., 0., 01462 type, 01463 gauss_lobatto_grid); 01464 } 01465 01466 01467 01468 void MeshTools::Generation::build_square (UnstructuredMesh& mesh, 01469 const unsigned int nx, 01470 const unsigned int ny, 01471 const Real xmin, const Real xmax, 01472 const Real ymin, const Real ymax, 01473 const ElemType type, 01474 const bool gauss_lobatto_grid) 01475 { 01476 // This method only makes sense in 2D! 01477 // But we now just turn a non-2D mesh into a 2D mesh 01478 //libmesh_assert_equal_to (mesh.mesh_dimension(), 2); 01479 01480 // Call the build_cube() member to actually do the work for us. 01481 build_cube (mesh, 01482 nx, ny, 0, 01483 xmin, xmax, 01484 ymin, ymax, 01485 0., 0., 01486 type, 01487 gauss_lobatto_grid); 01488 } 01489 01490 01491 01492 01493 01494 01495 01496 01497 01498 #ifndef LIBMESH_ENABLE_AMR 01499 void MeshTools::Generation::build_sphere (UnstructuredMesh&, 01500 const Real, 01501 const unsigned int, 01502 const ElemType, 01503 const unsigned int, 01504 const bool) 01505 { 01506 libmesh_error_msg("Building a circle/sphere only works with AMR."); 01507 } 01508 01509 #else 01510 01511 void MeshTools::Generation::build_sphere (UnstructuredMesh& mesh, 01512 const Real rad, 01513 const unsigned int nr, 01514 const ElemType type, 01515 const unsigned int n_smooth, 01516 const bool flat) 01517 { 01518 libmesh_assert_greater (rad, 0.); 01519 //libmesh_assert_greater (nr, 0); // must refine at least once otherwise will end up with a square/cube 01520 01521 START_LOG("build_sphere()", "MeshTools::Generation"); 01522 01523 // Clear the mesh and start from scratch 01524 mesh.clear(); 01525 01526 BoundaryInfo& boundary_info = mesh.get_boundary_info(); 01527 01528 // Sphere is centered at origin by default 01529 const Point cent; 01530 01531 const Sphere sphere (cent, rad); 01532 01533 switch (mesh.mesh_dimension()) 01534 { 01535 //----------------------------------------------------------------- 01536 // Build a line in one dimension 01537 case 1: 01538 { 01539 build_line (mesh, 3, -rad, rad, type); 01540 } 01541 01542 01543 01544 01545 //----------------------------------------------------------------- 01546 // Build a circle or hollow sphere in two dimensions 01547 case 2: 01548 { 01549 // For ParallelMesh, if we don't specify node IDs the Mesh 01550 // will try to pick an appropriate (unique) one for us. But 01551 // since we are adding these nodes on all processors, we want 01552 // to be sure they have consistent IDs across all processors. 01553 unsigned node_id = 0; 01554 01555 if (flat) 01556 { 01557 const Real sqrt_2 = std::sqrt(2.); 01558 const Real rad_2 = .25*rad; 01559 const Real rad_sqrt_2 = rad/sqrt_2; 01560 01561 // (Temporary) convenient storage for node pointers 01562 std::vector<Node*> nodes(8); 01563 01564 // Point 0 01565 nodes[0] = mesh.add_point (Point(-rad_2,-rad_2, 0.), node_id++); 01566 01567 // Point 1 01568 nodes[1] = mesh.add_point (Point( rad_2,-rad_2, 0.), node_id++); 01569 01570 // Point 2 01571 nodes[2] = mesh.add_point (Point( rad_2, rad_2, 0.), node_id++); 01572 01573 // Point 3 01574 nodes[3] = mesh.add_point (Point(-rad_2, rad_2, 0.), node_id++); 01575 01576 // Point 4 01577 nodes[4] = mesh.add_point (Point(-rad_sqrt_2,-rad_sqrt_2, 0.), node_id++); 01578 01579 // Point 5 01580 nodes[5] = mesh.add_point (Point( rad_sqrt_2,-rad_sqrt_2, 0.), node_id++); 01581 01582 // Point 6 01583 nodes[6] = mesh.add_point (Point( rad_sqrt_2, rad_sqrt_2, 0.), node_id++); 01584 01585 // Point 7 01586 nodes[7] = mesh.add_point (Point(-rad_sqrt_2, rad_sqrt_2, 0.), node_id++); 01587 01588 // Build the elements & set node pointers 01589 01590 // Element 0 01591 { 01592 Elem* elem0 = mesh.add_elem (new Quad4); 01593 elem0->set_node(0) = nodes[0]; 01594 elem0->set_node(1) = nodes[1]; 01595 elem0->set_node(2) = nodes[2]; 01596 elem0->set_node(3) = nodes[3]; 01597 } 01598 01599 // Element 1 01600 { 01601 Elem* elem1 = mesh.add_elem (new Quad4); 01602 elem1->set_node(0) = nodes[4]; 01603 elem1->set_node(1) = nodes[0]; 01604 elem1->set_node(2) = nodes[3]; 01605 elem1->set_node(3) = nodes[7]; 01606 } 01607 01608 // Element 2 01609 { 01610 Elem* elem2 = mesh.add_elem (new Quad4); 01611 elem2->set_node(0) = nodes[4]; 01612 elem2->set_node(1) = nodes[5]; 01613 elem2->set_node(2) = nodes[1]; 01614 elem2->set_node(3) = nodes[0]; 01615 } 01616 01617 // Element 3 01618 { 01619 Elem* elem3 = mesh.add_elem (new Quad4); 01620 elem3->set_node(0) = nodes[1]; 01621 elem3->set_node(1) = nodes[5]; 01622 elem3->set_node(2) = nodes[6]; 01623 elem3->set_node(3) = nodes[2]; 01624 } 01625 01626 // Element 4 01627 { 01628 Elem* elem4 = mesh.add_elem (new Quad4); 01629 elem4->set_node(0) = nodes[3]; 01630 elem4->set_node(1) = nodes[2]; 01631 elem4->set_node(2) = nodes[6]; 01632 elem4->set_node(3) = nodes[7]; 01633 } 01634 01635 } 01636 else 01637 { 01638 // Create the 12 vertices of a regular unit icosahedron 01639 Real t = 0.5 * (1 + std::sqrt(5.0)); 01640 Real s = rad / std::sqrt(1 + t*t); 01641 t *= s; 01642 01643 mesh.add_point (Point(-s, t, 0), node_id++); 01644 mesh.add_point (Point( s, t, 0), node_id++); 01645 mesh.add_point (Point(-s, -t, 0), node_id++); 01646 mesh.add_point (Point( s, -t, 0), node_id++); 01647 01648 mesh.add_point (Point( 0, -s, t), node_id++); 01649 mesh.add_point (Point( 0, s, t), node_id++); 01650 mesh.add_point (Point( 0, -s, -t), node_id++); 01651 mesh.add_point (Point( 0, s, -t), node_id++); 01652 01653 mesh.add_point (Point( t, 0, -s), node_id++); 01654 mesh.add_point (Point( t, 0, s), node_id++); 01655 mesh.add_point (Point(-t, 0, -s), node_id++); 01656 mesh.add_point (Point(-t, 0, s), node_id++); 01657 01658 // Create the 20 triangles of the icosahedron 01659 static const unsigned int idx1 [6] = {11, 5, 1, 7, 10, 11}; 01660 static const unsigned int idx2 [6] = {9, 4, 2, 6, 8, 9}; 01661 static const unsigned int idx3 [6] = {1, 5, 11, 10, 7, 1}; 01662 01663 for (unsigned int i = 0; i < 5; ++i) 01664 { 01665 // 5 elems around point 0 01666 Elem* new_elem = mesh.add_elem (new Tri3); 01667 new_elem->set_node(0) = mesh.node_ptr(0); 01668 new_elem->set_node(1) = mesh.node_ptr(idx1[i]); 01669 new_elem->set_node(2) = mesh.node_ptr(idx1[i+1]); 01670 01671 // 5 adjacent elems 01672 new_elem = mesh.add_elem (new Tri3); 01673 new_elem->set_node(0) = mesh.node_ptr(idx3[i]); 01674 new_elem->set_node(1) = mesh.node_ptr(idx3[i+1]); 01675 new_elem->set_node(2) = mesh.node_ptr(idx2[i]); 01676 01677 // 5 elems around point 3 01678 new_elem = mesh.add_elem (new Tri3); 01679 new_elem->set_node(0) = mesh.node_ptr(3); 01680 new_elem->set_node(1) = mesh.node_ptr(idx2[i]); 01681 new_elem->set_node(2) = mesh.node_ptr(idx2[i+1]); 01682 01683 // 5 adjacent elems 01684 new_elem = mesh.add_elem (new Tri3); 01685 new_elem->set_node(0) = mesh.node_ptr(idx2[i+1]); 01686 new_elem->set_node(1) = mesh.node_ptr(idx2[i]); 01687 new_elem->set_node(2) = mesh.node_ptr(idx3[i+1]); 01688 } 01689 } 01690 01691 break; 01692 } // end case 2 01693 01694 01695 01696 01697 01698 //----------------------------------------------------------------- 01699 // Build a sphere in three dimensions 01700 case 3: 01701 { 01702 // (Currently) supported types 01703 if (!((type == HEX8) || (type == HEX27))) 01704 { 01705 // FIXME: We'd need an all_tet() routine (which could also be used by 01706 // build_square()) to do Tets, or Prisms for that matter. 01707 libmesh_error_msg("Error: Only HEX8/27 currently supported."); 01708 } 01709 01710 01711 // 3D analog of 2D initial grid: 01712 const Real 01713 r_small = 0.25*rad, // 0.25 *radius 01714 r_med = (0.125*std::sqrt(2.)+0.5)*rad; // .67677*radius 01715 01716 // (Temporary) convenient storage for node pointers 01717 std::vector<Node*> nodes(16); 01718 01719 // For ParallelMesh, if we don't specify node IDs the Mesh 01720 // will try to pick an appropriate (unique) one for us. But 01721 // since we are adding these nodes on all processors, we want 01722 // to be sure they have consistent IDs across all processors. 01723 unsigned node_id = 0; 01724 01725 // Points 0-7 are the initial HEX8 01726 nodes[0] = mesh.add_point (Point(-r_small,-r_small, -r_small), node_id++); 01727 nodes[1] = mesh.add_point (Point( r_small,-r_small, -r_small), node_id++); 01728 nodes[2] = mesh.add_point (Point( r_small, r_small, -r_small), node_id++); 01729 nodes[3] = mesh.add_point (Point(-r_small, r_small, -r_small), node_id++); 01730 nodes[4] = mesh.add_point (Point(-r_small,-r_small, r_small), node_id++); 01731 nodes[5] = mesh.add_point (Point( r_small,-r_small, r_small), node_id++); 01732 nodes[6] = mesh.add_point (Point( r_small, r_small, r_small), node_id++); 01733 nodes[7] = mesh.add_point (Point(-r_small, r_small, r_small), node_id++); 01734 01735 // Points 8-15 are for the outer hexes, we number them in the same way 01736 nodes[8] = mesh.add_point (Point(-r_med,-r_med, -r_med), node_id++); 01737 nodes[9] = mesh.add_point (Point( r_med,-r_med, -r_med), node_id++); 01738 nodes[10] = mesh.add_point (Point( r_med, r_med, -r_med), node_id++); 01739 nodes[11] = mesh.add_point (Point(-r_med, r_med, -r_med), node_id++); 01740 nodes[12] = mesh.add_point (Point(-r_med,-r_med, r_med), node_id++); 01741 nodes[13] = mesh.add_point (Point( r_med,-r_med, r_med), node_id++); 01742 nodes[14] = mesh.add_point (Point( r_med, r_med, r_med), node_id++); 01743 nodes[15] = mesh.add_point (Point(-r_med, r_med, r_med), node_id++); 01744 01745 // Now create the elements and add them to the mesh 01746 // Element 0 - center element 01747 { 01748 Elem* elem0 = mesh.add_elem (new Hex8); 01749 elem0->set_node(0) = nodes[0]; 01750 elem0->set_node(1) = nodes[1]; 01751 elem0->set_node(2) = nodes[2]; 01752 elem0->set_node(3) = nodes[3]; 01753 elem0->set_node(4) = nodes[4]; 01754 elem0->set_node(5) = nodes[5]; 01755 elem0->set_node(6) = nodes[6]; 01756 elem0->set_node(7) = nodes[7]; 01757 } 01758 01759 // Element 1 - "bottom" 01760 { 01761 Elem* elem1 = mesh.add_elem (new Hex8); 01762 elem1->set_node(0) = nodes[8]; 01763 elem1->set_node(1) = nodes[9]; 01764 elem1->set_node(2) = nodes[10]; 01765 elem1->set_node(3) = nodes[11]; 01766 elem1->set_node(4) = nodes[0]; 01767 elem1->set_node(5) = nodes[1]; 01768 elem1->set_node(6) = nodes[2]; 01769 elem1->set_node(7) = nodes[3]; 01770 } 01771 01772 // Element 2 - "front" 01773 { 01774 Elem* elem2 = mesh.add_elem (new Hex8); 01775 elem2->set_node(0) = nodes[8]; 01776 elem2->set_node(1) = nodes[9]; 01777 elem2->set_node(2) = nodes[1]; 01778 elem2->set_node(3) = nodes[0]; 01779 elem2->set_node(4) = nodes[12]; 01780 elem2->set_node(5) = nodes[13]; 01781 elem2->set_node(6) = nodes[5]; 01782 elem2->set_node(7) = nodes[4]; 01783 } 01784 01785 // Element 3 - "right" 01786 { 01787 Elem* elem3 = mesh.add_elem (new Hex8); 01788 elem3->set_node(0) = nodes[1]; 01789 elem3->set_node(1) = nodes[9]; 01790 elem3->set_node(2) = nodes[10]; 01791 elem3->set_node(3) = nodes[2]; 01792 elem3->set_node(4) = nodes[5]; 01793 elem3->set_node(5) = nodes[13]; 01794 elem3->set_node(6) = nodes[14]; 01795 elem3->set_node(7) = nodes[6]; 01796 } 01797 01798 // Element 4 - "back" 01799 { 01800 Elem* elem4 = mesh.add_elem (new Hex8); 01801 elem4->set_node(0) = nodes[3]; 01802 elem4->set_node(1) = nodes[2]; 01803 elem4->set_node(2) = nodes[10]; 01804 elem4->set_node(3) = nodes[11]; 01805 elem4->set_node(4) = nodes[7]; 01806 elem4->set_node(5) = nodes[6]; 01807 elem4->set_node(6) = nodes[14]; 01808 elem4->set_node(7) = nodes[15]; 01809 } 01810 01811 // Element 5 - "left" 01812 { 01813 Elem* elem5 = mesh.add_elem (new Hex8); 01814 elem5->set_node(0) = nodes[8]; 01815 elem5->set_node(1) = nodes[0]; 01816 elem5->set_node(2) = nodes[3]; 01817 elem5->set_node(3) = nodes[11]; 01818 elem5->set_node(4) = nodes[12]; 01819 elem5->set_node(5) = nodes[4]; 01820 elem5->set_node(6) = nodes[7]; 01821 elem5->set_node(7) = nodes[15]; 01822 } 01823 01824 // Element 6 - "top" 01825 { 01826 Elem* elem6 = mesh.add_elem (new Hex8); 01827 elem6->set_node(0) = nodes[4]; 01828 elem6->set_node(1) = nodes[5]; 01829 elem6->set_node(2) = nodes[6]; 01830 elem6->set_node(3) = nodes[7]; 01831 elem6->set_node(4) = nodes[12]; 01832 elem6->set_node(5) = nodes[13]; 01833 elem6->set_node(6) = nodes[14]; 01834 elem6->set_node(7) = nodes[15]; 01835 } 01836 01837 break; 01838 } // end case 3 01839 01840 default: 01841 libmesh_error_msg("Unknown dimension " << mesh.mesh_dimension()); 01842 01843 01844 01845 } // end switch (dim) 01846 01847 // Now we have the beginnings of a sphere. 01848 // Add some more elements by doing uniform refinements and 01849 // popping nodes to the boundary. 01850 MeshRefinement mesh_refinement (mesh); 01851 01852 // Loop over the elements, refine, pop nodes to boundary. 01853 for (unsigned int r=0; r<nr; r++) 01854 { 01855 mesh_refinement.uniformly_refine(1); 01856 01857 MeshBase::element_iterator it = mesh.active_elements_begin(); 01858 const MeshBase::element_iterator end = mesh.active_elements_end(); 01859 01860 for (; it != end; ++it) 01861 { 01862 Elem* elem = *it; 01863 01864 for (unsigned int s=0; s<elem->n_sides(); s++) 01865 if (elem->neighbor(s) == NULL || (mesh.mesh_dimension() == 2 && !flat)) 01866 { 01867 UniquePtr<Elem> side(elem->build_side(s)); 01868 01869 // Pop each point to the sphere boundary 01870 for (unsigned int n=0; n<side->n_nodes(); n++) 01871 side->point(n) = 01872 sphere.closest_point(side->point(n)); 01873 } 01874 } 01875 } 01876 01877 // The mesh now contains a refinement hierarchy due to the refinements 01878 // used to generate the grid. In order to call other support functions 01879 // like all_tri() and all_second_order, you need a "flat" mesh file (with no 01880 // refinement trees) so 01881 MeshTools::Modification::flatten(mesh); 01882 01883 // In 2D, convert all the quads to triangles if requested 01884 if (mesh.mesh_dimension()==2) 01885 { 01886 if ((type == TRI6) || (type == TRI3)) 01887 { 01888 MeshTools::Modification::all_tri(mesh); 01889 } 01890 } 01891 01892 01893 // Convert to second-order elements if the user requested it. 01894 if (Elem::second_order_equivalent_type(type) == INVALID_ELEM) 01895 { 01896 // type is already a second-order, determine if it is the 01897 // "full-ordered" second-order element, or the "serendipity" 01898 // second order element. Note also that all_second_order 01899 // can't be called once the mesh has been refined. 01900 bool full_ordered = !((type==QUAD8) || (type==HEX20)); 01901 mesh.all_second_order(full_ordered); 01902 01903 // And pop to the boundary again... 01904 MeshBase::element_iterator it = mesh.active_elements_begin(); 01905 const MeshBase::element_iterator end = mesh.active_elements_end(); 01906 01907 for (; it != end; ++it) 01908 { 01909 Elem* elem = *it; 01910 01911 for (unsigned int s=0; s<elem->n_sides(); s++) 01912 if (elem->neighbor(s) == NULL) 01913 { 01914 UniquePtr<Elem> side(elem->build_side(s)); 01915 01916 // Pop each point to the sphere boundary 01917 for (unsigned int n=0; n<side->n_nodes(); n++) 01918 side->point(n) = 01919 sphere.closest_point(side->point(n)); 01920 } 01921 } 01922 } 01923 01924 01925 // The meshes could probably use some smoothing. 01926 LaplaceMeshSmoother smoother(mesh); 01927 smoother.smooth(n_smooth); 01928 01929 // We'll give the whole sphere surface a boundary id of 0 01930 { 01931 MeshBase::element_iterator it = mesh.active_elements_begin(); 01932 const MeshBase::element_iterator end = mesh.active_elements_end(); 01933 01934 for (; it != end; ++it) 01935 { 01936 Elem* elem = *it; 01937 for (unsigned short s=0; s != elem->n_sides(); ++s) 01938 if (!elem->neighbor(s)) 01939 boundary_info.add_side(elem, s, 0); 01940 } 01941 } 01942 01943 STOP_LOG("build_sphere()", "MeshTools::Generation"); 01944 01945 01946 // Done building the mesh. Now prepare it for use. 01947 mesh.prepare_for_use(/*skip_renumber =*/ false); 01948 } 01949 01950 #endif // #ifndef LIBMESH_ENABLE_AMR 01951 01952 01953 // Meshes the tensor product of a 1D and a 1D-or-2D domain. 01954 void MeshTools::Generation::build_extrusion (UnstructuredMesh& mesh, 01955 const MeshBase& cross_section, 01956 const unsigned int nz, 01957 RealVectorValue extrusion_vector) 01958 { 01959 if (!cross_section.n_elem()) 01960 return; 01961 01962 START_LOG("build_extrusion()", "MeshTools::Generation"); 01963 01964 dof_id_type orig_elem = cross_section.n_elem(); 01965 dof_id_type orig_nodes = cross_section.n_nodes(); 01966 01967 unsigned int order = 1; 01968 01969 BoundaryInfo& boundary_info = mesh.get_boundary_info(); 01970 const BoundaryInfo& cross_section_boundary_info = cross_section.get_boundary_info(); 01971 01972 // If cross_section is distributed, so is its extrusion 01973 if (!cross_section.is_serial()) 01974 mesh.delete_remote_elements(); 01975 01976 // We know a priori how many elements we'll need 01977 mesh.reserve_elem(nz*orig_elem); 01978 01979 // For straightforward meshes we need one or two additional layers per 01980 // element. 01981 if ((*cross_section.elements_begin())->default_order() == SECOND) 01982 order = 2; 01983 01984 mesh.reserve_nodes((order*nz+1)*orig_nodes); 01985 01986 MeshBase::const_node_iterator nd = cross_section.nodes_begin(); 01987 const MeshBase::const_node_iterator nend = cross_section.nodes_end(); 01988 for (; nd!=nend; ++nd) 01989 { 01990 const Node* node = *nd; 01991 01992 for (unsigned int k=0; k != order*nz+1; ++k) 01993 { 01994 Node *new_node = 01995 mesh.add_point(*node + 01996 (extrusion_vector * k / nz / order), 01997 node->id() + (k * orig_nodes), 01998 node->processor_id()); 01999 02000 const std::vector<boundary_id_type> ids_to_copy = 02001 cross_section_boundary_info.boundary_ids(node); 02002 02003 boundary_info.add_node(new_node, ids_to_copy); 02004 } 02005 } 02006 02007 const std::set<boundary_id_type> &side_ids = 02008 cross_section_boundary_info.get_side_boundary_ids(); 02009 const boundary_id_type next_side_id = side_ids.empty() ? 02010 0 : cast_int<boundary_id_type>(*side_ids.rbegin() + 1); 02011 02012 MeshBase::const_element_iterator el = cross_section.elements_begin(); 02013 const MeshBase::const_element_iterator end = cross_section.elements_end(); 02014 for (; el!=end; ++el) 02015 { 02016 const Elem* elem = *el; 02017 const ElemType etype = elem->type(); 02018 02019 // build_extrusion currently only works on coarse meshes 02020 libmesh_assert (!elem->parent()); 02021 02022 for (unsigned int k=0; k != nz; ++k) 02023 { 02024 Elem *new_elem; 02025 switch (etype) 02026 { 02027 case EDGE2: 02028 { 02029 new_elem = new Quad4; 02030 new_elem->set_node(0) = mesh.node_ptr(elem->get_node(0)->id() + (k * orig_nodes)); 02031 new_elem->set_node(1) = mesh.node_ptr(elem->get_node(1)->id() + (k * orig_nodes)); 02032 new_elem->set_node(2) = mesh.node_ptr(elem->get_node(1)->id() + ((k+1) * orig_nodes)); 02033 new_elem->set_node(3) = mesh.node_ptr(elem->get_node(0)->id() + ((k+1) * orig_nodes)); 02034 break; 02035 } 02036 case EDGE3: 02037 { 02038 new_elem = new Quad9; 02039 new_elem->set_node(0) = mesh.node_ptr(elem->get_node(0)->id() + (2*k * orig_nodes)); 02040 new_elem->set_node(1) = mesh.node_ptr(elem->get_node(1)->id() + (2*k * orig_nodes)); 02041 new_elem->set_node(2) = mesh.node_ptr(elem->get_node(1)->id() + ((2*k+2) * orig_nodes)); 02042 new_elem->set_node(3) = mesh.node_ptr(elem->get_node(0)->id() + ((2*k+2) * orig_nodes)); 02043 new_elem->set_node(4) = mesh.node_ptr(elem->get_node(2)->id() + (2*k * orig_nodes)); 02044 new_elem->set_node(5) = mesh.node_ptr(elem->get_node(1)->id() + ((2*k+1) * orig_nodes)); 02045 new_elem->set_node(6) = mesh.node_ptr(elem->get_node(2)->id() + ((2*k+2) * orig_nodes)); 02046 new_elem->set_node(7) = mesh.node_ptr(elem->get_node(0)->id() + ((2*k+1) * orig_nodes)); 02047 new_elem->set_node(8) = mesh.node_ptr(elem->get_node(2)->id() + ((2*k+1) * orig_nodes)); 02048 break; 02049 } 02050 case TRI3: 02051 { 02052 new_elem = new Prism6; 02053 new_elem->set_node(0) = mesh.node_ptr(elem->get_node(0)->id() + (k * orig_nodes)); 02054 new_elem->set_node(1) = mesh.node_ptr(elem->get_node(1)->id() + (k * orig_nodes)); 02055 new_elem->set_node(2) = mesh.node_ptr(elem->get_node(2)->id() + (k * orig_nodes)); 02056 new_elem->set_node(3) = mesh.node_ptr(elem->get_node(0)->id() + ((k+1) * orig_nodes)); 02057 new_elem->set_node(4) = mesh.node_ptr(elem->get_node(1)->id() + ((k+1) * orig_nodes)); 02058 new_elem->set_node(5) = mesh.node_ptr(elem->get_node(2)->id() + ((k+1) * orig_nodes)); 02059 break; 02060 } 02061 case TRI6: 02062 { 02063 new_elem = new Prism18; 02064 new_elem->set_node(0) = mesh.node_ptr(elem->get_node(0)->id() + (2*k * orig_nodes)); 02065 new_elem->set_node(1) = mesh.node_ptr(elem->get_node(1)->id() + (2*k * orig_nodes)); 02066 new_elem->set_node(2) = mesh.node_ptr(elem->get_node(2)->id() + (2*k * orig_nodes)); 02067 new_elem->set_node(3) = mesh.node_ptr(elem->get_node(0)->id() + ((2*k+2) * orig_nodes)); 02068 new_elem->set_node(4) = mesh.node_ptr(elem->get_node(1)->id() + ((2*k+2) * orig_nodes)); 02069 new_elem->set_node(5) = mesh.node_ptr(elem->get_node(2)->id() + ((2*k+2) * orig_nodes)); 02070 new_elem->set_node(6) = mesh.node_ptr(elem->get_node(3)->id() + (2*k * orig_nodes)); 02071 new_elem->set_node(7) = mesh.node_ptr(elem->get_node(4)->id() + (2*k * orig_nodes)); 02072 new_elem->set_node(8) = mesh.node_ptr(elem->get_node(5)->id() + (2*k * orig_nodes)); 02073 new_elem->set_node(9) = mesh.node_ptr(elem->get_node(0)->id() + ((2*k+1) * orig_nodes)); 02074 new_elem->set_node(10) = mesh.node_ptr(elem->get_node(1)->id() + ((2*k+1) * orig_nodes)); 02075 new_elem->set_node(11) = mesh.node_ptr(elem->get_node(2)->id() + ((2*k+1) * orig_nodes)); 02076 new_elem->set_node(12) = mesh.node_ptr(elem->get_node(3)->id() + ((2*k+2) * orig_nodes)); 02077 new_elem->set_node(13) = mesh.node_ptr(elem->get_node(4)->id() + ((2*k+2) * orig_nodes)); 02078 new_elem->set_node(14) = mesh.node_ptr(elem->get_node(5)->id() + ((2*k+2) * orig_nodes)); 02079 new_elem->set_node(15) = mesh.node_ptr(elem->get_node(3)->id() + ((2*k+1) * orig_nodes)); 02080 new_elem->set_node(16) = mesh.node_ptr(elem->get_node(4)->id() + ((2*k+1) * orig_nodes)); 02081 new_elem->set_node(17) = mesh.node_ptr(elem->get_node(5)->id() + ((2*k+1) * orig_nodes)); 02082 break; 02083 } 02084 case QUAD4: 02085 { 02086 new_elem = new Hex8; 02087 new_elem->set_node(0) = mesh.node_ptr(elem->get_node(0)->id() + (k * orig_nodes)); 02088 new_elem->set_node(1) = mesh.node_ptr(elem->get_node(1)->id() + (k * orig_nodes)); 02089 new_elem->set_node(2) = mesh.node_ptr(elem->get_node(2)->id() + (k * orig_nodes)); 02090 new_elem->set_node(3) = mesh.node_ptr(elem->get_node(3)->id() + (k * orig_nodes)); 02091 new_elem->set_node(4) = mesh.node_ptr(elem->get_node(0)->id() + ((k+1) * orig_nodes)); 02092 new_elem->set_node(5) = mesh.node_ptr(elem->get_node(1)->id() + ((k+1) * orig_nodes)); 02093 new_elem->set_node(6) = mesh.node_ptr(elem->get_node(2)->id() + ((k+1) * orig_nodes)); 02094 new_elem->set_node(7) = mesh.node_ptr(elem->get_node(3)->id() + ((k+1) * orig_nodes)); 02095 break; 02096 } 02097 case QUAD9: 02098 { 02099 new_elem = new Hex27; 02100 new_elem->set_node(0) = mesh.node_ptr(elem->get_node(0)->id() + (2*k * orig_nodes)); 02101 new_elem->set_node(1) = mesh.node_ptr(elem->get_node(1)->id() + (2*k * orig_nodes)); 02102 new_elem->set_node(2) = mesh.node_ptr(elem->get_node(2)->id() + (2*k * orig_nodes)); 02103 new_elem->set_node(3) = mesh.node_ptr(elem->get_node(3)->id() + (2*k * orig_nodes)); 02104 new_elem->set_node(4) = mesh.node_ptr(elem->get_node(0)->id() + ((2*k+2) * orig_nodes)); 02105 new_elem->set_node(5) = mesh.node_ptr(elem->get_node(1)->id() + ((2*k+2) * orig_nodes)); 02106 new_elem->set_node(6) = mesh.node_ptr(elem->get_node(2)->id() + ((2*k+2) * orig_nodes)); 02107 new_elem->set_node(7) = mesh.node_ptr(elem->get_node(3)->id() + ((2*k+2) * orig_nodes)); 02108 new_elem->set_node(8) = mesh.node_ptr(elem->get_node(4)->id() + (2*k * orig_nodes)); 02109 new_elem->set_node(9) = mesh.node_ptr(elem->get_node(5)->id() + (2*k * orig_nodes)); 02110 new_elem->set_node(10) = mesh.node_ptr(elem->get_node(6)->id() + (2*k * orig_nodes)); 02111 new_elem->set_node(11) = mesh.node_ptr(elem->get_node(7)->id() + (2*k * orig_nodes)); 02112 new_elem->set_node(12) = mesh.node_ptr(elem->get_node(0)->id() + ((2*k+1) * orig_nodes)); 02113 new_elem->set_node(13) = mesh.node_ptr(elem->get_node(1)->id() + ((2*k+1) * orig_nodes)); 02114 new_elem->set_node(14) = mesh.node_ptr(elem->get_node(2)->id() + ((2*k+1) * orig_nodes)); 02115 new_elem->set_node(15) = mesh.node_ptr(elem->get_node(3)->id() + ((2*k+1) * orig_nodes)); 02116 new_elem->set_node(16) = mesh.node_ptr(elem->get_node(4)->id() + ((2*k+2) * orig_nodes)); 02117 new_elem->set_node(17) = mesh.node_ptr(elem->get_node(5)->id() + ((2*k+2) * orig_nodes)); 02118 new_elem->set_node(18) = mesh.node_ptr(elem->get_node(6)->id() + ((2*k+2) * orig_nodes)); 02119 new_elem->set_node(19) = mesh.node_ptr(elem->get_node(7)->id() + ((2*k+2) * orig_nodes)); 02120 new_elem->set_node(20) = mesh.node_ptr(elem->get_node(8)->id() + (2*k * orig_nodes)); 02121 new_elem->set_node(21) = mesh.node_ptr(elem->get_node(4)->id() + ((2*k+1) * orig_nodes)); 02122 new_elem->set_node(22) = mesh.node_ptr(elem->get_node(5)->id() + ((2*k+1) * orig_nodes)); 02123 new_elem->set_node(23) = mesh.node_ptr(elem->get_node(6)->id() + ((2*k+1) * orig_nodes)); 02124 new_elem->set_node(24) = mesh.node_ptr(elem->get_node(7)->id() + ((2*k+1) * orig_nodes)); 02125 new_elem->set_node(25) = mesh.node_ptr(elem->get_node(8)->id() + ((2*k+2) * orig_nodes)); 02126 new_elem->set_node(26) = mesh.node_ptr(elem->get_node(8)->id() + ((2*k+1) * orig_nodes)); 02127 break; 02128 } 02129 default: 02130 { 02131 libmesh_not_implemented(); 02132 break; 02133 } 02134 } 02135 02136 new_elem->set_id(elem->id() + (k * orig_elem)); 02137 new_elem->processor_id() = elem->processor_id(); 02138 02139 // maintain the subdomain_id 02140 new_elem->subdomain_id() = elem->subdomain_id(); 02141 02142 new_elem = mesh.add_elem(new_elem); 02143 02144 // Copy any old boundary ids on all sides 02145 for (unsigned short s = 0; s != elem->n_sides(); ++s) 02146 { 02147 const std::vector<boundary_id_type> ids_to_copy = 02148 cross_section_boundary_info.boundary_ids(elem, s); 02149 02150 if (new_elem->dim() == 3) 02151 { 02152 // For 2D->3D extrusion, we give the boundary IDs 02153 // for side s on the old element to side s+1 on the 02154 // new element. This is just a happy coincidence as 02155 // far as I can tell... 02156 boundary_info.add_side 02157 (new_elem, cast_int<unsigned short>(s+1), 02158 ids_to_copy); 02159 } 02160 else 02161 { 02162 // For 1D->2D extrusion, the boundary IDs map as: 02163 // Old elem -> New elem 02164 // 0 -> 3 02165 // 1 -> 1 02166 libmesh_assert_less(s, 2); 02167 const unsigned short sidemap[2] = {3, 1}; 02168 boundary_info.add_side(new_elem, sidemap[s], ids_to_copy); 02169 } 02170 } 02171 02172 // Give new boundary ids to bottom and top 02173 if (k == 0) 02174 boundary_info.add_side(new_elem, 0, next_side_id); 02175 if (k == nz-1) 02176 { 02177 // For 2D->3D extrusion, the "top" ID is 1+the original 02178 // element's number of sides. For 1D->2D extrusion, the 02179 // "top" ID is side 2. 02180 const unsigned short top_id = new_elem->dim() == 3 ? 02181 cast_int<unsigned short>(elem->n_sides()+1) : 2; 02182 boundary_info.add_side 02183 (new_elem, top_id, 02184 cast_int<boundary_id_type>(next_side_id+1)); 02185 } 02186 } 02187 } 02188 02189 STOP_LOG("build_extrusion()", "MeshTools::Generation"); 02190 02191 // Done building the mesh. Now prepare it for use. 02192 mesh.prepare_for_use(/*skip_renumber =*/ false); 02193 } 02194 02195 02196 02197 02198 #ifdef LIBMESH_HAVE_TRIANGLE 02199 02200 // Triangulates a 2D rectangular region with or without holes 02201 void MeshTools::Generation::build_delaunay_square(UnstructuredMesh& mesh, 02202 const unsigned int nx, // num. of elements in x-dir 02203 const unsigned int ny, // num. of elements in y-dir 02204 const Real xmin, const Real xmax, 02205 const Real ymin, const Real ymax, 02206 const ElemType type, 02207 const std::vector<TriangleInterface::Hole*>* holes) 02208 { 02209 // Check for reasonable size 02210 libmesh_assert_greater_equal (nx, 1); // need at least 1 element in x-direction 02211 libmesh_assert_greater_equal (ny, 1); // need at least 1 element in y-direction 02212 libmesh_assert_less (xmin, xmax); 02213 libmesh_assert_less (ymin, ymax); 02214 02215 // Clear out any data which may have been in the Mesh 02216 mesh.clear(); 02217 02218 BoundaryInfo& boundary_info = mesh.get_boundary_info(); 02219 02220 // Make sure the new Mesh will be 2D 02221 mesh.set_mesh_dimension(2); 02222 02223 // The x and y spacing between boundary points 02224 const Real delta_x = (xmax-xmin) / static_cast<Real>(nx); 02225 const Real delta_y = (ymax-ymin) / static_cast<Real>(ny); 02226 02227 // Bottom 02228 for (unsigned int p=0; p<=nx; ++p) 02229 mesh.add_point(Point(xmin + p*delta_x, ymin)); 02230 02231 // Right side 02232 for (unsigned int p=1; p<ny; ++p) 02233 mesh.add_point(Point(xmax, ymin + p*delta_y)); 02234 02235 // Top 02236 for (unsigned int p=0; p<=nx; ++p) 02237 mesh.add_point(Point(xmax - p*delta_x, ymax)); 02238 02239 // Left side 02240 for (unsigned int p=1; p<ny; ++p) 02241 mesh.add_point(Point(xmin, ymax - p*delta_y)); 02242 02243 // Be sure we added as many points as we thought we did 02244 libmesh_assert_equal_to (mesh.n_nodes(), 2*(nx+ny)); 02245 02246 // Construct the Triangle Interface object 02247 TriangleInterface t(mesh); 02248 02249 // Set custom variables for the triangulation 02250 t.desired_area() = 0.5 * (xmax-xmin)*(ymax-ymin) / static_cast<Real>(nx*ny); 02251 t.triangulation_type() = TriangleInterface::PSLG; 02252 t.elem_type() = type; 02253 02254 if (holes != NULL) 02255 t.attach_hole_list(holes); 02256 02257 // Triangulate! 02258 t.triangulate(); 02259 02260 // The mesh is now generated, but we still need to mark the boundaries 02261 // to be consistent with the other build_square routines. Note that all 02262 // hole boundary elements get the same ID, 4. 02263 MeshBase::element_iterator el = mesh.elements_begin(); 02264 const MeshBase::element_iterator end_el = mesh.elements_end(); 02265 for ( ; el != end_el; ++el) 02266 { 02267 const Elem* elem = *el; 02268 02269 for (unsigned int s=0; s<elem->n_sides(); s++) 02270 if (elem->neighbor(s) == NULL) 02271 { 02272 UniquePtr<Elem> side (elem->build_side(s)); 02273 02274 // Check the location of the side's midpoint. Since 02275 // the square has straight sides, the midpoint is not 02276 // on the corner and thus it is uniquely on one of the 02277 // sides. 02278 Point side_midpoint= 0.5f*( (*side->get_node(0)) + (*side->get_node(1)) ); 02279 02280 // The boundary ids are set following the same convention as Quad4 sides 02281 // bottom = 0 02282 // right = 1 02283 // top = 2 02284 // left = 3 02285 // hole = 4 02286 boundary_id_type bc_id=4; 02287 02288 // bottom 02289 if (std::fabs(side_midpoint(1) - ymin) < TOLERANCE) 02290 bc_id=0; 02291 02292 // right 02293 else if (std::fabs(side_midpoint(0) - xmax) < TOLERANCE) 02294 bc_id=1; 02295 02296 // top 02297 else if (std::fabs(side_midpoint(1) - ymax) < TOLERANCE) 02298 bc_id=2; 02299 02300 // left 02301 else if (std::fabs(side_midpoint(0) - xmin) < TOLERANCE) 02302 bc_id=3; 02303 02304 // If the point is not on any of the external boundaries, it 02305 // is on one of the holes.... 02306 02307 // Finally, add this element's information to the boundary info object. 02308 boundary_info.add_side(elem->id(), s, bc_id); 02309 } 02310 } 02311 02312 } // end build_delaunay_square 02313 02314 #endif // LIBMESH_HAVE_TRIANGLE 02315 02316 02317 02318 } // namespace libMesh