diff --git a/cpppetsc/tools/generate_xdmf.py b/cpppetsc/tools/generate_xdmf.py index 34de96cc5116b2545498ff6a6d2522d7bc9d0a68..1c5fca94474ac79fe82ed05d31849831c838b05c 100755 --- a/cpppetsc/tools/generate_xdmf.py +++ b/cpppetsc/tools/generate_xdmf.py @@ -79,6 +79,8 @@ def number_of_corners_to_type( 'Triangle' >>> number_of_corners_to_type(4, 2) 'Quadrilateral' + >>> number_of_corners_to_type(6, 2) + 'Tri_6' >>> number_of_corners_to_type(2, 2) Traceback (most recent call last): generate_xdmf.UnsupportedElementType @@ -104,6 +106,7 @@ def number_of_corners_to_type( 2: { 3: "Triangle", 4: "Quadrilateral", + 6: "Tri_6", }, 3: { 4: "Tetrahedron", diff --git a/elements/src/CMakeLists.txt b/elements/src/CMakeLists.txt index 9b93172e53f14671bc5b3db1b7bf69fd6f93f0b4..3c1af024fc49f745bc9d23899f56d383c12196ca 100644 --- a/elements/src/CMakeLists.txt +++ b/elements/src/CMakeLists.txt @@ -70,6 +70,7 @@ add_library(${PROJECT_NAME}-elements shape/Quad4.cc shape/Seg2.cc shape/Tri3.cc + shape/Tri6.cc shape/ShapeBase.cc shape/ValueTrait.cc shape/automatic_gradients.cc diff --git a/elements/src/include/ae108/elements/shape/Tri6.h b/elements/src/include/ae108/elements/shape/Tri6.h new file mode 100644 index 0000000000000000000000000000000000000000..33cb73bef4dd54c61254d8d0ced5526a496c2950 --- /dev/null +++ b/elements/src/include/ae108/elements/shape/Tri6.h @@ -0,0 +1,62 @@ +// © 2020 ETH Zurich, Mechanics and Materials Lab +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "ae108/elements/shape/GradientTrait.h" +#include "ae108/elements/shape/PointTrait.h" +#include "ae108/elements/shape/ShapeBase.h" +#include "ae108/elements/shape/ValueTrait.h" +#include <cstddef> + +namespace ae108 { +namespace elements { +namespace shape { + +// refer to Cook et. al (2002), "Concepts and applications of Finite Element +// Analysis", 4th ed., p.265 +struct Tri6 : ShapeBase<std::size_t, double, 2, 6> {}; + +AE108_ELEMENTS_SHAPE_DEFINE_VALUE(Tri6, {{ + (1 - xi[0] - xi[1]) * + (1 - 2 * xi[0] - 2 * xi[1]), + xi[0] * (2 * xi[0] - 1), + xi[1] * (2 * xi[1] - 1), + 4 * xi[0] * (1 - xi[0] - xi[1]), + 4 * xi[0] * xi[1], + 4 * xi[1] * (1 - xi[0] - xi[1]), + }}); + +AE108_ELEMENTS_SHAPE_DEFINE_GRADIENTS( + Tri6, {{ + {{4. * (xi[0] + xi[1]) - 3., 4. * (xi[0] + xi[1]) - 3.}}, + {{4. * xi[0] - 1., 0.}}, + {{0., 4 * xi[1] - 1}}, + {{4. * (1 - 2 * xi[0] - xi[1]), -4. * xi[0]}}, + {{4. * xi[1], 4. * xi[0]}}, + {{-4. * xi[1], 4. * (1 - xi[0] - 2. * xi[1])}}, + }}); + +AE108_ELEMENTS_SHAPE_DEFINE_POINTS(Tri6, {{ + {{0., 0.}}, + {{1., 0.}}, + {{0., 1.}}, + {{0.5, 0.}}, + {{0.5, 0.5}}, + {{0., 0.5}}, + }}); + +} // namespace shape +} // namespace elements +} // namespace ae108 \ No newline at end of file diff --git a/elements/src/shape/Tri6.cc b/elements/src/shape/Tri6.cc new file mode 100644 index 0000000000000000000000000000000000000000..a7a67797db492553f0c688b0be2f093e4e5db39d --- /dev/null +++ b/elements/src/shape/Tri6.cc @@ -0,0 +1,15 @@ +// © 2020 ETH Zurich, Mechanics and Materials Lab +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ae108/elements/shape/Tri6.h" \ No newline at end of file diff --git a/elements/test/CMakeLists.txt b/elements/test/CMakeLists.txt index 11ff253edf6b70b34ccc10e8312fba744b9587bd..92574cd20de2b0f5b5d60aba1c58cbbeba23b839 100644 --- a/elements/test/CMakeLists.txt +++ b/elements/test/CMakeLists.txt @@ -30,6 +30,7 @@ add_executable(${PROJECT_NAME}-ElementsTests shape/Quad4_Test.cc shape/Seg2_Test.cc shape/Tri3_Test.cc + shape/Tri6_Test.cc tensor/Tensor_Test.cc tensor/as_matrix_of_columns_Test.cc tensor/as_matrix_of_rows_Test.cc diff --git a/elements/test/shape/Tri6_Test.cc b/elements/test/shape/Tri6_Test.cc new file mode 100644 index 0000000000000000000000000000000000000000..d4f9a94d68a96e0e638da7f973a54c96a929ddec --- /dev/null +++ b/elements/test/shape/Tri6_Test.cc @@ -0,0 +1,52 @@ +// © 2020 ETH Zurich, Mechanics and Materials Lab +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "Shape_Test.h" +#include "ae108/elements/shape/Tri6.h" +#include "ae108/elements/shape/get_points.h" +#include <gmock/gmock.h> + +using testing::DoubleEq; +using testing::ElementsAre; +using testing::Test; +using testing::Types; + +namespace ae108 { +namespace elements { +namespace shape { +namespace { + +using Configurations = Types<Tri6>; + +INSTANTIATE_TYPED_TEST_CASE_P(Tri6_Test, Shape_Test, Configurations); + +struct Tri6_Test : Test { + using Shape = Tri6; +}; + +TEST_F(Tri6_Test, points_are_correct) { + const auto points = get_points<Shape>(); + + EXPECT_THAT(points, ElementsAre(ElementsAre(DoubleEq(0.), DoubleEq(0.)), + ElementsAre(DoubleEq(1.), DoubleEq(0.)), + ElementsAre(DoubleEq(0.), DoubleEq(1.)), + ElementsAre(DoubleEq(0.5), DoubleEq(0.)), + ElementsAre(DoubleEq(0.5), DoubleEq(0.5)), + ElementsAre(DoubleEq(0.), DoubleEq(0.5)))); +} + +} // namespace +} // namespace shape +} // namespace elements +} // namespace ae108 \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index b5b59853892cca1d74ab7c4624eda0e58ea491e5..5536ee6c6d8d0c26450bd131375b269b9d4a2a99 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -48,3 +48,10 @@ add_executable(${PROJECT_NAME}-Input Input.cc) target_link_libraries(${PROJECT_NAME}-Input PRIVATE ae108::cpppetsc ) + +add_executable(${PROJECT_NAME}-QuadraticTriangles QuadraticTriangles.cc) +target_link_libraries(${PROJECT_NAME}-QuadraticTriangles + PRIVATE ae108::solve + PRIVATE ae108::assembly + PRIVATE ae108::elements +) diff --git a/examples/QuadraticTriangles.cc b/examples/QuadraticTriangles.cc new file mode 100644 index 0000000000000000000000000000000000000000..4b8c3b03ef032d53af2d11c39085cac589fcf7e1 --- /dev/null +++ b/examples/QuadraticTriangles.cc @@ -0,0 +1,199 @@ +// © 2020 ETH Zurich, Mechanics and Materials Lab +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ae108/assembly/Assembler.h" +#include "ae108/cpppetsc/Mesh.h" +#include "ae108/cpppetsc/ParallelComputePolicy.h" +#include "ae108/cpppetsc/Vector.h" +#include "ae108/cpppetsc/Viewer.h" +#include "ae108/cpppetsc/createVectorFromSource.h" +#include "ae108/cpppetsc/setName.h" +#include "ae108/cpppetsc/writeToViewer.h" +#include "ae108/elements/CoreElement.h" +#include "ae108/elements/embedding/IsoparametricEmbedding.h" +#include "ae108/elements/integrator/IsoparametricIntegrator.h" +#include "ae108/elements/materialmodels/Hookean.h" +#include "ae108/elements/mesh/generate_quadratic_triangle_mesh.h" +#include "ae108/elements/quadrature/Quadrature.h" +#include "ae108/elements/shape/Tri6.h" +#include "ae108/solve/NonlinearSolver.h" +#include <array> + +using namespace ae108; + +using Policy = cpppetsc::ParallelComputePolicy; +using Mesh = cpppetsc::Mesh<Policy>; +using Vector = cpppetsc::Vector<Policy>; +using Viewer = cpppetsc::Viewer<Policy>; +using BoundaryCondition = cpppetsc::MeshBoundaryCondition<Mesh>; + +namespace embedding = ae108::elements::embedding; +namespace integrator = ae108::elements::integrator; +namespace materialmodels = ae108::elements::materialmodels; +namespace quadrature = ae108::elements::quadrature; +namespace shape = ae108::elements::shape; +namespace mesh = ae108::elements::mesh; + +// In this example we will simulate a mesh of quadratic triangles that +// that we will generate later. + +// Let's specify the parameters of this mesh. + +constexpr auto number_of_vertices_per_element = Mesh::size_type{6}; +constexpr auto dimension = Mesh::size_type{2}; +constexpr auto dof_per_vertex = Mesh::size_type{2}; + +// Let's specify how the elements behave when deformed. + +// First, we select a Hookean (linear elastic) material model in 2D. + +using MaterialModel = materialmodels::Hookean<dimension>; + +// We'll choose the Tri6 shape functions (6 shape functions) that are defined +// in 2D reference space. + +using Shape = shape::Tri6; + +// Since also our physical space is 2D we may use the isoparametric embedding. + +using Embedding = embedding::IsoparametricEmbedding<Shape>; + +// Let's choose a simple quadrature rule in reference space. + +using Quadrature = quadrature::Quadrature<quadrature::QuadratureType::Simplex, + dimension, 2 /* order */>; + +// We use an "Integrator" to integrate in physical space. Of course, it depends +// on the selected quadrature rule and the embedding. In the case of the +// isoparametric embedding, this embedding is specified by the "Shape". + +using Integrator = integrator::IsoparametricIntegrator<Shape, Quadrature>; + +// Now we are ready to select our element type. + +using Element = elements::CoreElement<MaterialModel, Integrator>; + +// We will assemble e.g. energy using a collection of elements. This is done by +// the assembler. (The list DefaultFeaturePlugins contain the features (e.g. +// energy) that most elements support.) + +using Assembler = + assembly::Assembler<Element, assembly::DefaultFeaturePlugins, Policy>; + +// Our goal is to minimize the energy. This is done by the solver. + +using Solver = solve::NonlinearSolver<Assembler>; + +int main(int argc, char **argv) { + // PETSc must be initialized before using it. + Policy::handleError(PetscInitialize(&argc, &argv, NULL, NULL)); + + // We use a scope around our computation to make sure everything is cleaned up + // before we call PetscFinalize. + { + // Now we generate a geometry of 4 elements connected in x-direction. + // *-----*-----* + // | \ | \ | + // *-----*-----* + const auto geometry = + mesh::generate_quadratic_triangle_mesh({{2., 1.}}, {{2, 1}}); + + const auto mesh = + Mesh::fromConnectivity(dimension, geometry.connectivity(), + geometry.number_of_positions(), dof_per_vertex); + auto assembler = Assembler(); + + const auto model = MaterialModel(1.0, 0.); + + // Depending on whether we use MPI, our mesh may be distributed and not all + // elements are present on this computational node. + + // Let's add those elements that are "local" to the assembler. + + for (const auto &element : mesh.localElements()) { + const auto vertexIndices = element.vertexIndices(); + assembler.emplaceElement( + element, model, + Integrator(Embedding(Embedding::Collection<Embedding::PhysicalPoint>{{ + geometry.position_of_vertex(vertexIndices.at(0)), + geometry.position_of_vertex(vertexIndices.at(1)), + geometry.position_of_vertex(vertexIndices.at(2)), + geometry.position_of_vertex(vertexIndices.at(3)), + geometry.position_of_vertex(vertexIndices.at(4)), + geometry.position_of_vertex(vertexIndices.at(5)), + }}))); + } + + // We need to create a solver. We do not use the time, so we can set it to + // zero. + + const auto solver = Solver(&mesh); + const auto time = Element::Time{0.}; + + // Before we can produce meaningful results, we need to specify boundary + // conditions. Let's fix the nodes at x=0 and pull on the nodes at x=2. + + std::vector<BoundaryCondition> boundary_conditions; + for (const auto &vertex : mesh.localVertices()) { + const auto position = geometry.position_of_vertex(vertex.index()); + const auto tolerance = .1; + if (std::abs(position[0] - 0.) < tolerance) { + // The displacement in x direction is zero. + boundary_conditions.push_back({vertex, 0, 0.}); + // The displacement in y direction is zero. + boundary_conditions.push_back({vertex, 1, 0.}); + } else if (std::abs(position[0] - 2.) < tolerance) { + // The displacement in x direction is .5. + boundary_conditions.push_back({vertex, 0, .5}); + // The displacement in y direction is 0. + boundary_conditions.push_back({vertex, 1, 0.}); + } + } + + // We are ready to minimize the energy. + + auto result = solver.computeSolution( + boundary_conditions, Vector::fromGlobalMesh(mesh), time, &assembler); + + // As a final step, we write the result to a file. + + // We may view this result in e.g. ParaView by generating an XDMF file with + // the `generate_xdmf.py` script in the repository, and opening this file + // with ParaView ("XDMF Reader"). + + // First we collect the coordinates in a vector. + using DataSource = std::function<void(Mesh::size_type, Mesh::value_type *)>; + auto coordinates = cpppetsc::createVectorFromSource( + mesh, dimension, + DataSource( + [&](const Mesh::size_type index, Mesh::value_type *const out) { + const auto &position = geometry.position_of_vertex(index); + std::copy(position.begin(), position.end(), out); + })); + cpppetsc::setName("coordinates", &coordinates); + + // Now we write the mesh to a file. + auto viewer = Viewer::fromHdf5FilePath("quadratic_triangles.ae108", + Viewer::Mode::write); + cpppetsc::writeToViewer(mesh, coordinates, &viewer); + + // Let's write the result to the file. + cpppetsc::setName("result", &result); + cpppetsc::writeToViewer(result, &viewer); + + fprintf(stderr, "The data has been written to the file " + "\"quadratic_triangles.ae108\".\n"); + } + Policy::handleError(PetscFinalize()); +}