void testMesh() { // There'd better be 3 elements CPPUNIT_ASSERT_EQUAL( (dof_id_type)3, _mesh->n_elem() ); // There'd better be only 6 nodes CPPUNIT_ASSERT_EQUAL( (dof_id_type)6, _mesh->n_nodes() ); /* The nodes for the EDGE2 element should have the same global ids as the bottom edge of the top QUAD4 element */ CPPUNIT_ASSERT_EQUAL( _mesh->elem(2)->node(0), _mesh->elem(0)->node(0) ); CPPUNIT_ASSERT_EQUAL( _mesh->elem(2)->node(1), _mesh->elem(0)->node(1) ); /* The nodes for the EDGE2 element should have the same global ids as the top edge of the bottom QUAD4 element */ CPPUNIT_ASSERT_EQUAL( _mesh->elem(2)->node(0), _mesh->elem(1)->node(3) ); CPPUNIT_ASSERT_EQUAL( _mesh->elem(2)->node(1), _mesh->elem(1)->node(2) ); /* The nodes for the bottom edge of the top QUAD4 element should have the same global ids as the top edge of the bottom QUAD4 element */ CPPUNIT_ASSERT_EQUAL( _mesh->elem(0)->node(0), _mesh->elem(1)->node(3) ); CPPUNIT_ASSERT_EQUAL( _mesh->elem(0)->node(1), _mesh->elem(1)->node(2) ); // We didn't set an interior_parent on the edge element, so it // should default to NULL CPPUNIT_ASSERT( !_mesh->elem(2)->interior_parent() ); }
void testMesh() { // There'd better be 3 elements CPPUNIT_ASSERT_EQUAL( (dof_id_type)3, _mesh->n_elem() ); // There'd better be only 6 nodes CPPUNIT_ASSERT_EQUAL( (dof_id_type)6, _mesh->n_nodes() ); /* The nodes for the EDGE2 element should have the same global ids as the bottom edge of the top QUAD4 element */ CPPUNIT_ASSERT_EQUAL( _mesh->elem(2)->node(0), _mesh->elem(0)->node(0) ); CPPUNIT_ASSERT_EQUAL( _mesh->elem(2)->node(1), _mesh->elem(0)->node(1) ); /* The nodes for the EDGE2 element should have the same global ids as the top edge of the bottom QUAD4 element */ CPPUNIT_ASSERT_EQUAL( _mesh->elem(2)->node(0), _mesh->elem(1)->node(3) ); CPPUNIT_ASSERT_EQUAL( _mesh->elem(2)->node(1), _mesh->elem(1)->node(2) ); /* The nodes for the bottom edge of the top QUAD4 element should have the same global ids as the top edge of the bottom QUAD4 element */ CPPUNIT_ASSERT_EQUAL( _mesh->elem(0)->node(0), _mesh->elem(1)->node(3) ); CPPUNIT_ASSERT_EQUAL( _mesh->elem(0)->node(1), _mesh->elem(1)->node(2) ); }
// Begin the main program. int main (int argc, char** argv) { // Initialize libMesh. LibMeshInit init (argc, argv); // Skip this 3D example if libMesh was compiled as 1D/2D-only. libmesh_example_requires (3 == LIBMESH_DIM, "3D support"); // Skip this example without --enable-node-valence #ifndef LIBMESH_ENABLE_NODE_VALENCE libmesh_example_requires (false, "--enable-node-valence"); #endif // Skip this example without --enable-amr; requires MeshRefinement #ifndef LIBMESH_ENABLE_AMR libmesh_example_requires(false, "--enable-amr"); #else // Skip this example without --enable-second; requires d2phi #ifndef LIBMESH_ENABLE_SECOND_DERIVATIVES libmesh_example_requires(false, "--enable-second"); #else // Create a 2D mesh distributed across the default MPI communicator. // Subdivision surfaces do not appear to work with ParallelMesh yet. SerialMesh mesh (init.comm(), 2); // Read the coarse square mesh. mesh.read ("square_mesh.off"); // Resize the square plate to edge length L. const Real L = 100.; MeshTools::Modification::scale(mesh, L, L, L); // Quadrisect the mesh triangles a few times to obtain a // finer mesh. Subdivision surface elements require the // refinement data to be removed afterwards. MeshRefinement mesh_refinement (mesh); mesh_refinement.uniformly_refine (3); MeshTools::Modification::flatten (mesh); // Write the mesh before the ghost elements are added. #if defined(LIBMESH_HAVE_VTK) VTKIO(mesh).write ("without_ghosts.pvtu"); #endif #if defined(LIBMESH_HAVE_EXODUS_API) ExodusII_IO(mesh).write ("without_ghosts.e"); #endif // Print information about the triangulated mesh to the screen. mesh.print_info(); // Turn the triangulated mesh into a subdivision mesh // and add an additional row of "ghost" elements around // it in order to complete the extended local support of // the triangles at the boundaries. If the second // argument is set to true, the outermost existing // elements are converted into ghost elements, and the // actual physical mesh is thus getting smaller. MeshTools::Subdivision::prepare_subdivision_mesh (mesh, false); // Print information about the subdivision mesh to the screen. mesh.print_info(); // Write the mesh with the ghost elements added. // Compare this to the original mesh to see the difference. #if defined(LIBMESH_HAVE_VTK) VTKIO(mesh).write ("with_ghosts.pvtu"); #endif #if defined(LIBMESH_HAVE_EXODUS_API) ExodusII_IO(mesh).write ("with_ghosts.e"); #endif // Create an equation systems object. EquationSystems equation_systems (mesh); // Declare the system and its variables. // Create a linear implicit system named "Shell". LinearImplicitSystem & system = equation_systems.add_system<LinearImplicitSystem> ("Shell"); // Add the three translational deformation variables // "u", "v", "w" to "Shell". Since subdivision shell // elements meet the C1-continuity requirement, no // rotational or other auxiliary variables are needed. // Loop Subdivision Elements are always interpolated // by quartic box splines, hence the order must always // be \p FOURTH. system.add_variable ("u", FOURTH, SUBDIVISION); system.add_variable ("v", FOURTH, SUBDIVISION); system.add_variable ("w", FOURTH, SUBDIVISION); // Give the system a pointer to the matrix and rhs assembly // function. system.attach_assemble_function (assemble_shell); // Use the parameters of the equation systems object to // tell the shell system about the material properties, the // shell thickness, and the external load. const Real h = 1.; const Real E = 1.e7; const Real nu = 0.; const Real q = 1.; equation_systems.parameters.set<Real> ("thickness") = h; equation_systems.parameters.set<Real> ("young's modulus") = E; equation_systems.parameters.set<Real> ("poisson ratio") = nu; equation_systems.parameters.set<Real> ("uniform load") = q; // Initialize the data structures for the equation system. equation_systems.init(); // Print information about the system to the screen. equation_systems.print_info(); // Solve the linear system. system.solve(); // After solving the system, write the solution to a VTK // or ExodusII output file ready for import in, e.g., // Paraview. #if defined(LIBMESH_HAVE_VTK) VTKIO(mesh).write_equation_systems ("out.pvtu", equation_systems); #endif #if defined(LIBMESH_HAVE_EXODUS_API) ExodusII_IO(mesh).write_equation_systems ("out.e", equation_systems); #endif // Find the center node to measure the maximum deformation of the plate. Node* center_node = 0; Real nearest_dist_sq = mesh.point(0).size_sq(); for (unsigned int nid=1; nid<mesh.n_nodes(); ++nid) { const Real dist_sq = mesh.point(nid).size_sq(); if (dist_sq < nearest_dist_sq) { nearest_dist_sq = dist_sq; center_node = mesh.node_ptr(nid); } } // Finally, we evaluate the z-displacement "w" at the center node. const unsigned int w_var = system.variable_number ("w"); dof_id_type w_dof = center_node->dof_number (system.number(), w_var, 0); Number w = 0; if (w_dof >= system.get_dof_map().first_dof() && w_dof < system.get_dof_map().end_dof()) w = system.current_solution(w_dof); system.comm().sum(w); // The analytic solution for the maximum displacement of // a clamped square plate in pure bending, from Taylor, // Govindjee, Commun. Numer. Meth. Eng. 20, 757-765, 2004. const Real D = E * h*h*h / (12*(1-nu*nu)); const Real w_analytic = 0.001265319 * L*L*L*L * q / D; // Print the finite element solution and the analytic // prediction of the maximum displacement of the clamped // square plate to the screen. std::cout << "z-displacement of the center point: " << w << std::endl; std::cout << "Analytic solution for pure bending: " << w_analytic << std::endl; #endif // #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES #endif // #ifdef LIBMESH_ENABLE_AMR // All done. return 0; }