void build_mesh() { _mesh = new SerialMesh(*TestCommWorld); /* (0,1) (1,1) x---------------x | | | | | | | | | | x---------------x (0,0) (1,0) | | | | | | | | x---------------x (0,-1) (1,-1) */ _mesh->set_mesh_dimension(2); _mesh->add_point( Point(0.0,-1.0), 4 ); _mesh->add_point( Point(1.0,-1.0), 5 ); _mesh->add_point( Point(1.0, 0.0), 1 ); _mesh->add_point( Point(1.0, 1.0), 2 ); _mesh->add_point( Point(0.0, 1.0), 3 ); _mesh->add_point( Point(0.0, 0.0), 0 ); { Elem* elem_top = _mesh->add_elem( new Quad4 ); elem_top->set_node(0) = _mesh->node_ptr(0); elem_top->set_node(1) = _mesh->node_ptr(1); elem_top->set_node(2) = _mesh->node_ptr(2); elem_top->set_node(3) = _mesh->node_ptr(3); Elem* elem_bottom = _mesh->add_elem( new Quad4 ); elem_bottom->set_node(0) = _mesh->node_ptr(4); elem_bottom->set_node(1) = _mesh->node_ptr(5); elem_bottom->set_node(2) = _mesh->node_ptr(1); elem_bottom->set_node(3) = _mesh->node_ptr(0); Elem* edge = _mesh->add_elem( new Edge2 ); edge->set_node(0) = _mesh->node_ptr(0); edge->set_node(1) = _mesh->node_ptr(1); // 2D elements will have subdomain id 0, this one will have 1 edge->subdomain_id() = 1; } // libMesh will renumber, but we numbered according to its scheme // anyway. We do this because when we call uniformly_refine subsequenly, // it's going use skip_renumber=false. _mesh->prepare_for_use(false /*skip_renumber*/); }
// 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; }