From f27a5413861d2a930b3dfb731da8667f6b94cd1d Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Fri, 23 Aug 2024 09:46:49 -0700 Subject: [PATCH 01/92] adding testing tool that generates all possible face orientations --- .../numerics/functional/tests/CMakeLists.txt | 1 + .../tests/dg_restriction_operators.cpp | 144 ++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 src/serac/numerics/functional/tests/dg_restriction_operators.cpp diff --git a/src/serac/numerics/functional/tests/CMakeLists.txt b/src/serac/numerics/functional/tests/CMakeLists.txt index 9118582f26..65409c8d9c 100644 --- a/src/serac/numerics/functional/tests/CMakeLists.txt +++ b/src/serac/numerics/functional/tests/CMakeLists.txt @@ -18,6 +18,7 @@ blt_add_executable(NAME tensor_unit_tests set(functional_serial_test_sources functional_shape_derivatives.cpp simplex_basis_function_unit_tests.cpp + dg_restriction_operators.cpp bug_boundary_qoi.cpp domain_tests.cpp geometric_factors_tests.cpp diff --git a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp new file mode 100644 index 0000000000..fc805b835d --- /dev/null +++ b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp @@ -0,0 +1,144 @@ +#include + +#include "serac/numerics/functional/domain.hpp" + +using namespace serac; + +std::string mesh_dir = SERAC_REPO_DIR "/data/meshes/"; + +int possible_permutations(mfem::Geometry::Type geom) { + if (geom == mfem::Geometry::TRIANGLE) return 3; + if (geom == mfem::Geometry::SQUARE) return 4; + if (geom == mfem::Geometry::TETRAHEDRON) return 12; + if (geom == mfem::Geometry::CUBE) return 24; + return -1; +} + +template < int n > +std::array< int, n > apply_permutation(const int (&arr)[n], const int (&p)[n]) { + std::array permuted_arr{}; + for (int i = 0; i < n; i++) { + permuted_arr[i] = arr[p[i]]; + } + return permuted_arr; +} + +mfem::Mesh generate_permuted_meshes(mfem::Geometry::Type geom, int i) { + + if (geom == mfem::Geometry::TRIANGLE) { + constexpr int dim = 2; + constexpr int num_elements = 2; + constexpr int num_vertices = 4; + constexpr int num_permutations = 3; + int positive_permutations[num_permutations][3] = {{0, 1, 2}, {1, 2, 0}, {2, 0, 1}}; + int elements[num_elements][3] = {{0, 1, 3}, {1, 2, 3}}; + double vertices[num_vertices][dim] = {{0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}, {0.0, 1.0}}; + + mfem::Mesh output(dim, num_vertices, num_elements); + + for (auto vertex : vertices) { output.AddVertex(vertex); } + + // the first element is always fixed + output.AddTri(elements[0]); + + // but the second element is permuted to the specified orientation + auto permuted_element = apply_permutation(elements[1], positive_permutations[i]); + output.AddTri(permuted_element.data()); + + return output; + } + + if (geom == mfem::Geometry::SQUARE) { + constexpr int dim = 2; + constexpr int num_elements = 2; + constexpr int num_vertices = 6; + constexpr int num_permutations = 4; + int positive_permutations[num_permutations][4] = {{0, 1, 2, 3}, {1, 2, 3, 0}, {2, 3, 0, 1}, {3, 0, 1, 2}}; + int elements[2][4] = {{0, 1, 4, 3}, {1, 2, 5, 4}}; + double vertices[6][2] = {{0.0, 0.0}, {1.0, 0.0}, {2.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}, {2.0, 1.0}}; + + mfem::Mesh output(dim, num_vertices, num_elements); + + for (auto vertex : vertices) { output.AddVertex(vertex); } + + // the first element is always fixed + output.AddQuad(elements[0]); + + // but the second element is permuted to the specified orientation + auto permuted_element = apply_permutation(elements[1], positive_permutations[i]); + output.AddQuad(permuted_element.data()); + + return output; + } + + if (geom == mfem::Geometry::TETRAHEDRON) { + constexpr int dim = 3; + constexpr int num_elements = 2; + constexpr int num_vertices = 5; + constexpr int num_permutations = 12; + int positive_permutations[num_permutations][4] = { + {0, 1, 2, 3}, {0, 2, 3, 1}, {0, 3, 1, 2}, {1, 0, 3, 2}, + {1, 2, 0, 3}, {1, 3, 2, 0}, {2, 0, 1, 3}, {2, 1, 3, 0}, + {2, 3, 0, 1}, {3, 0, 2, 1}, {3, 1, 0, 2}, {3, 2, 1, 0} + }; + int elements[num_elements][4] = {{0, 1, 2, 3}, {1, 2, 3, 4}}; + double vertices[num_vertices][3] = {{0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, {1, 1, 1}}; + + mfem::Mesh output(dim, num_vertices, num_elements); + + for (auto vertex : vertices) { output.AddVertex(vertex); } + + // the first element is always fixed + output.AddTet(elements[0]); + + // but the second element is permuted to the specified orientation + auto permuted_element = apply_permutation(elements[1], positive_permutations[i]); + output.AddTet(permuted_element.data()); + + return output; + } + + if (geom == mfem::Geometry::CUBE) { + constexpr int dim = 3; + constexpr int num_elements = 2; + constexpr int num_vertices = 12; + constexpr int num_permutations = 24; + int positive_permutations[num_permutations][8] = { + {0, 1, 2, 3, 4, 5, 6, 7}, {0, 3, 7, 4, 1, 2, 6, 5}, {0, 4, 5, 1, 3, 7, 6, 2}, + {1, 0, 4, 5, 2, 3, 7, 6}, {1, 2, 3, 0, 5, 6, 7, 4}, {1, 5, 6, 2, 0, 4, 7, 3}, + {2, 1, 5, 6, 3, 0, 4, 7}, {2, 3, 0, 1, 6, 7, 4, 5}, {2, 6, 7, 3, 1, 5, 4, 0}, + {3, 0, 1, 2, 7, 4, 5, 6}, {3, 2, 6, 7, 0, 1, 5, 4}, {3, 7, 4, 0, 2, 6, 5, 1}, + {4, 0, 3, 7, 5, 1, 2, 6}, {4, 5, 1, 0, 7, 6, 2, 3}, {4, 7, 6, 5, 0, 3, 2, 1}, + {5, 1, 0, 4, 6, 2, 3, 7}, {5, 4, 7, 6, 1, 0, 3, 2}, {5, 6, 2, 1, 4, 7, 3, 0}, + {6, 2, 1, 5, 7, 3, 0, 4}, {6, 5, 4, 7, 2, 1, 0, 3}, {6, 7, 3, 2, 5, 4, 0, 1}, + {7, 3, 2, 6, 4, 0, 1, 5}, {7, 4, 0, 3, 6, 5, 1, 2}, {7, 6, 5, 4, 3, 2, 1, 0} + }; + + double vertices[num_vertices][3] = { + {0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}, + {0, 0, 1}, {1, 0, 1}, {1, 1, 1}, {0, 1, 1}, + {0, 0, 2}, {1, 0, 2}, {1, 1, 2}, {0, 1, 2} + }; + + int elements[num_elements][8] = { + {0, 1, 2, 3, 4, 5, 6, 7}, + {4, 5, 6, 7, 8, 9, 10, 11} + }; + + mfem::Mesh output(dim, num_vertices, num_elements); + + for (auto vertex : vertices) { output.AddVertex(vertex); } + + // the first element is always fixed + output.AddTet(elements[0]); + + // but the second element is permuted to the specified orientation + auto permuted_element = apply_permutation(elements[1], positive_permutations[i]); + output.AddTet(permuted_element.data()); + + return output; + } + + return {}; + +} From 26766aae382c8151e6bd23c2366c9c923a468a76 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Fri, 23 Aug 2024 09:48:31 -0700 Subject: [PATCH 02/92] fix AddTet to AddHex --- .../functional/tests/dg_restriction_operators.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp index fc805b835d..3477aa3114 100644 --- a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp +++ b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp @@ -54,8 +54,8 @@ mfem::Mesh generate_permuted_meshes(mfem::Geometry::Type geom, int i) { constexpr int num_vertices = 6; constexpr int num_permutations = 4; int positive_permutations[num_permutations][4] = {{0, 1, 2, 3}, {1, 2, 3, 0}, {2, 3, 0, 1}, {3, 0, 1, 2}}; - int elements[2][4] = {{0, 1, 4, 3}, {1, 2, 5, 4}}; - double vertices[6][2] = {{0.0, 0.0}, {1.0, 0.0}, {2.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}, {2.0, 1.0}}; + int elements[num_elements][4] = {{0, 1, 4, 3}, {1, 2, 5, 4}}; + double vertices[num_vertices][dim] = {{0.0, 0.0}, {1.0, 0.0}, {2.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}, {2.0, 1.0}}; mfem::Mesh output(dim, num_vertices, num_elements); @@ -82,7 +82,7 @@ mfem::Mesh generate_permuted_meshes(mfem::Geometry::Type geom, int i) { {2, 3, 0, 1}, {3, 0, 2, 1}, {3, 1, 0, 2}, {3, 2, 1, 0} }; int elements[num_elements][4] = {{0, 1, 2, 3}, {1, 2, 3, 4}}; - double vertices[num_vertices][3] = {{0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, {1, 1, 1}}; + double vertices[num_vertices][dim] = {{0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, {1, 1, 1}}; mfem::Mesh output(dim, num_vertices, num_elements); @@ -114,7 +114,7 @@ mfem::Mesh generate_permuted_meshes(mfem::Geometry::Type geom, int i) { {7, 3, 2, 6, 4, 0, 1, 5}, {7, 4, 0, 3, 6, 5, 1, 2}, {7, 6, 5, 4, 3, 2, 1, 0} }; - double vertices[num_vertices][3] = { + double vertices[num_vertices][dim] = { {0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}, {0, 0, 1}, {1, 0, 1}, {1, 1, 1}, {0, 1, 1}, {0, 0, 2}, {1, 0, 2}, {1, 1, 2}, {0, 1, 2} @@ -130,11 +130,11 @@ mfem::Mesh generate_permuted_meshes(mfem::Geometry::Type geom, int i) { for (auto vertex : vertices) { output.AddVertex(vertex); } // the first element is always fixed - output.AddTet(elements[0]); + output.AddHex(elements[0]); // but the second element is permuted to the specified orientation auto permuted_element = apply_permutation(elements[1], positive_permutations[i]); - output.AddTet(permuted_element.data()); + output.AddHex(permuted_element.data()); return output; } From 3a7c374fa96caa5459bbeec1a4897d3f56080523 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Fri, 23 Aug 2024 11:02:03 -0700 Subject: [PATCH 03/92] add InteriorFace fn, add some preliminary tests --- src/serac/numerics/functional/domain.cpp | 44 +++++++++++++++ src/serac/numerics/functional/domain.hpp | 6 ++- .../tests/dg_restriction_operators.cpp | 54 ++++++++++++++++++- 3 files changed, 102 insertions(+), 2 deletions(-) diff --git a/src/serac/numerics/functional/domain.cpp b/src/serac/numerics/functional/domain.cpp index 3d39e2d0a6..dbb86d2a60 100644 --- a/src/serac/numerics/functional/domain.cpp +++ b/src/serac/numerics/functional/domain.cpp @@ -527,6 +527,50 @@ Domain EntireBoundary(const mfem::Mesh& mesh) return output; } +/// @brief constructs a domain from all the interior face elements in a mesh +Domain InteriorFaces(const mfem::Mesh& mesh) { + + Domain output{mesh, mesh.SpaceDimension() - 1, Domain::Type::InteriorFaces}; + + int edge_id = 0; + int tri_id = 0; + int quad_id = 0; + + for (int f = 0; f < mesh.GetNumFaces(); f++) { + + // discard faces with the wrong type + if (!mesh.GetFaceInformation(f).IsInterior()) continue; + + auto geom = mesh.GetFaceGeometry(f); + + switch (geom) { + case mfem::Geometry::SEGMENT: + output.edge_ids_.push_back(edge_id++); + output.mfem_edge_ids_.push_back(f); + break; + case mfem::Geometry::TRIANGLE: + output.tri_ids_.push_back(tri_id++); + output.mfem_tri_ids_.push_back(f); + break; + case mfem::Geometry::SQUARE: + output.quad_ids_.push_back(quad_id++); + output.mfem_quad_ids_.push_back(f); + break; + default: + SLIC_ERROR("unsupported element type"); + break; + } + } + + return output; + +} + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + /// @cond using c_iter = std::vector::const_iterator; using b_iter = std::back_insert_iterator>; diff --git a/src/serac/numerics/functional/domain.hpp b/src/serac/numerics/functional/domain.hpp index ad7c169117..f29591e819 100644 --- a/src/serac/numerics/functional/domain.hpp +++ b/src/serac/numerics/functional/domain.hpp @@ -23,7 +23,8 @@ struct Domain { enum Type { Elements, - BoundaryElements + BoundaryElements, + InteriorFaces }; static constexpr int num_types = 2; ///< the number of entries in the Type enum @@ -140,6 +141,9 @@ Domain EntireDomain(const mfem::Mesh& mesh); /// @brief constructs a domain from all the boundary elements in a mesh Domain EntireBoundary(const mfem::Mesh& mesh); +/// @brief constructs a domain from all the interior face elements in a mesh +Domain InteriorFaces(const mfem::Mesh& mesh); + /// @brief create a new domain that is the union of `a` and `b` Domain operator|(const Domain& a, const Domain& b); diff --git a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp index 3477aa3114..09b28e0e4e 100644 --- a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp +++ b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp @@ -23,7 +23,7 @@ std::array< int, n > apply_permutation(const int (&arr)[n], const int (&p)[n]) { return permuted_arr; } -mfem::Mesh generate_permuted_meshes(mfem::Geometry::Type geom, int i) { +mfem::Mesh generate_permuted_mesh(mfem::Geometry::Type geom, int i) { if (geom == mfem::Geometry::TRIANGLE) { constexpr int dim = 2; @@ -45,6 +45,8 @@ mfem::Mesh generate_permuted_meshes(mfem::Geometry::Type geom, int i) { auto permuted_element = apply_permutation(elements[1], positive_permutations[i]); output.AddTri(permuted_element.data()); + output.FinalizeMesh(); + return output; } @@ -68,6 +70,8 @@ mfem::Mesh generate_permuted_meshes(mfem::Geometry::Type geom, int i) { auto permuted_element = apply_permutation(elements[1], positive_permutations[i]); output.AddQuad(permuted_element.data()); + output.FinalizeMesh(); + return output; } @@ -95,6 +99,8 @@ mfem::Mesh generate_permuted_meshes(mfem::Geometry::Type geom, int i) { auto permuted_element = apply_permutation(elements[1], positive_permutations[i]); output.AddTet(permuted_element.data()); + output.FinalizeMesh(); + return output; } @@ -136,9 +142,55 @@ mfem::Mesh generate_permuted_meshes(mfem::Geometry::Type geom, int i) { auto permuted_element = apply_permutation(elements[1], positive_permutations[i]); output.AddHex(permuted_element.data()); + output.FinalizeMesh(); + return output; } return {}; } + +TEST(DomainInterior, TriMesh) { + mfem::Mesh mesh = generate_permuted_mesh(mfem::Geometry::TRIANGLE, 0); + EXPECT_EQ(mesh.GetNE(), 2); + std::ofstream outfile("triangle.mesh"); + mesh.Print(outfile); + + Domain interior_faces = InteriorFaces(mesh); + EXPECT_EQ(interior_faces.edge_ids_.size(), 1); + EXPECT_EQ(interior_faces.mfem_edge_ids_.size(), 1); +} + +TEST(DomainInterior, QuadMesh) { + mfem::Mesh mesh = generate_permuted_mesh(mfem::Geometry::SQUARE, 0); + EXPECT_EQ(mesh.GetNE(), 2); + std::ofstream outfile("quad.mesh"); + mesh.Print(outfile); + + Domain interior_faces = InteriorFaces(mesh); + EXPECT_EQ(interior_faces.edge_ids_.size(), 1); + EXPECT_EQ(interior_faces.mfem_edge_ids_.size(), 1); +} + +TEST(DomainInterior, TetMesh) { + mfem::Mesh mesh = generate_permuted_mesh(mfem::Geometry::TETRAHEDRON, 0); + EXPECT_EQ(mesh.GetNE(), 2); + std::ofstream outfile("tet.mesh"); + mesh.Print(outfile); + + Domain interior_faces = InteriorFaces(mesh); + EXPECT_EQ(interior_faces.tri_ids_.size(), 1); + EXPECT_EQ(interior_faces.mfem_tri_ids_.size(), 1); +} + +TEST(DomainInterior, HexMesh) { + mfem::Mesh mesh = generate_permuted_mesh(mfem::Geometry::CUBE, 0); + EXPECT_EQ(mesh.GetNE(), 2); + std::ofstream outfile("hex.mesh"); + mesh.Print(outfile); + + Domain interior_faces = InteriorFaces(mesh); + EXPECT_EQ(interior_faces.quad_ids_.size(), 1); + EXPECT_EQ(interior_faces.mfem_quad_ids_.size(), 1); +} From bd2947be65f934f50ea2cff648ced6da66dbad0f Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Fri, 23 Aug 2024 16:09:27 -0700 Subject: [PATCH 04/92] fix linkage error in restriction helper function, setting up next tests --- .../functional/element_restriction.hpp | 4 +- .../tests/dg_restriction_operators.cpp | 147 ++++++++++++++---- 2 files changed, 117 insertions(+), 34 deletions(-) diff --git a/src/serac/numerics/functional/element_restriction.hpp b/src/serac/numerics/functional/element_restriction.hpp index 7f12dca598..f949212b8c 100644 --- a/src/serac/numerics/functional/element_restriction.hpp +++ b/src/serac/numerics/functional/element_restriction.hpp @@ -243,7 +243,7 @@ struct BlockElementRestriction { * @param fes the finite element space containing the dof information * @param geom the kind of element geometry */ -Array2D GetElementDofs(mfem::FiniteElementSpace* fes, mfem::Geometry::Type geom); +axom::Array GetElementDofs(const mfem::FiniteElementSpace* fes, mfem::Geometry::Type geom); /** * @brief Get the list of dofs for each face element (of the specified geometry) from the mfem::FiniteElementSpace @@ -252,4 +252,4 @@ Array2D GetElementDofs(mfem::FiniteElementSpace* fes, mfem::Geometry::Type * @param geom the kind of element geometry * @param type whether the face is of interior or boundary type */ -Array2D GetFaceDofs(mfem::FiniteElementSpace* fes, mfem::Geometry::Type face_geom, FaceType type); +axom::Array GetFaceDofs(const mfem::FiniteElementSpace* fes, mfem::Geometry::Type face_geom, FaceType type); diff --git a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp index 09b28e0e4e..ed4f190d0d 100644 --- a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp +++ b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp @@ -1,6 +1,7 @@ #include #include "serac/numerics/functional/domain.hpp" +#include "serac/numerics/functional/element_restriction.hpp" using namespace serac; @@ -31,6 +32,19 @@ mfem::Mesh generate_permuted_mesh(mfem::Geometry::Type geom, int i) { constexpr int num_vertices = 4; constexpr int num_permutations = 3; int positive_permutations[num_permutations][3] = {{0, 1, 2}, {1, 2, 0}, {2, 0, 1}}; + + /* + y + ^ + | + 3----------2 + |\, | + | \, | + | \, | + | \, | + | \,| + 0----------1--> x + */ int elements[num_elements][3] = {{0, 1, 3}, {1, 2, 3}}; double vertices[num_vertices][dim] = {{0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}, {0.0, 1.0}}; @@ -56,6 +70,19 @@ mfem::Mesh generate_permuted_mesh(mfem::Geometry::Type geom, int i) { constexpr int num_vertices = 6; constexpr int num_permutations = 4; int positive_permutations[num_permutations][4] = {{0, 1, 2, 3}, {1, 2, 3, 0}, {2, 3, 0, 1}, {3, 0, 1, 2}}; + + + /* + y + ^ + | + 3----------4----------5 + | | | + | | | + | | | + | | | + 0----------1----------2--> x + */ int elements[num_elements][4] = {{0, 1, 4, 3}, {1, 2, 5, 4}}; double vertices[num_vertices][dim] = {{0.0, 0.0}, {1.0, 0.0}, {2.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}, {2.0, 1.0}}; @@ -120,6 +147,31 @@ mfem::Mesh generate_permuted_mesh(mfem::Geometry::Type geom, int i) { {7, 3, 2, 6, 4, 0, 1, 5}, {7, 4, 0, 3, 6, 5, 1, 2}, {7, 6, 5, 4, 3, 2, 1, 0} }; + /* + z + ^ + | + 8----------11 + |\ |\ + | \ | \ + | \ | \ + | 9------+---10 + | | | | + 4---+------7 | + |\ | |\ | + | \ | | \ | + | \| | \| + | 5------+---6 + | | | | + 0---+------3---|--> y + \ | \ | + \ | \ | + \| \| + 1----------2 + \ + v + x + */ double vertices[num_vertices][dim] = { {0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}, {0, 0, 1}, {1, 0, 1}, {1, 1, 1}, {0, 1, 1}, @@ -151,46 +203,77 @@ mfem::Mesh generate_permuted_mesh(mfem::Geometry::Type geom, int i) { } +std::ostream & operator<<(std::ostream & out, axom::Array arr) { + for (int i = 0; i < arr.shape()[0]; i++) { + for (int j = 0; j < arr.shape()[1]; j++) { + out << arr[i][j].index() << " "; + } + out << std::endl; + } + return out; +} + TEST(DomainInterior, TriMesh) { - mfem::Mesh mesh = generate_permuted_mesh(mfem::Geometry::TRIANGLE, 0); - EXPECT_EQ(mesh.GetNE(), 2); - std::ofstream outfile("triangle.mesh"); - mesh.Print(outfile); - - Domain interior_faces = InteriorFaces(mesh); - EXPECT_EQ(interior_faces.edge_ids_.size(), 1); - EXPECT_EQ(interior_faces.mfem_edge_ids_.size(), 1); + constexpr int p = 1; + constexpr int dim = 2; + + mfem::Mesh mesh = generate_permuted_mesh(mfem::Geometry::TRIANGLE, 1); + EXPECT_EQ(mesh.GetNE(), 2); + std::ofstream outfile("triangle.mesh"); + mesh.Print(outfile); + + Domain interior_faces = InteriorFaces(mesh); + EXPECT_EQ(interior_faces.edge_ids_.size(), 1); + EXPECT_EQ(interior_faces.mfem_edge_ids_.size(), 1); + + auto H1_fec = std::make_unique(p, dim); + auto Hcurl_fec = std::make_unique(p, dim); + auto L2_fec = std::make_unique(p, dim, mfem::BasisType::GaussLobatto); + + auto H1_fes = std::make_unique(&mesh, H1_fec.get()); + auto Hcurl_fes = std::make_unique(&mesh, Hcurl_fec.get()); + auto L2_fes = std::make_unique(&mesh, L2_fec.get()); + + auto H1_dofs = GetFaceDofs(H1_fes.get(), mfem::Geometry::SEGMENT, FaceType::INTERIOR); + std::cout << H1_dofs << std::endl; + + auto Hcurl_dofs = GetFaceDofs(Hcurl_fes.get(), mfem::Geometry::SEGMENT, FaceType::INTERIOR); + std::cout << Hcurl_dofs << std::endl; + + auto L2_dofs = GetFaceDofs(L2_fes.get(), mfem::Geometry::SEGMENT, FaceType::INTERIOR); + std::cout << L2_dofs << std::endl; + } TEST(DomainInterior, QuadMesh) { - mfem::Mesh mesh = generate_permuted_mesh(mfem::Geometry::SQUARE, 0); - EXPECT_EQ(mesh.GetNE(), 2); - std::ofstream outfile("quad.mesh"); - mesh.Print(outfile); - - Domain interior_faces = InteriorFaces(mesh); - EXPECT_EQ(interior_faces.edge_ids_.size(), 1); - EXPECT_EQ(interior_faces.mfem_edge_ids_.size(), 1); + mfem::Mesh mesh = generate_permuted_mesh(mfem::Geometry::SQUARE, 0); + EXPECT_EQ(mesh.GetNE(), 2); + std::ofstream outfile("quad.mesh"); + mesh.Print(outfile); + + Domain interior_faces = InteriorFaces(mesh); + EXPECT_EQ(interior_faces.edge_ids_.size(), 1); + EXPECT_EQ(interior_faces.mfem_edge_ids_.size(), 1); } TEST(DomainInterior, TetMesh) { - mfem::Mesh mesh = generate_permuted_mesh(mfem::Geometry::TETRAHEDRON, 0); - EXPECT_EQ(mesh.GetNE(), 2); - std::ofstream outfile("tet.mesh"); - mesh.Print(outfile); - - Domain interior_faces = InteriorFaces(mesh); - EXPECT_EQ(interior_faces.tri_ids_.size(), 1); - EXPECT_EQ(interior_faces.mfem_tri_ids_.size(), 1); + mfem::Mesh mesh = generate_permuted_mesh(mfem::Geometry::TETRAHEDRON, 0); + EXPECT_EQ(mesh.GetNE(), 2); + std::ofstream outfile("tet.mesh"); + mesh.Print(outfile); + + Domain interior_faces = InteriorFaces(mesh); + EXPECT_EQ(interior_faces.tri_ids_.size(), 1); + EXPECT_EQ(interior_faces.mfem_tri_ids_.size(), 1); } TEST(DomainInterior, HexMesh) { - mfem::Mesh mesh = generate_permuted_mesh(mfem::Geometry::CUBE, 0); - EXPECT_EQ(mesh.GetNE(), 2); - std::ofstream outfile("hex.mesh"); - mesh.Print(outfile); - - Domain interior_faces = InteriorFaces(mesh); - EXPECT_EQ(interior_faces.quad_ids_.size(), 1); - EXPECT_EQ(interior_faces.mfem_quad_ids_.size(), 1); + mfem::Mesh mesh = generate_permuted_mesh(mfem::Geometry::CUBE, 0); + EXPECT_EQ(mesh.GetNE(), 2); + std::ofstream outfile("hex.mesh"); + mesh.Print(outfile); + + Domain interior_faces = InteriorFaces(mesh); + EXPECT_EQ(interior_faces.quad_ids_.size(), 1); + EXPECT_EQ(interior_faces.mfem_quad_ids_.size(), 1); } From 044162ff7848ec962f403f8cd5b7c67937bad285 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Fri, 23 Aug 2024 16:57:35 -0700 Subject: [PATCH 05/92] trying to figure out how to get node positions/directions from mfem --- .../tests/dg_restriction_operators.cpp | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp index ed4f190d0d..7ef2745a8b 100644 --- a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp +++ b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp @@ -213,6 +213,25 @@ std::ostream & operator<<(std::ostream & out, axom::Array(&mesh, Hcurl_fec.get()); auto L2_fes = std::make_unique(&mesh, L2_fec.get()); + mfem::GridFunction H1_gf(H1_fes.get()); + mfem::GridFunction Hcurl_gf(Hcurl_fes.get()); + mfem::GridFunction L2_gf(L2_fes.get()); + + mfem::FunctionCoefficient scalar_func(scalar_func_2D); + mfem::VectorFunctionCoefficient vector_func(dim, vector_func_2D); + + H1_gf.ProjectCoefficient(scalar_func); + Hcurl_gf.ProjectCoefficient(vector_func); + H1_gf.ProjectCoefficient(scalar_func); + auto H1_dofs = GetFaceDofs(H1_fes.get(), mfem::Geometry::SEGMENT, FaceType::INTERIOR); std::cout << H1_dofs << std::endl; From d3529fa576fa37d9726b087c18da02ec7cfa5b30 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Sun, 25 Aug 2024 16:11:30 -0700 Subject: [PATCH 06/92] decode hcurl dof orientations for face elements --- src/serac/numerics/functional/element_restriction.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/serac/numerics/functional/element_restriction.cpp b/src/serac/numerics/functional/element_restriction.cpp index 9068da4056..d0f3749b8c 100644 --- a/src/serac/numerics/functional/element_restriction.cpp +++ b/src/serac/numerics/functional/element_restriction.cpp @@ -363,7 +363,11 @@ axom::Array GetFaceDofs(const mfem::FiniteEleme if (isHcurl(*fes)) { for (int k = 0; k < dofs.Size(); k++) { - face_dofs.push_back(uint64_t(dofs[k])); + if (dofs[k] >= 0) { + face_dofs.push_back(DoF{uint64_t(dofs[k]), 0}); + } else { + face_dofs.push_back(DoF{uint64_t(-1 - dofs[k]), 1}); + } } } else { for (int k = 0; k < dofs.Size(); k++) { From 0e96c71457fe23352c6794a23473dd0c7f81bb1f Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Sun, 25 Aug 2024 16:12:04 -0700 Subject: [PATCH 07/92] add exhaustive test suite of DG face dof agreement --- .../tests/dg_restriction_operators.cpp | 328 +++++++++++++----- 1 file changed, 250 insertions(+), 78 deletions(-) diff --git a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp index 7ef2745a8b..2c2d175a1d 100644 --- a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp +++ b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp @@ -7,6 +7,14 @@ using namespace serac; std::string mesh_dir = SERAC_REPO_DIR "/data/meshes/"; +constexpr mfem::Geometry::Type face_type(mfem::Geometry::Type geom) { + if (geom == mfem::Geometry::TRIANGLE) return mfem::Geometry::SEGMENT; + if (geom == mfem::Geometry::SQUARE) return mfem::Geometry::SEGMENT; + if (geom == mfem::Geometry::TETRAHEDRON) return mfem::Geometry::TRIANGLE; + if (geom == mfem::Geometry::CUBE) return mfem::Geometry::SQUARE; + return mfem::Geometry::INVALID; +} + int possible_permutations(mfem::Geometry::Type geom) { if (geom == mfem::Geometry::TRIANGLE) return 3; if (geom == mfem::Geometry::SQUARE) return 4; @@ -38,11 +46,11 @@ mfem::Mesh generate_permuted_mesh(mfem::Geometry::Type geom, int i) { ^ | 3----------2 - |\, | - | \, | - | \, | - | \, | - | \,| + |'. | + | '. | + | '. | + | '. | + | '.| 0----------1--> x */ int elements[num_elements][3] = {{0, 1, 3}, {1, 2, 3}}; @@ -112,6 +120,24 @@ mfem::Mesh generate_permuted_mesh(mfem::Geometry::Type geom, int i) { {1, 2, 0, 3}, {1, 3, 2, 0}, {2, 0, 1, 3}, {2, 1, 3, 0}, {2, 3, 0, 1}, {3, 0, 2, 1}, {3, 1, 0, 2}, {3, 2, 1, 0} }; + + /* + + .4. + y .*'/ '*. + \ .*' / '*. + 2--.../ '*. + |\ / '---... '*. x + | \ / '''---...'*. .*' + | / :::>1 + z | / \ ...---'''.*' + '*. |/ ...---''' .*' + 3--'''\ .*' + '*. \ .*' + '*.\ .*' + '0' + + */ int elements[num_elements][4] = {{0, 1, 2, 3}, {1, 2, 3, 4}}; double vertices[num_vertices][dim] = {{0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, {1, 1, 1}}; @@ -213,41 +239,59 @@ std::ostream & operator<<(std::ostream & out, axom::Array +double scalar_func(const mfem::Vector & x, double /*t*/) { + if constexpr (dim == 2) { + return x[0] + 10 * x[1]; + } else { + return x[0] + 10 * x[1] + 100 * x[2]; + } } -double scalar_func_3D(const mfem::Vector & x, double /*t*/) { - return x[0] + 10 * x[1] + 100 * x[2]; +template < int dim > +void vector_func(const mfem::Vector & x, double /*t*/, mfem::Vector & output) { + if constexpr (dim == 2) { + output[0] = +x[1]; + output[1] = -x[0]; + } else { + output[0] = +x[1]; + output[1] = -x[0]+x[2]; + output[2] = -x[1]; + } } -void vector_func_2D(const mfem::Vector & x, double /*t*/, mfem::Vector & output) { - output[0] = +x[1]; - output[1] = -x[0]; -} +template < mfem::Geometry::Type geom > +void parametrized_test(int polynomial_order, int permutation) { -void vector_func_3D(const mfem::Vector & x, double /*t*/, mfem::Vector & output) { - output[0] = +x[1]; - output[1] = -x[0]+x[2]; - output[2] = -x[1]; -} + constexpr mfem::Geometry::Type face_geom = face_type(geom); + constexpr int dim = dimension_of(geom); -TEST(DomainInterior, TriMesh) { - constexpr int p = 1; - constexpr int dim = 2; + mfem::Mesh mesh = generate_permuted_mesh(geom, permutation); - mfem::Mesh mesh = generate_permuted_mesh(mfem::Geometry::TRIANGLE, 1); + Domain interior_faces = InteriorFaces(mesh); + + // each one of these meshes should have two elements + // and a single "face" that separates them EXPECT_EQ(mesh.GetNE(), 2); - std::ofstream outfile("triangle.mesh"); - mesh.Print(outfile); - Domain interior_faces = InteriorFaces(mesh); - EXPECT_EQ(interior_faces.edge_ids_.size(), 1); - EXPECT_EQ(interior_faces.mfem_edge_ids_.size(), 1); + if (face_geom == mfem::Geometry::SEGMENT) { + EXPECT_EQ(interior_faces.edge_ids_.size(), 1); + EXPECT_EQ(interior_faces.mfem_edge_ids_.size(), 1); + } - auto H1_fec = std::make_unique(p, dim); - auto Hcurl_fec = std::make_unique(p, dim); - auto L2_fec = std::make_unique(p, dim, mfem::BasisType::GaussLobatto); + if (face_geom == mfem::Geometry::TRIANGLE) { + EXPECT_EQ(interior_faces.tri_ids_.size(), 1); + EXPECT_EQ(interior_faces.mfem_tri_ids_.size(), 1); + } + + if (face_geom == mfem::Geometry::SQUARE) { + EXPECT_EQ(interior_faces.quad_ids_.size(), 1); + EXPECT_EQ(interior_faces.mfem_quad_ids_.size(), 1); + } + + auto H1_fec = std::make_unique(polynomial_order, dim); + auto Hcurl_fec = std::make_unique(polynomial_order, dim); + auto L2_fec = std::make_unique(polynomial_order, dim, mfem::BasisType::GaussLobatto); auto H1_fes = std::make_unique(&mesh, H1_fec.get()); auto Hcurl_fes = std::make_unique(&mesh, Hcurl_fec.get()); @@ -257,53 +301,181 @@ TEST(DomainInterior, TriMesh) { mfem::GridFunction Hcurl_gf(Hcurl_fes.get()); mfem::GridFunction L2_gf(L2_fes.get()); - mfem::FunctionCoefficient scalar_func(scalar_func_2D); - mfem::VectorFunctionCoefficient vector_func(dim, vector_func_2D); - - H1_gf.ProjectCoefficient(scalar_func); - Hcurl_gf.ProjectCoefficient(vector_func); - H1_gf.ProjectCoefficient(scalar_func); - - auto H1_dofs = GetFaceDofs(H1_fes.get(), mfem::Geometry::SEGMENT, FaceType::INTERIOR); - std::cout << H1_dofs << std::endl; - - auto Hcurl_dofs = GetFaceDofs(Hcurl_fes.get(), mfem::Geometry::SEGMENT, FaceType::INTERIOR); - std::cout << Hcurl_dofs << std::endl; - - auto L2_dofs = GetFaceDofs(L2_fes.get(), mfem::Geometry::SEGMENT, FaceType::INTERIOR); - std::cout << L2_dofs << std::endl; - -} - -TEST(DomainInterior, QuadMesh) { - mfem::Mesh mesh = generate_permuted_mesh(mfem::Geometry::SQUARE, 0); - EXPECT_EQ(mesh.GetNE(), 2); - std::ofstream outfile("quad.mesh"); - mesh.Print(outfile); - - Domain interior_faces = InteriorFaces(mesh); - EXPECT_EQ(interior_faces.edge_ids_.size(), 1); - EXPECT_EQ(interior_faces.mfem_edge_ids_.size(), 1); -} - -TEST(DomainInterior, TetMesh) { - mfem::Mesh mesh = generate_permuted_mesh(mfem::Geometry::TETRAHEDRON, 0); - EXPECT_EQ(mesh.GetNE(), 2); - std::ofstream outfile("tet.mesh"); - mesh.Print(outfile); - - Domain interior_faces = InteriorFaces(mesh); - EXPECT_EQ(interior_faces.tri_ids_.size(), 1); - EXPECT_EQ(interior_faces.mfem_tri_ids_.size(), 1); + mfem::FunctionCoefficient sfunc(scalar_func); + mfem::VectorFunctionCoefficient vfunc(dim, vector_func); + + H1_gf.ProjectCoefficient(sfunc); + Hcurl_gf.ProjectCoefficient(vfunc); + L2_gf.ProjectCoefficient(sfunc); + + auto H1_dofs = GetFaceDofs(H1_fes.get(), face_geom, FaceType::INTERIOR); + auto Hcurl_dofs = GetFaceDofs(Hcurl_fes.get(), face_geom, FaceType::INTERIOR); + auto L2_dofs = GetFaceDofs(L2_fes.get(), face_geom, FaceType::INTERIOR); + + // verify that the dofs for the L2 faces are aligned properly + // + // sam: we should also check that the actual values match + // the scalar function evaluated at nodes, but I don't know + // how to get that info from mfem + int dofs_per_side = L2_dofs.shape()[1] / 2; + for (int i = 0; i < dofs_per_side; i++) { + int id1 = int(L2_dofs(0, i).index()); + int id2 = int(L2_dofs(0, i + dofs_per_side).index()); + EXPECT_NEAR(L2_gf[id1], L2_gf[id2], 5.0e-14); + } } -TEST(DomainInterior, HexMesh) { - mfem::Mesh mesh = generate_permuted_mesh(mfem::Geometry::CUBE, 0); - EXPECT_EQ(mesh.GetNE(), 2); - std::ofstream outfile("hex.mesh"); - mesh.Print(outfile); - - Domain interior_faces = InteriorFaces(mesh); - EXPECT_EQ(interior_faces.quad_ids_.size(), 1); - EXPECT_EQ(interior_faces.mfem_quad_ids_.size(), 1); -} +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +TEST(DomainInterior, TriMesh10) { parametrized_test(1, 0); } +TEST(DomainInterior, TriMesh11) { parametrized_test(1, 1); } +TEST(DomainInterior, TriMesh12) { parametrized_test(1, 2); } + +TEST(DomainInterior, TriMesh20) { parametrized_test(2, 0); } +TEST(DomainInterior, TriMesh21) { parametrized_test(2, 1); } +TEST(DomainInterior, TriMesh22) { parametrized_test(2, 2); } + +TEST(DomainInterior, TriMesh30) { parametrized_test(3, 0); } +TEST(DomainInterior, TriMesh31) { parametrized_test(3, 1); } +TEST(DomainInterior, TriMesh32) { parametrized_test(3, 2); } + +//////////////////////////////////////////////////////////////////////////////// + +TEST(DomainInterior, QuadMesh10) { parametrized_test(1, 0); } +TEST(DomainInterior, QuadMesh11) { parametrized_test(1, 1); } +TEST(DomainInterior, QuadMesh12) { parametrized_test(1, 2); } +TEST(DomainInterior, QuadMesh13) { parametrized_test(1, 3); } + +TEST(DomainInterior, QuadMesh20) { parametrized_test(2, 0); } +TEST(DomainInterior, QuadMesh21) { parametrized_test(2, 1); } +TEST(DomainInterior, QuadMesh22) { parametrized_test(2, 2); } +TEST(DomainInterior, QuadMesh23) { parametrized_test(2, 3); } + +TEST(DomainInterior, QuadMesh30) { parametrized_test(3, 0); } +TEST(DomainInterior, QuadMesh31) { parametrized_test(3, 1); } +TEST(DomainInterior, QuadMesh32) { parametrized_test(3, 2); } +TEST(DomainInterior, QuadMesh33) { parametrized_test(3, 3); } + +//////////////////////////////////////////////////////////////////////////////// + +TEST(DomainInterior, TetMesh100) { parametrized_test(1, 0); } +TEST(DomainInterior, TetMesh101) { parametrized_test(1, 1); } +TEST(DomainInterior, TetMesh102) { parametrized_test(1, 2); } +TEST(DomainInterior, TetMesh103) { parametrized_test(1, 3); } +TEST(DomainInterior, TetMesh104) { parametrized_test(1, 4); } +TEST(DomainInterior, TetMesh105) { parametrized_test(1, 5); } +TEST(DomainInterior, TetMesh106) { parametrized_test(1, 6); } +TEST(DomainInterior, TetMesh107) { parametrized_test(1, 7); } +TEST(DomainInterior, TetMesh108) { parametrized_test(1, 8); } +TEST(DomainInterior, TetMesh109) { parametrized_test(1, 9); } +TEST(DomainInterior, TetMesh110) { parametrized_test(1, 10); } +TEST(DomainInterior, TetMesh111) { parametrized_test(1, 11); } + +TEST(DomainInterior, TetMesh200) { parametrized_test(2, 0); } +TEST(DomainInterior, TetMesh201) { parametrized_test(2, 1); } +TEST(DomainInterior, TetMesh202) { parametrized_test(2, 2); } +TEST(DomainInterior, TetMesh203) { parametrized_test(2, 3); } +TEST(DomainInterior, TetMesh204) { parametrized_test(2, 4); } +TEST(DomainInterior, TetMesh205) { parametrized_test(2, 5); } +TEST(DomainInterior, TetMesh206) { parametrized_test(2, 6); } +TEST(DomainInterior, TetMesh207) { parametrized_test(2, 7); } +TEST(DomainInterior, TetMesh208) { parametrized_test(2, 8); } +TEST(DomainInterior, TetMesh209) { parametrized_test(2, 9); } +TEST(DomainInterior, TetMesh210) { parametrized_test(2, 10); } +TEST(DomainInterior, TetMesh211) { parametrized_test(2, 11); } + +TEST(DomainInterior, TetMesh300) { parametrized_test(3, 0); } +TEST(DomainInterior, TetMesh301) { parametrized_test(3, 1); } +TEST(DomainInterior, TetMesh302) { parametrized_test(3, 2); } +TEST(DomainInterior, TetMesh303) { parametrized_test(3, 3); } +TEST(DomainInterior, TetMesh304) { parametrized_test(3, 4); } +TEST(DomainInterior, TetMesh305) { parametrized_test(3, 5); } +TEST(DomainInterior, TetMesh306) { parametrized_test(3, 6); } +TEST(DomainInterior, TetMesh307) { parametrized_test(3, 7); } +TEST(DomainInterior, TetMesh308) { parametrized_test(3, 8); } +TEST(DomainInterior, TetMesh309) { parametrized_test(3, 9); } +TEST(DomainInterior, TetMesh310) { parametrized_test(3, 10); } +TEST(DomainInterior, TetMesh311) { parametrized_test(3, 11); } + +//////////////////////////////////////////////////////////////////////////////// + +TEST(DomainInterior, HexMesh100) { parametrized_test(1, 0); } +TEST(DomainInterior, HexMesh101) { parametrized_test(1, 1); } +TEST(DomainInterior, HexMesh102) { parametrized_test(1, 2); } +TEST(DomainInterior, HexMesh103) { parametrized_test(1, 3); } +TEST(DomainInterior, HexMesh104) { parametrized_test(1, 4); } +TEST(DomainInterior, HexMesh105) { parametrized_test(1, 5); } +TEST(DomainInterior, HexMesh106) { parametrized_test(1, 6); } +TEST(DomainInterior, HexMesh107) { parametrized_test(1, 7); } +TEST(DomainInterior, HexMesh108) { parametrized_test(1, 8); } +TEST(DomainInterior, HexMesh109) { parametrized_test(1, 9); } +TEST(DomainInterior, HexMesh110) { parametrized_test(1, 10); } +TEST(DomainInterior, HexMesh111) { parametrized_test(1, 11); } +TEST(DomainInterior, HexMesh112) { parametrized_test(1, 12); } +TEST(DomainInterior, HexMesh113) { parametrized_test(1, 13); } +TEST(DomainInterior, HexMesh114) { parametrized_test(1, 14); } +TEST(DomainInterior, HexMesh115) { parametrized_test(1, 15); } +TEST(DomainInterior, HexMesh116) { parametrized_test(1, 16); } +TEST(DomainInterior, HexMesh117) { parametrized_test(1, 17); } +TEST(DomainInterior, HexMesh118) { parametrized_test(1, 18); } +TEST(DomainInterior, HexMesh119) { parametrized_test(1, 19); } +TEST(DomainInterior, HexMesh120) { parametrized_test(1, 20); } +TEST(DomainInterior, HexMesh121) { parametrized_test(1, 21); } +TEST(DomainInterior, HexMesh122) { parametrized_test(1, 22); } +TEST(DomainInterior, HexMesh123) { parametrized_test(1, 23); } + +TEST(DomainInterior, HexMesh200) { parametrized_test(2, 0); } +TEST(DomainInterior, HexMesh201) { parametrized_test(2, 1); } +TEST(DomainInterior, HexMesh202) { parametrized_test(2, 2); } +TEST(DomainInterior, HexMesh203) { parametrized_test(2, 3); } +TEST(DomainInterior, HexMesh204) { parametrized_test(2, 4); } +TEST(DomainInterior, HexMesh205) { parametrized_test(2, 5); } +TEST(DomainInterior, HexMesh206) { parametrized_test(2, 6); } +TEST(DomainInterior, HexMesh207) { parametrized_test(2, 7); } +TEST(DomainInterior, HexMesh208) { parametrized_test(2, 8); } +TEST(DomainInterior, HexMesh209) { parametrized_test(2, 9); } +TEST(DomainInterior, HexMesh210) { parametrized_test(2, 10); } +TEST(DomainInterior, HexMesh211) { parametrized_test(2, 11); } +TEST(DomainInterior, HexMesh212) { parametrized_test(2, 12); } +TEST(DomainInterior, HexMesh213) { parametrized_test(2, 13); } +TEST(DomainInterior, HexMesh214) { parametrized_test(2, 14); } +TEST(DomainInterior, HexMesh215) { parametrized_test(2, 15); } +TEST(DomainInterior, HexMesh216) { parametrized_test(2, 16); } +TEST(DomainInterior, HexMesh217) { parametrized_test(2, 17); } +TEST(DomainInterior, HexMesh218) { parametrized_test(2, 18); } +TEST(DomainInterior, HexMesh219) { parametrized_test(2, 19); } +TEST(DomainInterior, HexMesh220) { parametrized_test(2, 20); } +TEST(DomainInterior, HexMesh221) { parametrized_test(2, 21); } +TEST(DomainInterior, HexMesh222) { parametrized_test(2, 22); } +TEST(DomainInterior, HexMesh223) { parametrized_test(2, 23); } + +TEST(DomainInterior, HexMesh300) { parametrized_test(3, 0); } +TEST(DomainInterior, HexMesh301) { parametrized_test(3, 1); } +TEST(DomainInterior, HexMesh302) { parametrized_test(3, 2); } +TEST(DomainInterior, HexMesh303) { parametrized_test(3, 3); } +TEST(DomainInterior, HexMesh304) { parametrized_test(3, 4); } +TEST(DomainInterior, HexMesh305) { parametrized_test(3, 5); } +TEST(DomainInterior, HexMesh306) { parametrized_test(3, 6); } +TEST(DomainInterior, HexMesh307) { parametrized_test(3, 7); } +TEST(DomainInterior, HexMesh308) { parametrized_test(3, 8); } +TEST(DomainInterior, HexMesh309) { parametrized_test(3, 9); } +TEST(DomainInterior, HexMesh310) { parametrized_test(3, 10); } +TEST(DomainInterior, HexMesh311) { parametrized_test(3, 11); } +TEST(DomainInterior, HexMesh312) { parametrized_test(3, 12); } +TEST(DomainInterior, HexMesh313) { parametrized_test(3, 13); } +TEST(DomainInterior, HexMesh314) { parametrized_test(3, 14); } +TEST(DomainInterior, HexMesh315) { parametrized_test(3, 15); } +TEST(DomainInterior, HexMesh316) { parametrized_test(3, 16); } +TEST(DomainInterior, HexMesh317) { parametrized_test(3, 17); } +TEST(DomainInterior, HexMesh318) { parametrized_test(3, 18); } +TEST(DomainInterior, HexMesh319) { parametrized_test(3, 19); } +TEST(DomainInterior, HexMesh320) { parametrized_test(3, 20); } +TEST(DomainInterior, HexMesh321) { parametrized_test(3, 21); } +TEST(DomainInterior, HexMesh322) { parametrized_test(3, 22); } +TEST(DomainInterior, HexMesh323) { parametrized_test(3, 23); } + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// From f1de1c00a876e32ddb3054308fc3a2baf7d223bc Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Mon, 26 Aug 2024 06:43:19 -0700 Subject: [PATCH 08/92] plumbing for the new integral type --- src/serac/numerics/functional/functional.hpp | 13 + src/serac/numerics/functional/integral.hpp | 88 +++++ .../interior_face_integral_kernels.hpp | 364 ++++++++++++++++++ .../numerics/functional/tests/CMakeLists.txt | 1 + .../tests/dg_restriction_operators.cpp | 8 +- .../functional/tests/functional_basic_dg.cpp | 121 ++++++ 6 files changed, 591 insertions(+), 4 deletions(-) create mode 100644 src/serac/numerics/functional/interior_face_integral_kernels.hpp create mode 100644 src/serac/numerics/functional/tests/functional_basic_dg.cpp diff --git a/src/serac/numerics/functional/functional.hpp b/src/serac/numerics/functional/functional.hpp index 9cd91ed7d6..44a8688e04 100644 --- a/src/serac/numerics/functional/functional.hpp +++ b/src/serac/numerics/functional/functional.hpp @@ -351,6 +351,19 @@ class Functional { integrals_.push_back(MakeBoundaryIntegral(domain, integrand, std::vector{args...})); } + /** + * @brief TODO + */ + template + void AddInteriorFaceIntegral(Dimension, DependsOn, const Integrand& integrand, mfem::Mesh& mesh) + { + check_for_missing_nodal_gridfunc(mesh); + + using signature = test(decltype(serac::type(trial_spaces))...); + integrals_.push_back( + MakeInteriorFaceIntegral(InteriorFaces(mesh), integrand, std::vector{args...})); + } + /** * @brief Adds an area integral, i.e., over 2D elements in R^2 space * @tparam lambda the type of the integrand functor: must implement operator() with an appropriate function signature diff --git a/src/serac/numerics/functional/integral.hpp b/src/serac/numerics/functional/integral.hpp index 337118a7d8..f0ad546321 100644 --- a/src/serac/numerics/functional/integral.hpp +++ b/src/serac/numerics/functional/integral.hpp @@ -16,6 +16,7 @@ #include "serac/numerics/functional/function_signature.hpp" #include "serac/numerics/functional/domain_integral_kernels.hpp" #include "serac/numerics/functional/boundary_integral_kernels.hpp" +#include "serac/numerics/functional/interior_face_integral_kernels.hpp" #include "serac/numerics/functional/differentiate_wrt.hpp" namespace serac { @@ -346,4 +347,91 @@ Integral MakeBoundaryIntegral(const Domain& domain, const lambda_type& qf, std:: return integral; } +/** + * @brief function to generate kernels held by an `Integral` object of type "InteriorFaceDomain", with a specific element + * type + * + * @tparam geom the element geometry + * @tparam Q a parameter that controls the number of quadrature points + * @tparam test the kind of test functions used in the integral + * @tparam trials the trial space(s) of the integral's inputs + * @tparam lambda_type a callable object that implements the q-function concept + * @param s an object used to pass around test/trial information + * @param integral the Integral object to initialize + * @param qf the quadrature function + */ +template +void generate_interior_face_kernels(FunctionSignature s, Integral& integral, const lambda_type& qf) +{ + integral.geometric_factors_[geom] = GeometricFactors(integral.domain_, Q, geom, FaceType::BOUNDARY); + GeometricFactors& gf = integral.geometric_factors_[geom]; + if (gf.num_elements == 0) return; + + const double* positions = gf.X.Read(); + const double* jacobians = gf.J.Read(); + const uint32_t num_elements = uint32_t(gf.num_elements); + const uint32_t qpts_per_element = num_quadrature_points(geom, Q); + const int* elements = &gf.elements[0]; + + std::shared_ptr dummy_derivatives; + integral.evaluation_[geom] = interior_face_integral::evaluation_kernel( + s, qf, positions, jacobians, dummy_derivatives, elements, num_elements); + + constexpr std::size_t num_args = s.num_args; + [[maybe_unused]] static constexpr int dim = dimension_of(geom); + for_constexpr([&](auto index) { + // allocate memory for the derivatives of the q-function at each quadrature point + // + // Note: ptrs' lifetime is managed in an unusual way! It is captured by-value in the + // action_of_gradient functor below to augment the reference count, and extend its lifetime to match + // that of the boundaryIntegral that allocated it. + using derivative_type = decltype(interior_face_integral::get_derivative_type(qf)); + auto ptr = accelerator::make_shared_array(num_elements * qpts_per_element); + + integral.evaluation_with_AD_[index][geom] = + interior_face_integral::evaluation_kernel(s, qf, positions, jacobians, ptr, elements, num_elements); + + integral.jvp_[index][geom] = + interior_face_integral::jacobian_vector_product_kernel(s, ptr, elements, num_elements); + integral.element_gradient_[index][geom] = + interior_face_integral::element_gradient_kernel(s, ptr, elements, num_elements); + }); +} + +/** + * @brief function to generate kernels held by an `Integral` object of type "Boundary", for all element types + * + * @tparam s a function signature type containing test/trial space informationa type containing a function signature + * @tparam Q a parameter that controls the number of quadrature points + * @tparam dim the dimension of the domain + * @tparam lambda_type a callable object that implements the q-function concept + * @param domain the domain of integration + * @param qf the quadrature function + * @param argument_indices the indices of trial space arguments used in the Integral + * @return Integral the initialized `Integral` object + * + * @note this function is not meant to be called by users + */ +template +Integral MakeInteriorFaceIntegral(const Domain& domain, const lambda_type& qf, std::vector argument_indices) +{ + FunctionSignature signature; + + SLIC_ERROR_IF(domain.type_ != Domain::Type::InteriorFaces, + "Error: trying to evaluate a boundary integral over a non-boundary domain of integration"); + + Integral integral(domain, argument_indices); + + if constexpr (dim == 1) { + generate_interior_face_kernels(signature, integral, qf); + } + + if constexpr (dim == 2) { + generate_interior_face_kernels(signature, integral, qf); + generate_interior_face_kernels(signature, integral, qf); + } + + return integral; +} + } // namespace serac diff --git a/src/serac/numerics/functional/interior_face_integral_kernels.hpp b/src/serac/numerics/functional/interior_face_integral_kernels.hpp new file mode 100644 index 0000000000..59417d654f --- /dev/null +++ b/src/serac/numerics/functional/interior_face_integral_kernels.hpp @@ -0,0 +1,364 @@ +// Copyright (c) 2019-2024, Lawrence Livermore National Security, LLC and +// other Serac Project Developers. See the top-level LICENSE file for +// details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#pragma once + +#include + +#include "serac/serac_config.hpp" +#include "serac/numerics/functional/quadrature_data.hpp" +#include "serac/numerics/functional/differentiate_wrt.hpp" + +namespace serac { + +namespace interior_face_integral { + +/** + * @tparam space the user-specified trial space + * @tparam dimension describes whether the problem is 1D, 2D, or 3D + * + * @brief a struct used to encode what type of arguments will be passed to a domain integral q-function, for the given + * trial space + */ +template +struct QFunctionArgument; + +/// @overload +template +struct QFunctionArgument, Dimension<1>> { + using type = serac::tuple; ///< what will be passed to the q-function +}; + +/// @overload +template +struct QFunctionArgument, Dimension> { + using type = serac::tuple>; ///< what will be passed to the q-function +}; + +/// @overload +template +struct QFunctionArgument, Dimension<1>> { + using type = serac::tuple, tensor>; ///< what will be passed to the q-function +}; + +/// @overload +template +struct QFunctionArgument, Dimension> { + using type = serac::tuple, tensor>; ///< what will be passed to the q-function +}; + +/// @overload +template +struct QFunctionArgument, Dimension<1>> { + using type = serac::tuple; ///< what will be passed to the q-function +}; + +/// @overload +template +struct QFunctionArgument, Dimension> { + using type = serac::tuple>; ///< what will be passed to the q-function +}; + +/// @overload +template +struct QFunctionArgument, Dimension<1>> { + using type = serac::tuple, tensor>; ///< what will be passed to the q-function +}; + +/// @overload +template +struct QFunctionArgument, Dimension> { + using type = serac::tuple, tensor>; ///< what will be passed to the q-function +}; + +/// @overload +SERAC_SUPPRESS_NVCC_HOSTDEVICE_WARNING +template +SERAC_HOST_DEVICE auto apply_qf_helper(const lambda& qf, double t, const tensor& x_q, const T& arg_tuple, + std::integer_sequence) +{ + tensor J_q{}; + return qf(t, serac::tuple{x_q, J_q}, serac::get(arg_tuple)...); +} + +/// @overload +SERAC_SUPPRESS_NVCC_HOSTDEVICE_WARNING +template +SERAC_HOST_DEVICE auto apply_qf_helper(const lambda& qf, double t, const tensor& x_q, const T& arg_tuple, + std::integer_sequence) +{ + constexpr int dim = 3; + tensor J_q{}; + return qf(t, serac::tuple{x_q, J_q}, serac::get(arg_tuple)...); +} + +/// @overload +template +SERAC_HOST_DEVICE auto apply_qf(const lambda& qf, double t, const coords_type& x_q, const serac::tuple& arg_tuple) +{ + return apply_qf_helper(qf, t, x_q, arg_tuple, std::make_integer_sequence(sizeof...(T))>{}); +} + +template +auto get_derivative_type(lambda qf) +{ + using qf_arguments = serac::tuple>::type...>; + return tuple{get_gradient(apply_qf(qf, double{}, tensor{}, make_dual_wrt(qf_arguments{}))), + zero{}}; +}; + +template +SERAC_HOST_DEVICE auto batch_apply_qf(lambda qf, double t, const tensor& positions, + const tensor& jacobians, const T&... inputs) +{ + constexpr int dim = 2; + using first_arg_t = serac::tuple, tensor>; + using return_type = decltype(qf(double{}, first_arg_t{}, T{}[0]...)); + tensor, n> outputs{}; + for (int i = 0; i < n; i++) { + tensor x_q; + tensor J_q; + for (int j = 0; j < dim; j++) { + x_q[j] = positions(j, i); + J_q[j] = jacobians(0, j, i); + } + double scale = norm(cross(J_q)); + + get<0>(outputs[i]) = qf(t, serac::tuple{x_q, J_q}, inputs[i]...) * scale; + } + return outputs; +} + +template +SERAC_HOST_DEVICE auto batch_apply_qf(lambda qf, double t, const tensor& positions, + const tensor& jacobians, const T&... inputs) +{ + constexpr int dim = 3; + using first_arg_t = serac::tuple, tensor>; + using return_type = decltype(qf(double{}, first_arg_t{}, T{}[0]...)); + tensor, n> outputs{}; + for (int i = 0; i < n; i++) { + tensor x_q; + tensor J_q; + for (int j = 0; j < dim; j++) { + x_q[j] = positions(j, i); + for (int k = 0; k < dim - 1; k++) { + J_q(j, k) = jacobians(k, j, i); + } + } + double scale = norm(cross(J_q)); + + get<0>(outputs[i]) = qf(t, serac::tuple{x_q, J_q}, inputs[i]...) * scale; + } + return outputs; +} + +/// @trial_elements the element type for each trial space +template +void evaluation_kernel_impl(trial_element_type trial_elements, test_element, double t, + const std::vector& inputs, double* outputs, const double* positions, + const double* jacobians, lambda_type qf, [[maybe_unused]] derivative_type* qf_derivatives, + const int* elements, uint32_t num_elements, camp::int_seq) +{ + // mfem provides this information as opaque arrays of doubles, + // so we reinterpret the pointer with + constexpr int dim = dimension_of(geom) + 1; + constexpr int nqp = num_quadrature_points(geom, Q); + auto J = reinterpret_cast*>(jacobians); + auto x = reinterpret_cast*>(positions); + auto r = reinterpret_cast(outputs); + static constexpr TensorProductQuadratureRule rule{}; + + static constexpr int qpts_per_elem = num_quadrature_points(geom, Q); + + [[maybe_unused]] tuple u = { + reinterpret_cast(trial_elements))::dof_type*>(inputs[indices])...}; + + // for each element in the domain + for (uint32_t e = 0; e < num_elements; e++) { + // load the jacobians and positions for each quadrature point in this element + auto J_e = J[e]; + auto x_e = x[e]; + + // batch-calculate values / derivatives of each trial space, at each quadrature point + [[maybe_unused]] tuple qf_inputs = {promote_each_to_dual_when( + get(trial_elements).interpolate(get(u)[elements[e]], rule))...}; + + // (batch) evalute the q-function at each quadrature point + auto qf_outputs = batch_apply_qf(qf, t, x_e, J_e, get(qf_inputs)...); + + // write out the q-function derivatives after applying the + // physical_to_parent transformation, so that those transformations + // won't need to be applied in the action_of_gradient and element_gradient kernels + if constexpr (differentiation_index != serac::NO_DIFFERENTIATION) { + for (int q = 0; q < leading_dimension(qf_outputs); q++) { + qf_derivatives[e * qpts_per_elem + uint32_t(q)] = get_gradient(qf_outputs[q]); + } + } + + // (batch) integrate the material response against the test-space basis functions + test_element::integrate(get_value(qf_outputs), rule, &r[elements[e]]); + } +} + +//clang-format off +template +SERAC_HOST_DEVICE auto chain_rule(const S& dfdx, const T& dx) +{ + return serac::chain_rule(serac::get<0>(serac::get<0>(dfdx)), serac::get<0>(dx)) + + serac::chain_rule(serac::get<1>(serac::get<0>(dfdx)), serac::get<1>(dx)); +} +//clang-format on + +template +SERAC_HOST_DEVICE auto batch_apply_chain_rule(derivative_type* qf_derivatives, const tensor& inputs) +{ + using return_type = decltype(chain_rule(derivative_type{}, T{})); + tensor, n> outputs{}; + for (int i = 0; i < n; i++) { + get<0>(outputs[i]) = chain_rule(qf_derivatives[i], inputs[i]); + } + return outputs; +} + +/** + * @brief The base kernel template used to create create custom directional derivative + * kernels associated with finite element calculations + * + * @tparam test The type of the test function space + * @tparam trial The type of the trial function space + * The above spaces can be any combination of {H1, Hcurl, Hdiv (TODO), L2 (TODO)} + * + * Template parameters other than the test and trial spaces are used for customization + optimization + * and are erased through the @p std::function members of @p BoundaryIntegral + * @tparam g The shape of the element (only quadrilateral and hexahedron are supported at present) + * @tparam Q parameter describing number of quadrature points (see num_quadrature_points() function for more details) + * @tparam derivatives_type Type representing the derivative of the q-function w.r.t. its input arguments + * + * @note lambda does not appear as a template argument, as the directional derivative is + * inherently just a linear transformation + * + * @param[in] dU The full set of per-element DOF values (primary input) + * @param[inout] dR The full set of per-element residuals (primary output) + * @param[in] derivatives_ptr The address at which derivatives of the q-function with + * respect to its arguments are stored + * @param[in] J_ The Jacobians of the element transformations at all quadrature points + * @see mfem::GeometricFactors + * @param[in] num_elements The number of elements in the mesh + */ +template +void action_of_gradient_kernel(const double* dU, double* dR, derivatives_type* qf_derivatives, const int* elements, + std::size_t num_elements) +{ + using test_element = finite_element; + using trial_element = finite_element; + + // mfem provides this information in 1D arrays, so we reshape it + // into strided multidimensional arrays before using + constexpr int nqp = num_quadrature_points(geom, Q); + auto du = reinterpret_cast(dU); + auto dr = reinterpret_cast(dR); + static constexpr TensorProductQuadratureRule rule{}; + + // for each element in the domain + for (uint32_t e = 0; e < num_elements; e++) { + // (batch) interpolate each quadrature point's value + auto qf_inputs = trial_element::interpolate(du[elements[e]], rule); + + // (batch) evalute the q-function at each quadrature point + auto qf_outputs = batch_apply_chain_rule(qf_derivatives + e * nqp, qf_inputs); + + // (batch) integrate the material response against the test-space basis functions + test_element::integrate(qf_outputs, rule, &dr[elements[e]]); + } +} + +/** + * @brief The base kernel template used to compute tangent element entries that can be assembled + * into a tangent matrix + * + * @tparam test The type of the test function space + * @tparam trial The type of the trial function space + * The above spaces can be any combination of {H1, Hcurl, Hdiv (TODO), L2 (TODO), QOI} + * + * Template parameters other than the test and trial spaces are used for customization + optimization + * and are erased through the @p std::function members of @p Integral + * @tparam g The shape of the element (only quadrilateral and hexahedron are supported at present) + * @tparam Q parameter describing number of quadrature points (see num_quadrature_points() function for more details) + * @tparam derivatives_type Type representing the derivative of the q-function w.r.t. its input arguments + * + * + * @param[inout] dk 3-dimensional array storing the element gradient matrices + * @param[in] derivatives_ptr pointer to data describing the derivatives of the q-function with respect to its arguments + * @param[in] J_ The Jacobians of the element transformations at all quadrature points + * @see mfem::GeometricFactors + * @param[in] num_elements The number of elements in the mesh + */ +template +void element_gradient_kernel(ExecArrayView dK, derivatives_type* qf_derivatives, + const int* elements, std::size_t num_elements) +{ + using test_element = finite_element; + using trial_element = finite_element; + + constexpr int nquad = num_quadrature_points(g, Q); + + static constexpr TensorProductQuadratureRule rule{}; + + // for each element in the domain + for (uint32_t e = 0; e < num_elements; e++) { + auto* output_ptr = reinterpret_cast(&dK(elements[e], 0, 0)); + + tensor derivatives{}; + for (int q = 0; q < nquad; q++) { + derivatives(q) = qf_derivatives[e * nquad + uint32_t(q)]; + } + + for (int J = 0; J < trial_element::ndof; J++) { + auto source_and_flux = trial_element::batch_apply_shape_fn(J, derivatives, rule); + test_element::integrate(source_and_flux, rule, output_ptr + J, trial_element::ndof); + } + } +} + +template +auto evaluation_kernel(signature s, lambda_type qf, const double* positions, const double* jacobians, + std::shared_ptr qf_derivatives, const int* elements, uint32_t num_elements) +{ + auto trial_elements = trial_elements_tuple(s); + auto test_element = get_test_element(s); + return [=](double time, const std::vector& inputs, double* outputs, bool /* update state */) { + evaluation_kernel_impl(trial_elements, test_element, time, inputs, outputs, positions, jacobians, qf, + qf_derivatives.get(), elements, num_elements, s.index_seq); + }; +} + +template +std::function jacobian_vector_product_kernel( + signature, std::shared_ptr qf_derivatives, const int* elements, uint32_t num_elements) +{ + return [=](const double* du, double* dr) { + using test_space = typename signature::return_type; + using trial_space = typename std::tuple_element::type; + action_of_gradient_kernel(du, dr, qf_derivatives.get(), elements, num_elements); + }; +} + +template +std::function)> element_gradient_kernel( + signature, std::shared_ptr qf_derivatives, const int* elements, uint32_t num_elements) +{ + return [=](ExecArrayView K_elem) { + using test_space = typename signature::return_type; + using trial_space = typename std::tuple_element::type; + element_gradient_kernel(K_elem, qf_derivatives.get(), elements, num_elements); + }; +} + +} // namespace boundary_integral + +} // namespace serac diff --git a/src/serac/numerics/functional/tests/CMakeLists.txt b/src/serac/numerics/functional/tests/CMakeLists.txt index 65409c8d9c..c0286ed3ae 100644 --- a/src/serac/numerics/functional/tests/CMakeLists.txt +++ b/src/serac/numerics/functional/tests/CMakeLists.txt @@ -39,6 +39,7 @@ set(functional_parallel_test_sources functional_with_domain.cpp functional_basic_h1_scalar.cpp functional_basic_h1_vector.cpp + functional_basic_dg.cpp functional_multiphysics.cpp functional_qoi.cpp functional_nonlinear.cpp diff --git a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp index 2c2d175a1d..25984761b9 100644 --- a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp +++ b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp @@ -313,16 +313,16 @@ void parametrized_test(int polynomial_order, int permutation) { auto L2_dofs = GetFaceDofs(L2_fes.get(), face_geom, FaceType::INTERIOR); // verify that the dofs for the L2 faces are aligned properly - // - // sam: we should also check that the actual values match - // the scalar function evaluated at nodes, but I don't know - // how to get that info from mfem int dofs_per_side = L2_dofs.shape()[1] / 2; for (int i = 0; i < dofs_per_side; i++) { int id1 = int(L2_dofs(0, i).index()); int id2 = int(L2_dofs(0, i + dofs_per_side).index()); EXPECT_NEAR(L2_gf[id1], L2_gf[id2], 5.0e-14); } + + // TODO: check that the actual values match their respective functions + // evaluated directly at the nodes (for H1, Hcurl, and L2 gfs) + } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/serac/numerics/functional/tests/functional_basic_dg.cpp b/src/serac/numerics/functional/tests/functional_basic_dg.cpp new file mode 100644 index 0000000000..593d985c53 --- /dev/null +++ b/src/serac/numerics/functional/tests/functional_basic_dg.cpp @@ -0,0 +1,121 @@ +// Copyright (c) 2019-2024, Lawrence Livermore National Security, LLC and +// other Serac Project Developers. See the top-level LICENSE file for +// details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include +#include + +#include "mfem.hpp" + +#include + +#include "axom/slic/core/SimpleLogger.hpp" +#include "serac/infrastructure/input.hpp" +#include "serac/serac_config.hpp" +#include "serac/mesh/mesh_utils_base.hpp" +#include "serac/numerics/stdfunction_operator.hpp" +#include "serac/numerics/functional/functional.hpp" +#include "serac/numerics/functional/tensor.hpp" + +#include "serac/numerics/functional/tests/check_gradient.hpp" + +using namespace serac; +using namespace serac::profiling; + +template +void L2_test_2D() +{ + constexpr int dim = 2; + using test_space = L2; + using trial_space = L2; + + std::string meshfile = SERAC_REPO_DIR "/data/meshes/patch2D.mesh"; + + auto mesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 1); + + auto fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); + mfem::ParFiniteElementSpace fespace(mesh.get(), &fec); + + mfem::Vector U(fespace.TrueVSize()); + U.Randomize(); + + // Construct the new functional object using the specified test and trial spaces + Functional residual(&fespace, {&fespace}); + + residual.AddInteriorFaceIntegral( + Dimension{}, DependsOn<0>{}, + [=](double /*t*/, auto /*x*/, auto velocity) { + auto [u_avg, u_diff] = velocity; + return u_avg; + }, + *mesh); + + double t = 0.0; + check_gradient(residual, t, U); + +} + +TEST(basic, L2_test_2D_linear) { L2_test_2D<1>(); } + +#if 0 +template +void hcurl_test_3D() +{ + constexpr int dim = 3; + + std::string meshfile = SERAC_REPO_DIR "/data/meshes/patch3D.mesh"; + + auto mesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 1); + + // Create standard MFEM bilinear and linear forms on H1 + auto fec = mfem::ND_FECollection(p, dim); + mfem::ParFiniteElementSpace fespace(mesh.get(), &fec); + + mfem::Vector U(fespace.TrueVSize()); + U.Randomize(); + + // Define the types for the test and trial spaces using the function arguments + using test_space = Hcurl

; + using trial_space = Hcurl

; + + // Construct the new functional object using the known test and trial spaces + Functional residual(&fespace, {&fespace}); + + residual.AddInteriorFaceIntegral( + Dimension{}, DependsOn<0>{}, + [=](double /*t*/, auto /*x*/, auto vector_potential) { + auto [A, curl_A] = vector_potential; + auto source = dot(d00, A) + dot(d01, curl_A); + auto flux = dot(d10, A) + dot(d11, curl_A); + return serac::tuple{source, flux}; + }, + *mesh); + + check_gradient(residual, t, U); +} + +TEST(basic, hcurl_test_3D_linear) { hcurl_test_3D<1>(); } + +#endif + + +int main(int argc, char* argv[]) +{ + int num_procs, myid; + + ::testing::InitGoogleTest(&argc, argv); + + MPI_Init(&argc, &argv); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + MPI_Comm_rank(MPI_COMM_WORLD, &myid); + + axom::slic::SimpleLogger logger; + + int result = RUN_ALL_TESTS(); + + MPI_Finalize(); + + return result; +} From 53e88be911480c5ff07162c8a1849e6727542a61 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Fri, 30 Aug 2024 11:45:51 -0700 Subject: [PATCH 09/92] more plumbing for the new integral type --- .../functional/detail/quadrilateral_H1.inl | 1 + .../functional/detail/quadrilateral_Hcurl.inl | 1 + .../functional/detail/quadrilateral_L2.inl | 32 +++++++++++ .../numerics/functional/detail/segment_H1.inl | 1 + .../functional/detail/segment_Hcurl.inl | 3 ++ .../numerics/functional/detail/segment_L2.inl | 53 +++++++++++++++++++ .../functional/detail/triangle_H1.inl | 1 + .../functional/detail/triangle_L2.inl | 32 +++++++++++ src/serac/numerics/functional/domain.hpp | 2 +- .../functional/element_restriction.cpp | 4 +- src/serac/numerics/functional/functional.hpp | 18 +++++-- .../interior_face_integral_kernels.hpp | 39 +++++--------- .../functional/tests/functional_basic_dg.cpp | 25 +++++++-- 13 files changed, 175 insertions(+), 37 deletions(-) diff --git a/src/serac/numerics/functional/detail/quadrilateral_H1.inl b/src/serac/numerics/functional/detail/quadrilateral_H1.inl index 312f8e9c78..9e58f18215 100644 --- a/src/serac/numerics/functional/detail/quadrilateral_H1.inl +++ b/src/serac/numerics/functional/detail/quadrilateral_H1.inl @@ -33,6 +33,7 @@ struct finite_element > { typename std::conditional, tensor >::type; using dof_type = tensor; + using dof_type_if = dof_type; using value_type = typename std::conditional >::type; using derivative_type = diff --git a/src/serac/numerics/functional/detail/quadrilateral_Hcurl.inl b/src/serac/numerics/functional/detail/quadrilateral_Hcurl.inl index 815b5b72e3..fb28c55784 100644 --- a/src/serac/numerics/functional/detail/quadrilateral_Hcurl.inl +++ b/src/serac/numerics/functional/detail/quadrilateral_Hcurl.inl @@ -43,6 +43,7 @@ struct finite_element > { tensor x; tensor y; }; + using dof_type_if = dof_type; template using cpu_batched_values_type = tensor, q, q>; diff --git a/src/serac/numerics/functional/detail/quadrilateral_L2.inl b/src/serac/numerics/functional/detail/quadrilateral_L2.inl index 48ba045f01..356d072d17 100644 --- a/src/serac/numerics/functional/detail/quadrilateral_L2.inl +++ b/src/serac/numerics/functional/detail/quadrilateral_L2.inl @@ -32,6 +32,7 @@ struct finite_element > { typename std::conditional, tensor >::type; using dof_type = tensor; + using dof_type_if = tensor; using value_type = typename std::conditional >::type; using derivative_type = @@ -240,6 +241,37 @@ struct finite_element > { return output.one_dimensional; } + // overload for two-sided interior face kernels + template + SERAC_HOST_DEVICE static auto interpolate(const dof_type_if& X, const TensorProductQuadratureRule&) + { + static constexpr bool apply_weights = false; + static constexpr auto B = calculate_B(); + static constexpr auto G = calculate_G(); + + tensor< tuple< tensor, tensor >, q * q> output; + +#if 0 + tensor value{}; + + // apply the shape functions + for (int i = 0; i < c; i++) { + auto A0 = contract<1, 1>(X[i], B); + value(i) = contract<0, 1>(A0, B); + } + + for (int qy = 0; qy < q; qy++) { + for (int qx = 0; qx < q; qx++) { + for (int i = 0; i < c; i++) { + get(output.two_dimensional(qy, qx))[i] = value(i, qy, qx); + } + } + } +#endif + + return output; + } + // source can be one of: {zero, double, tensor, tensor} // flux can be one of: {zero, tensor, tensor, tensor, // tensor} diff --git a/src/serac/numerics/functional/detail/segment_H1.inl b/src/serac/numerics/functional/detail/segment_H1.inl index 11fe30bc2b..e9a41a28da 100644 --- a/src/serac/numerics/functional/detail/segment_H1.inl +++ b/src/serac/numerics/functional/detail/segment_H1.inl @@ -29,6 +29,7 @@ struct finite_element > { static constexpr int SOURCE = 0, FLUX = 1; using dof_type = tensor; + using dof_type_if = dof_type; using value_type = typename std::conditional >::type; using derivative_type = value_type; diff --git a/src/serac/numerics/functional/detail/segment_Hcurl.inl b/src/serac/numerics/functional/detail/segment_Hcurl.inl index 98677f6843..8fab7e2302 100644 --- a/src/serac/numerics/functional/detail/segment_Hcurl.inl +++ b/src/serac/numerics/functional/detail/segment_Hcurl.inl @@ -26,6 +26,9 @@ struct finite_element > { static constexpr int dim = 1; static constexpr int ndof = (p + 1); + using dof_type = tensor; + using dof_type_if = dof_type; + SERAC_HOST_DEVICE static constexpr tensor shape_functions(double xi) { return GaussLegendreInterpolation(xi); diff --git a/src/serac/numerics/functional/detail/segment_L2.inl b/src/serac/numerics/functional/detail/segment_L2.inl index d146037938..8abdbc87bb 100644 --- a/src/serac/numerics/functional/detail/segment_L2.inl +++ b/src/serac/numerics/functional/detail/segment_L2.inl @@ -29,6 +29,7 @@ struct finite_element > { static constexpr int SOURCE = 0, FLUX = 1; using dof_type = tensor; + using dof_type_if = tensor; using value_type = typename std::conditional >::type; using derivative_type = value_type; @@ -120,6 +121,32 @@ struct finite_element > { return output; } + template + SERAC_HOST_DEVICE static auto interpolate(const dof_type_if& X, const TensorProductQuadratureRule&) + { + static constexpr bool apply_weights = false; + static constexpr auto BT = transpose(calculate_B()); + + tensor values{}; + + tensor< tuple< tensor, tensor >, q> output; + + // apply the shape functions + for (int i = 0; i < c; i++) { + values = dot(X[i][0], BT); + for (int qx = 0; qx < q; qx++) { + get<0>(output[qx])[i] = values[qx]; + } + + values = dot(X[i][1], BT); + for (int qx = 0; qx < q; qx++) { + get<1>(output[qx])[i] = values[qx]; + } + } + + return output; + } + template SERAC_HOST_DEVICE static auto interpolate(const dof_type& X, const TensorProductQuadratureRule&) { @@ -190,5 +217,31 @@ struct finite_element > { } } } + + template + SERAC_HOST_DEVICE static void integrate(const tensor, tensor >, q>& qf_output, + const TensorProductQuadratureRule&, + dof_type_if * element_residual, + [[maybe_unused]] int step = 1) + { + using buffer_type = tensor< double, q>; + + static constexpr bool apply_weights = true; + static constexpr auto B = calculate_B(); + + for (int i = 0; i < c; i++) { + buffer_type source_0; + buffer_type source_1; + + for (int qx = 0; qx < q; qx++) { + source_0(qx) = get<0>(qf_output[qx])[i]; + source_1(qx) = get<1>(qf_output[qx])[i]; + } + + element_residual[0](i, 0) += dot(source_0, B); + element_residual[0](i, 1) += dot(source_1, B); + } + } + }; /// @endcond diff --git a/src/serac/numerics/functional/detail/triangle_H1.inl b/src/serac/numerics/functional/detail/triangle_H1.inl index 37c1600fa0..0e63afc533 100644 --- a/src/serac/numerics/functional/detail/triangle_H1.inl +++ b/src/serac/numerics/functional/detail/triangle_H1.inl @@ -36,6 +36,7 @@ struct finite_element > { typename std::conditional, tensor >::type; using dof_type = tensor; + using dof_type_if = dof_type; using value_type = typename std::conditional >::type; using derivative_type = diff --git a/src/serac/numerics/functional/detail/triangle_L2.inl b/src/serac/numerics/functional/detail/triangle_L2.inl index 2030008daa..90f7070a07 100644 --- a/src/serac/numerics/functional/detail/triangle_L2.inl +++ b/src/serac/numerics/functional/detail/triangle_L2.inl @@ -34,6 +34,7 @@ struct finite_element > { typename std::conditional, tensor >::type; using dof_type = tensor; + using dof_type_if = tensor; using value_type = typename std::conditional >::type; using derivative_type = @@ -298,6 +299,37 @@ struct finite_element > { return output.flattened; } + // overload for two-sided interior face kernels + template + SERAC_HOST_DEVICE static auto interpolate(const tensor& X, const TensorProductQuadratureRule&) + { + constexpr auto xi = GaussLegendreNodes(); + static constexpr int num_quadrature_points = q * (q + 1) / 2; + + tensor< tuple< tensor, tensor >, num_quadrature_points> output; + +#if 0 + // transpose the quadrature data into a flat tensor of tuples + union { + tensor, tensor >, num_quadrature_points> unflattened; + tensor flattened; + } output{}; + + for (int i = 0; i < c; i++) { + for (int j = 0; j < num_quadrature_points; j++) { + for (int k = 0; k < ndof; k++) { + get(output.unflattened[j])[i] += X(i, k) * shape_function(xi[j], k); + get(output.unflattened[j])[i] += X(i, k) * shape_function_gradient(xi[j], k); + } + } + } + + return output.flattened; +#endif + return output; + + } + template SERAC_HOST_DEVICE static void integrate(const tensor, q*(q + 1) / 2>& qf_output, const TensorProductQuadratureRule&, diff --git a/src/serac/numerics/functional/domain.hpp b/src/serac/numerics/functional/domain.hpp index f29591e819..072169f2ee 100644 --- a/src/serac/numerics/functional/domain.hpp +++ b/src/serac/numerics/functional/domain.hpp @@ -27,7 +27,7 @@ struct Domain { InteriorFaces }; - static constexpr int num_types = 2; ///< the number of entries in the Type enum + static constexpr int num_types = 3; ///< the number of entries in the Type enum /// @brief the underyling mesh for this domain const mfem::Mesh& mesh_; diff --git a/src/serac/numerics/functional/element_restriction.cpp b/src/serac/numerics/functional/element_restriction.cpp index d0f3749b8c..136a10b307 100644 --- a/src/serac/numerics/functional/element_restriction.cpp +++ b/src/serac/numerics/functional/element_restriction.cpp @@ -527,14 +527,14 @@ mfem::Array BlockElementRestriction::bOffsets() const void BlockElementRestriction::Gather(const mfem::Vector& L_vector, mfem::BlockVector& E_block_vector) const { - for (auto [geom, restriction] : restrictions) { + for (auto & [geom, restriction] : restrictions) { restriction.Gather(L_vector, E_block_vector.GetBlock(geom)); } } void BlockElementRestriction::ScatterAdd(const mfem::BlockVector& E_block_vector, mfem::Vector& L_vector) const { - for (auto [geom, restriction] : restrictions) { + for (auto & [geom, restriction] : restrictions) { restriction.ScatterAdd(E_block_vector.GetBlock(geom), L_vector); } } diff --git a/src/serac/numerics/functional/functional.hpp b/src/serac/numerics/functional/functional.hpp index 44a8688e04..97d32b4ff2 100644 --- a/src/serac/numerics/functional/functional.hpp +++ b/src/serac/numerics/functional/functional.hpp @@ -222,7 +222,7 @@ class Functional { { auto mem_type = mfem::Device::GetMemoryType(); - for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements}) { + for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements, Domain::Type::InteriorFaces}) { input_E_[type].resize(num_trial_spaces); } @@ -232,12 +232,16 @@ class Functional { input_L_[i].SetSize(P_trial_[i]->Height(), mfem::Device::GetMemoryType()); // L->E - for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements}) { + for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements, Domain::Type::InteriorFaces}) { if (type == Domain::Type::Elements) { G_trial_[type][i] = BlockElementRestriction(trial_fes[i]); - } else { + } + if (type == Domain::Type::BoundaryElements) { G_trial_[type][i] = BlockElementRestriction(trial_fes[i], FaceType::BOUNDARY); } + if (type == Domain::Type::InteriorFaces) { + G_trial_[type][i] = BlockElementRestriction(trial_fes[i], FaceType::INTERIOR); + } // note: we have to use "Update" here, as mfem::BlockVector's // copy assignment ctor (operator=) doesn't let you make changes @@ -246,12 +250,16 @@ class Functional { } } - for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements}) { + for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements, Domain::Type::InteriorFaces}) { if (type == Domain::Type::Elements) { G_test_[type] = BlockElementRestriction(test_fes); - } else { + } + if (type == Domain::Type::BoundaryElements) { G_test_[type] = BlockElementRestriction(test_fes, FaceType::BOUNDARY); } + if (type == Domain::Type::InteriorFaces) { + G_test_[type] = BlockElementRestriction(test_fes, FaceType::INTERIOR); + } output_E_[type].Update(G_test_[type].bOffsets(), mem_type); } diff --git a/src/serac/numerics/functional/interior_face_integral_kernels.hpp b/src/serac/numerics/functional/interior_face_integral_kernels.hpp index 59417d654f..43117a08d2 100644 --- a/src/serac/numerics/functional/interior_face_integral_kernels.hpp +++ b/src/serac/numerics/functional/interior_face_integral_kernels.hpp @@ -50,27 +50,15 @@ struct QFunctionArgument, Dimension> { }; /// @overload -template -struct QFunctionArgument, Dimension<1>> { +template +struct QFunctionArgument, Dimension > { using type = serac::tuple; ///< what will be passed to the q-function }; /// @overload -template -struct QFunctionArgument, Dimension> { - using type = serac::tuple>; ///< what will be passed to the q-function -}; - -/// @overload -template -struct QFunctionArgument, Dimension<1>> { - using type = serac::tuple, tensor>; ///< what will be passed to the q-function -}; - -/// @overload -template -struct QFunctionArgument, Dimension> { - using type = serac::tuple, tensor>; ///< what will be passed to the q-function +template +struct QFunctionArgument, Dimension > { + using type = serac::tuple< tensor , tensor >; ///< what will be passed to the q-function }; /// @overload @@ -105,8 +93,7 @@ template auto get_derivative_type(lambda qf) { using qf_arguments = serac::tuple>::type...>; - return tuple{get_gradient(apply_qf(qf, double{}, tensor{}, make_dual_wrt(qf_arguments{}))), - zero{}}; + return get_gradient(apply_qf(qf, double{}, tensor{}, make_dual_wrt(qf_arguments{}))); }; template @@ -116,7 +103,7 @@ SERAC_HOST_DEVICE auto batch_apply_qf(lambda qf, double t, const tensor, tensor>; using return_type = decltype(qf(double{}, first_arg_t{}, T{}[0]...)); - tensor, n> outputs{}; + tensor outputs{}; for (int i = 0; i < n; i++) { tensor x_q; tensor J_q; @@ -126,7 +113,7 @@ SERAC_HOST_DEVICE auto batch_apply_qf(lambda qf, double t, const tensor(outputs[i]) = qf(t, serac::tuple{x_q, J_q}, inputs[i]...) * scale; + outputs[i] = qf(t, serac::tuple{x_q, J_q}, inputs[i]...) * scale; } return outputs; } @@ -138,7 +125,7 @@ SERAC_HOST_DEVICE auto batch_apply_qf(lambda qf, double t, const tensor, tensor>; using return_type = decltype(qf(double{}, first_arg_t{}, T{}[0]...)); - tensor, n> outputs{}; + tensor outputs{}; for (int i = 0; i < n; i++) { tensor x_q; tensor J_q; @@ -150,7 +137,7 @@ SERAC_HOST_DEVICE auto batch_apply_qf(lambda qf, double t, const tensor(outputs[i]) = qf(t, serac::tuple{x_q, J_q}, inputs[i]...) * scale; + outputs[i] = qf(t, serac::tuple{x_q, J_q}, inputs[i]...) * scale; } return outputs; } @@ -169,13 +156,13 @@ void evaluation_kernel_impl(trial_element_type trial_elements, test_element, dou constexpr int nqp = num_quadrature_points(geom, Q); auto J = reinterpret_cast*>(jacobians); auto x = reinterpret_cast*>(positions); - auto r = reinterpret_cast(outputs); + auto r = reinterpret_cast(outputs); static constexpr TensorProductQuadratureRule rule{}; static constexpr int qpts_per_elem = num_quadrature_points(geom, Q); [[maybe_unused]] tuple u = { - reinterpret_cast(trial_elements))::dof_type*>(inputs[indices])...}; + reinterpret_cast(trial_elements))::dof_type_if*>(inputs[indices])...}; // for each element in the domain for (uint32_t e = 0; e < num_elements; e++) { @@ -301,6 +288,7 @@ template dK, derivatives_type* qf_derivatives, const int* elements, std::size_t num_elements) { + #if 0 using test_element = finite_element; using trial_element = finite_element; @@ -322,6 +310,7 @@ void element_gradient_kernel(ExecArrayView dK, d test_element::integrate(source_and_flux, rule, output_ptr + J, trial_element::ndof); } } + #endif } template residual(&fespace, {&fespace}); + constexpr int DERIVATIVE = 1; + residual.AddInteriorFaceIntegral( - Dimension{}, DependsOn<0>{}, - [=](double /*t*/, auto /*x*/, auto velocity) { - auto [u_avg, u_diff] = velocity; - return u_avg; + Dimension{}, DependsOn<0>{}, + [=](double /*t*/, auto X, auto velocity) { + + // compute the surface normal + auto dX_dxi = get(X); + auto n = normalize(cross(dX_dxi)); + + // extract the velocity values from each side of the interface + // note: the orientation convention is such that the normal + // computed as above will point from from side 1->2 + auto [u_1, u_2] = velocity; + + auto a = dot(u_2 - u_1, n); + + auto s_1 = u_1 * a; + auto s_2 = u_2 * a; + + return serac::tuple{s_1, s_2}; + }, *mesh); From 8f739f1b8fd9e42ad80c9a001ced8e50d910cffa Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Fri, 6 Sep 2024 11:46:31 -0700 Subject: [PATCH 10/92] debugging segfault --- src/serac/numerics/functional/element_restriction.hpp | 4 ++++ src/serac/numerics/functional/tests/functional_basic_dg.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/serac/numerics/functional/element_restriction.hpp b/src/serac/numerics/functional/element_restriction.hpp index f949212b8c..517af4e912 100644 --- a/src/serac/numerics/functional/element_restriction.hpp +++ b/src/serac/numerics/functional/element_restriction.hpp @@ -5,6 +5,7 @@ #include "mfem.hpp" #include "axom/core.hpp" #include "geometry.hpp" +#include "domain.hpp" inline bool isH1(const mfem::FiniteElementSpace& fes) { @@ -210,6 +211,9 @@ struct BlockElementRestriction { /// default ctor leaves this object uninitialized BlockElementRestriction() {} + /// create a BlockElementRestriction for the elements in a given domain + BlockElementRestriction(const mfem::FiniteElementSpace* fes, const Domain & domain); + /// create a BlockElementRestriction for all domain-elements (geom dim == spatial dim) BlockElementRestriction(const mfem::FiniteElementSpace* fes); diff --git a/src/serac/numerics/functional/tests/functional_basic_dg.cpp b/src/serac/numerics/functional/tests/functional_basic_dg.cpp index 4d6c719b99..51f363571f 100644 --- a/src/serac/numerics/functional/tests/functional_basic_dg.cpp +++ b/src/serac/numerics/functional/tests/functional_basic_dg.cpp @@ -33,7 +33,7 @@ void L2_test_2D() std::string meshfile = SERAC_REPO_DIR "/data/meshes/patch2D.mesh"; - auto mesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 1); + auto mesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 0); auto fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); mfem::ParFiniteElementSpace fespace(mesh.get(), &fec); From c00652255ee07e78718f44aca273880f4486fffd Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Wed, 11 Sep 2024 11:18:01 -0700 Subject: [PATCH 11/92] add new ctor for restriction that takes a Domain, fix two small bugs in restriction class --- src/serac/numerics/functional/domain.cpp | 7 + .../functional/element_restriction.cpp | 238 +++++++++++++++++- .../functional/element_restriction.hpp | 2 + 3 files changed, 246 insertions(+), 1 deletion(-) diff --git a/src/serac/numerics/functional/domain.cpp b/src/serac/numerics/functional/domain.cpp index dbb86d2a60..8385ef8d76 100644 --- a/src/serac/numerics/functional/domain.cpp +++ b/src/serac/numerics/functional/domain.cpp @@ -475,15 +475,19 @@ Domain EntireDomain(const mfem::Mesh& mesh) switch (geom) { case mfem::Geometry::TRIANGLE: output.tri_ids_.push_back(tri_id++); + output.mfem_tri_ids_.push_back(i); break; case mfem::Geometry::SQUARE: output.quad_ids_.push_back(quad_id++); + output.mfem_quad_ids_.push_back(i); break; case mfem::Geometry::TETRAHEDRON: output.tet_ids_.push_back(tet_id++); + output.mfem_tet_ids_.push_back(i); break; case mfem::Geometry::CUBE: output.hex_ids_.push_back(hex_id++); + output.mfem_hex_ids_.push_back(i); break; default: SLIC_ERROR("unsupported element type"); @@ -511,12 +515,15 @@ Domain EntireBoundary(const mfem::Mesh& mesh) switch (geom) { case mfem::Geometry::SEGMENT: output.edge_ids_.push_back(edge_id++); + output.mfem_edge_ids_.push_back(f); break; case mfem::Geometry::TRIANGLE: output.tri_ids_.push_back(tri_id++); + output.mfem_tri_ids_.push_back(f); break; case mfem::Geometry::SQUARE: output.quad_ids_.push_back(quad_id++); + output.mfem_quad_ids_.push_back(f); break; default: SLIC_ERROR("unsupported element type"); diff --git a/src/serac/numerics/functional/element_restriction.cpp b/src/serac/numerics/functional/element_restriction.cpp index 136a10b307..657ead140b 100644 --- a/src/serac/numerics/functional/element_restriction.cpp +++ b/src/serac/numerics/functional/element_restriction.cpp @@ -276,6 +276,74 @@ axom::Array GetElementRestriction(const mfem::F } } +axom::Array GetElementDofs(const mfem::FiniteElementSpace* fes, + mfem::Geometry::Type geom, + const std::vector< int > & mfem_elem_ids) + +{ + std::vector elem_dofs{}; + mfem::Mesh* mesh = fes->GetMesh(); + + // note: this assumes that all the elements are the same polynomial order + int p = fes->GetElementOrder(0); + std::vector > lex_perm = lexicographic_permutations(p); + + uint64_t n = 0; + + for (auto elem : mfem_elem_ids) { + + // discard elements with the wrong geometry + if (mesh->GetElementGeometry(elem) != geom) { + SLIC_ERROR("encountered incorrect element geometry type"); + } + + mfem::Array dofs; + + [[maybe_unused]] auto* dof_transformation = fes->GetElementDofs(elem, dofs); + + // mfem returns the H1 dofs in "native" order, so we need + // to apply the native-to-lexicographic permutation + if (isH1(*fes)) { + for (int k = 0; k < dofs.Size(); k++) { + elem_dofs.push_back({uint64_t(dofs[lex_perm[uint32_t(geom)][uint32_t(k)]])}); + } + } + + // the dofs mfem returns for Hcurl include information about + // dof orientation, but not for triangle faces on 3D elements. + // So, we need to manually + if (isHcurl(*fes)) { + // TODO + // TODO + // TODO + uint64_t sign = 1; + uint64_t orientation = 0; + for (int k = 0; k < dofs.Size(); k++) { + elem_dofs.push_back({uint64_t(dofs[k]), sign, orientation}); + } + } + + // mfem returns DG dofs in lexicographic order already + // so no permutation is required here + if (isDG(*fes)) { + for (int k = 0; k < dofs.Size(); k++) { + elem_dofs.push_back({uint64_t(dofs[k])}); + } + } + + n++; + } + + if (n == 0) { + return axom::Array{}; + } else { + uint64_t dofs_per_elem = elem_dofs.size() / n; + axom::Array output(n, dofs_per_elem); + std::memcpy(output.data(), elem_dofs.data(), sizeof(DoF) * n * dofs_per_elem); + return output; + } +} + axom::Array GetFaceDofs(const mfem::FiniteElementSpace* fes, mfem::Geometry::Type face_geom, FaceType type) { @@ -391,8 +459,144 @@ axom::Array GetFaceDofs(const mfem::FiniteEleme } } +axom::Array GetFaceDofs(const mfem::FiniteElementSpace* fes, + mfem::Geometry::Type face_geom, + const std::vector & mfem_face_ids) +{ + std::vector face_dofs; + mfem::Mesh* mesh = fes->GetMesh(); + mfem::Table* face_to_elem = mesh->GetFaceToElementTable(); + + // note: this assumes that all the elements are the same polynomial order + int p = fes->GetElementOrder(0); + Array2D face_perm = face_permutations(face_geom, p); + std::vector > local_face_dofs = geom_local_face_dofs(p); + std::vector > lex_perm = lexicographic_permutations(p); + + uint64_t n = 0; + + for (int f : mfem_face_ids) { + + if (mesh->GetFaceGeometry(f) != face_geom) { + SLIC_ERROR("encountered incorrect face geometry type"); + } + + // mfem doesn't provide this connectivity info for DG spaces directly, + // so we have to get at it indirectly in several steps: + if (isDG(*fes)) { + // 1. find the element(s) that this face belongs to + mfem::Array elem_ids; + face_to_elem->GetRow(f, elem_ids); + + for (auto elem : elem_ids) { + // 2a. get the list of faces (and their orientations) that belong to that element ... + mfem::Array elem_side_ids, orientations; + if (mesh->Dimension() == 2) { + mesh->GetElementEdges(elem, elem_side_ids, orientations); + + // mfem returns {-1, 1} for edge orientations, + // but {0, 1, ... , n} for face orientations. + // Here, we renumber the edge orientations to + // {0 (no permutation), 1 (reversed)} so the values can be + // consistently used as indices into a permutation table + for (auto& o : orientations) { + o = (o == -1) ? 1 : 0; + } + + } else { + mesh->GetElementFaces(elem, elem_side_ids, orientations); + } + + // 2b. ... and find `i` such that `elem_side_ids[i] == f` + int i; + for (i = 0; i < elem_side_ids.Size(); i++) { + if (elem_side_ids[i] == f) break; + } + + // 3. get the dofs for the entire element + mfem::Array elem_dof_ids; + fes->GetElementDofs(elem, elem_dof_ids); + + mfem::Geometry::Type elem_geom = mesh->GetElementGeometry(elem); + + // mfem uses different conventions for boundary element orientations in 2D and 3D. + // In 2D, mfem's official edge orientations on the boundary will always be a mix of + // CW and CCW, so we have to discard mfem's orientation information in order + // to get a consistent winding. + // + // In 3D, mfem does use a consistently CCW winding for boundary faces (I think). + int orientation = (mesh->Dimension() == 2) ? 0 : orientations[i]; + + // 4. extract only the dofs that correspond to side `i` + for (auto k : face_perm(orientation)) { + face_dofs.push_back(uint64_t(elem_dof_ids[local_face_dofs[uint32_t(elem_geom)](i, k)])); + } + + } + + // H1 and Hcurl spaces are more straight-forward, since + // we can use FiniteElementSpace::GetFaceDofs() directly + } else { + mfem::Array dofs; + + fes->GetFaceDofs(f, dofs); + + if (isHcurl(*fes)) { + for (int k = 0; k < dofs.Size(); k++) { + if (dofs[k] >= 0) { + face_dofs.push_back(DoF{uint64_t(dofs[k]), 0}); + } else { + face_dofs.push_back(DoF{uint64_t(-1 - dofs[k]), 1}); + } + } + } else { + for (int k = 0; k < dofs.Size(); k++) { + face_dofs.push_back(uint64_t(dofs[lex_perm[uint32_t(face_geom)][uint32_t(k)]])); + } + } + } + + n++; + } + + delete face_to_elem; + + if (n == 0) { + return axom::Array{}; + } else { + uint64_t dofs_per_face = face_dofs.size() / n; + axom::Array output(n, dofs_per_face); + std::memcpy(output.data(), face_dofs.data(), sizeof(DoF) * n * dofs_per_face); + return output; + } +} + namespace serac { +ElementRestriction::ElementRestriction(const mfem::FiniteElementSpace* fes, mfem::Geometry::Type elem_geom, const std::vector< int > & elem_ids) { + + int sdim = fes->GetMesh()->Dimension(); + int gdim = dimension_of(elem_geom); + + if (gdim == sdim) { + dof_info = GetElementDofs(fes, elem_geom, elem_ids); + } + if (gdim+1 == sdim) { + dof_info = GetFaceDofs(fes, elem_geom, elem_ids); + } + + ordering = fes->GetOrdering(); + + lsize = uint64_t(fes->GetVSize()); + components = uint64_t(fes->GetVDim()); + num_nodes = lsize / components; + num_elements = uint64_t(dof_info.shape()[0]); + nodes_per_elem = uint64_t(dof_info.shape()[1]); + esize = num_elements * nodes_per_elem * components; + + +} + ElementRestriction::ElementRestriction(const mfem::FiniteElementSpace* fes, mfem::Geometry::Type elem_geom) { dof_info = GetElementRestriction(fes, elem_geom); @@ -472,6 +676,32 @@ void ElementRestriction::ScatterAdd(const mfem::Vector& E_vector, mfem::Vector& //////////////////////////////////////////////////////////////////////// +/// create a BlockElementRestriction for the elements in a given domain +BlockElementRestriction::BlockElementRestriction(const mfem::FiniteElementSpace* fes, const Domain & domain) { + + // TODO: changing the mfem_XXX_ids arrays to mfem_ids[XXX] would simplify this + if (domain.mfem_edge_ids_.size() > 0) { + restrictions[mfem::Geometry::SEGMENT] = ElementRestriction(fes, mfem::Geometry::SEGMENT, domain.mfem_edge_ids_); + } + + if (domain.mfem_tri_ids_.size() > 0) { + restrictions[mfem::Geometry::TRIANGLE] = ElementRestriction(fes, mfem::Geometry::TRIANGLE, domain.mfem_tri_ids_); + } + + if (domain.mfem_quad_ids_.size() > 0) { + restrictions[mfem::Geometry::SQUARE] = ElementRestriction(fes, mfem::Geometry::SQUARE, domain.mfem_quad_ids_); + } + + if (domain.mfem_tet_ids_.size() > 0) { + restrictions[mfem::Geometry::TETRAHEDRON] = ElementRestriction(fes, mfem::Geometry::TETRAHEDRON, domain.mfem_tet_ids_); + } + + if (domain.mfem_hex_ids_.size() > 0) { + restrictions[mfem::Geometry::CUBE] = ElementRestriction(fes, mfem::Geometry::CUBE, domain.mfem_hex_ids_); + } + +} + BlockElementRestriction::BlockElementRestriction(const mfem::FiniteElementSpace* fes) { int dim = fes->GetMesh()->Dimension(); @@ -504,7 +734,13 @@ BlockElementRestriction::BlockElementRestriction(const mfem::FiniteElementSpace* } } -uint64_t BlockElementRestriction::ESize() const { return (*restrictions.begin()).second.ESize(); } +uint64_t BlockElementRestriction::ESize() const { + uint64_t total = 0; + for (auto & [geom, restriction] : restrictions) { + total += restriction.ESize(); + } + return total; +} uint64_t BlockElementRestriction::LSize() const { return (*restrictions.begin()).second.LSize(); } diff --git a/src/serac/numerics/functional/element_restriction.hpp b/src/serac/numerics/functional/element_restriction.hpp index 517af4e912..ded9712a9f 100644 --- a/src/serac/numerics/functional/element_restriction.hpp +++ b/src/serac/numerics/functional/element_restriction.hpp @@ -148,6 +148,8 @@ struct ElementRestriction { /// default ctor leaves this object uninitialized ElementRestriction() {} + ElementRestriction(const mfem::FiniteElementSpace* fes, mfem::Geometry::Type elem_geom, const std::vector & domain); + /// create an ElementRestriction for all domain-type (geom dim == spatial dim) elements of the specified geometry ElementRestriction(const mfem::FiniteElementSpace* fes, mfem::Geometry::Type elem_geom); From 51b9dae3033cd1aff53f9094fe250d2bb8bc0597 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Wed, 11 Sep 2024 11:18:25 -0700 Subject: [PATCH 12/92] add some tests for domain-based restriction operators --- .../numerics/functional/tests/CMakeLists.txt | 1 + .../tests/dg_restriction_operators.cpp | 1 + .../tests/element_restriction_tests.cpp | 277 ++++++++++++++++++ 3 files changed, 279 insertions(+) create mode 100644 src/serac/numerics/functional/tests/element_restriction_tests.cpp diff --git a/src/serac/numerics/functional/tests/CMakeLists.txt b/src/serac/numerics/functional/tests/CMakeLists.txt index c0286ed3ae..093c091fc0 100644 --- a/src/serac/numerics/functional/tests/CMakeLists.txt +++ b/src/serac/numerics/functional/tests/CMakeLists.txt @@ -18,6 +18,7 @@ blt_add_executable(NAME tensor_unit_tests set(functional_serial_test_sources functional_shape_derivatives.cpp simplex_basis_function_unit_tests.cpp + element_restriction_tests.cpp dg_restriction_operators.cpp bug_boundary_qoi.cpp domain_tests.cpp diff --git a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp index 25984761b9..dde6e1e80d 100644 --- a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp +++ b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp @@ -5,6 +5,7 @@ using namespace serac; + std::string mesh_dir = SERAC_REPO_DIR "/data/meshes/"; constexpr mfem::Geometry::Type face_type(mfem::Geometry::Type geom) { diff --git a/src/serac/numerics/functional/tests/element_restriction_tests.cpp b/src/serac/numerics/functional/tests/element_restriction_tests.cpp new file mode 100644 index 0000000000..7833514054 --- /dev/null +++ b/src/serac/numerics/functional/tests/element_restriction_tests.cpp @@ -0,0 +1,277 @@ +#include + +#include "serac/numerics/functional/domain.hpp" +#include "serac/numerics/functional/element_restriction.hpp" + +using namespace serac; + +std::ostream & operator<<(std::ostream & out, axom::Array arr) { + for (int i = 0; i < arr.shape()[0]; i++) { + for (int j = 0; j < arr.shape()[1]; j++) { + out << arr[i][j].index() << " "; + } + out << std::endl; + } + return out; +} + +TEST(patch_test_meshes, triangle_domains) { + + int p = 2; + int dim = 2; + mfem::Mesh mesh(SERAC_REPO_DIR"/data/meshes/patch2D_tris.mesh"); + + auto H1_fec = std::make_unique(p, dim); + auto H1_fes = std::make_unique(&mesh, H1_fec.get()); + + auto Hcurl_fec = std::make_unique(p, dim); + auto Hcurl_fes = std::make_unique(&mesh, Hcurl_fec.get()); + + auto L2_fec = std::make_unique(p, dim, mfem::BasisType::GaussLobatto); + auto L2_fes = std::make_unique(&mesh, L2_fec.get()); + + Domain whole = EntireDomain(mesh); + EXPECT_EQ(whole.mfem_edge_ids_.size(), 0); + EXPECT_EQ(whole.mfem_tri_ids_.size(), 4); + EXPECT_EQ(whole.mfem_quad_ids_.size(), 0); + EXPECT_EQ(whole.mfem_tet_ids_.size(), 0); + EXPECT_EQ(whole.mfem_hex_ids_.size(), 0); + + { + BlockElementRestriction H1_BER(H1_fes.get(), whole); + EXPECT_EQ(H1_BER.ESize(), 4 * 6); + } + + Domain boundary = EntireBoundary(mesh); + EXPECT_EQ(boundary.mfem_edge_ids_.size(), 4); + EXPECT_EQ(boundary.mfem_tri_ids_.size(), 0); + EXPECT_EQ(boundary.mfem_quad_ids_.size(), 0); + EXPECT_EQ(boundary.mfem_tet_ids_.size(), 0); + EXPECT_EQ(boundary.mfem_hex_ids_.size(), 0); + + { + BlockElementRestriction H1_BER(H1_fes.get(), boundary); + EXPECT_EQ(H1_BER.ESize(), 4 * 3); + } + + Domain interior = InteriorFaces(mesh); + EXPECT_EQ(interior.mfem_edge_ids_.size(), 4); + EXPECT_EQ(interior.mfem_tri_ids_.size(), 0); + EXPECT_EQ(interior.mfem_quad_ids_.size(), 0); + EXPECT_EQ(interior.mfem_tet_ids_.size(), 0); + EXPECT_EQ(interior.mfem_hex_ids_.size(), 0); + + { + BlockElementRestriction H1_BER(H1_fes.get(), interior); + EXPECT_EQ(H1_BER.ESize(), 4 * 3); + } + +} + +TEST(patch_test_meshes, quadrilateral_domains) { + + int p = 2; + int dim = 2; + mfem::Mesh mesh(SERAC_REPO_DIR"/data/meshes/patch2D_quads.mesh"); + + auto H1_fec = std::make_unique(p, dim); + auto Hcurl_fec = std::make_unique(p, dim); + auto L2_fec = std::make_unique(p, dim, mfem::BasisType::GaussLobatto); + + auto H1_fes = std::make_unique(&mesh, H1_fec.get()); + auto Hcurl_fes = std::make_unique(&mesh, Hcurl_fec.get()); + auto L2_fes = std::make_unique(&mesh, L2_fec.get()); + + Domain whole = EntireDomain(mesh); + EXPECT_EQ(whole.mfem_edge_ids_.size(), 0); + EXPECT_EQ(whole.mfem_tri_ids_.size(), 0); + EXPECT_EQ(whole.mfem_quad_ids_.size(), 5); + EXPECT_EQ(whole.mfem_tet_ids_.size(), 0); + EXPECT_EQ(whole.mfem_hex_ids_.size(), 0); + + { + BlockElementRestriction H1_BER(H1_fes.get(), whole); + EXPECT_EQ(H1_BER.ESize(), 5 * 9); + } + + Domain boundary = EntireBoundary(mesh); + EXPECT_EQ(boundary.mfem_edge_ids_.size(), 4); + EXPECT_EQ(boundary.mfem_tri_ids_.size(), 0); + EXPECT_EQ(boundary.mfem_quad_ids_.size(), 0); + EXPECT_EQ(boundary.mfem_tet_ids_.size(), 0); + EXPECT_EQ(boundary.mfem_hex_ids_.size(), 0); + + { + BlockElementRestriction H1_BER(H1_fes.get(), boundary); + EXPECT_EQ(H1_BER.ESize(), 4 * 3); + } + + Domain interior = InteriorFaces(mesh); + EXPECT_EQ(interior.mfem_edge_ids_.size(), 8); + EXPECT_EQ(interior.mfem_tri_ids_.size(), 0); + EXPECT_EQ(interior.mfem_quad_ids_.size(), 0); + EXPECT_EQ(interior.mfem_tet_ids_.size(), 0); + EXPECT_EQ(interior.mfem_hex_ids_.size(), 0); + + { + BlockElementRestriction H1_BER(H1_fes.get(), interior); + EXPECT_EQ(H1_BER.ESize(), 8 * 3); + } + +} + +TEST(patch_test_meshes, triangle_and_quadrilateral_domains) { + + int p = 2; + int dim = 2; + mfem::Mesh mesh(SERAC_REPO_DIR"/data/meshes/patch2D_tris_and_quads.mesh"); + + auto H1_fec = std::make_unique(p, dim); + auto Hcurl_fec = std::make_unique(p, dim); + auto L2_fec = std::make_unique(p, dim, mfem::BasisType::GaussLobatto); + + auto H1_fes = std::make_unique(&mesh, H1_fec.get()); + auto Hcurl_fes = std::make_unique(&mesh, Hcurl_fec.get()); + auto L2_fes = std::make_unique(&mesh, L2_fec.get()); + + Domain whole = EntireDomain(mesh); + EXPECT_EQ(whole.mfem_edge_ids_.size(), 0); + EXPECT_EQ(whole.mfem_tri_ids_.size(), 2); + EXPECT_EQ(whole.mfem_quad_ids_.size(), 4); + EXPECT_EQ(whole.mfem_tet_ids_.size(), 0); + EXPECT_EQ(whole.mfem_hex_ids_.size(), 0); + + { + BlockElementRestriction H1_BER(H1_fes.get(), whole); + EXPECT_EQ(H1_BER.ESize(), 2 * 6 + 4 * 9); + } + + Domain boundary = EntireBoundary(mesh); + EXPECT_EQ(boundary.mfem_edge_ids_.size(), 4); + EXPECT_EQ(boundary.mfem_tri_ids_.size(), 0); + EXPECT_EQ(boundary.mfem_quad_ids_.size(), 0); + EXPECT_EQ(boundary.mfem_tet_ids_.size(), 0); + EXPECT_EQ(boundary.mfem_hex_ids_.size(), 0); + + { + BlockElementRestriction H1_BER(H1_fes.get(), boundary); + EXPECT_EQ(H1_BER.ESize(), 4 * 3); + } + + Domain interior = InteriorFaces(mesh); + EXPECT_EQ(interior.mfem_edge_ids_.size(), 9); + EXPECT_EQ(interior.mfem_tri_ids_.size(), 0); + EXPECT_EQ(interior.mfem_quad_ids_.size(), 0); + EXPECT_EQ(interior.mfem_tet_ids_.size(), 0); + EXPECT_EQ(interior.mfem_hex_ids_.size(), 0); + + { + BlockElementRestriction H1_BER(H1_fes.get(), interior); + EXPECT_EQ(H1_BER.ESize(), 9 * 3); + } + +} + +TEST(patch_test_meshes, tetrahedron_domains) { + + int p = 2; + int dim = 3; + mfem::Mesh mesh(SERAC_REPO_DIR"/data/meshes/patch3D_tets.mesh"); + + auto H1_fec = std::make_unique(p, dim); + auto Hcurl_fec = std::make_unique(p, dim); + auto L2_fec = std::make_unique(p, dim, mfem::BasisType::GaussLobatto); + + auto H1_fes = std::make_unique(&mesh, H1_fec.get()); + auto Hcurl_fes = std::make_unique(&mesh, Hcurl_fec.get()); + auto L2_fes = std::make_unique(&mesh, L2_fec.get()); + + Domain whole = EntireDomain(mesh); + EXPECT_EQ(whole.mfem_edge_ids_.size(), 0); + EXPECT_EQ(whole.mfem_tri_ids_.size(), 0); + EXPECT_EQ(whole.mfem_quad_ids_.size(), 0); + EXPECT_EQ(whole.mfem_tet_ids_.size(), 12); + EXPECT_EQ(whole.mfem_hex_ids_.size(), 0); + + { + BlockElementRestriction H1_BER(H1_fes.get(), whole); + EXPECT_EQ(H1_BER.ESize(), 12 * 10); + } + + Domain boundary = EntireBoundary(mesh); + EXPECT_EQ(boundary.mfem_edge_ids_.size(), 0); + EXPECT_EQ(boundary.mfem_tri_ids_.size(), 12); + EXPECT_EQ(boundary.mfem_quad_ids_.size(), 0); + EXPECT_EQ(boundary.mfem_tet_ids_.size(), 0); + EXPECT_EQ(boundary.mfem_hex_ids_.size(), 0); + + { + BlockElementRestriction H1_BER(H1_fes.get(), boundary); + EXPECT_EQ(H1_BER.ESize(), 12 * 6); + } + + Domain interior = InteriorFaces(mesh); + EXPECT_EQ(interior.mfem_edge_ids_.size(), 0); + EXPECT_EQ(interior.mfem_tri_ids_.size(), 18); + EXPECT_EQ(interior.mfem_quad_ids_.size(), 0); + EXPECT_EQ(interior.mfem_tet_ids_.size(), 0); + EXPECT_EQ(interior.mfem_hex_ids_.size(), 0); + + { + BlockElementRestriction H1_BER(H1_fes.get(), interior); + EXPECT_EQ(H1_BER.ESize(), 18 * 6); + } + +} + +TEST(patch_test_meshes, hexahedron_domains) { + + int p = 2; + int dim = 3; + mfem::Mesh mesh(SERAC_REPO_DIR"/data/meshes/patch3D_hexes.mesh"); + + auto H1_fec = std::make_unique(p, dim); + auto Hcurl_fec = std::make_unique(p, dim); + auto L2_fec = std::make_unique(p, dim, mfem::BasisType::GaussLobatto); + + auto H1_fes = std::make_unique(&mesh, H1_fec.get()); + auto Hcurl_fes = std::make_unique(&mesh, Hcurl_fec.get()); + auto L2_fes = std::make_unique(&mesh, L2_fec.get()); + + Domain whole = EntireDomain(mesh); + EXPECT_EQ(whole.mfem_edge_ids_.size(), 0); + EXPECT_EQ(whole.mfem_tri_ids_.size(), 0); + EXPECT_EQ(whole.mfem_quad_ids_.size(), 0); + EXPECT_EQ(whole.mfem_tet_ids_.size(), 0); + EXPECT_EQ(whole.mfem_hex_ids_.size(), 7); + + { + BlockElementRestriction H1_BER(H1_fes.get(), whole); + EXPECT_EQ(H1_BER.ESize(), 7 * 27); + } + + Domain boundary = EntireBoundary(mesh); + EXPECT_EQ(boundary.mfem_edge_ids_.size(), 0); + EXPECT_EQ(boundary.mfem_tri_ids_.size(), 0); + EXPECT_EQ(boundary.mfem_quad_ids_.size(), 6); + EXPECT_EQ(boundary.mfem_tet_ids_.size(), 0); + EXPECT_EQ(boundary.mfem_hex_ids_.size(), 0); + + { + BlockElementRestriction H1_BER(H1_fes.get(), boundary); + EXPECT_EQ(H1_BER.ESize(), 6 * 9); + } + + Domain interior = InteriorFaces(mesh); + EXPECT_EQ(interior.mfem_edge_ids_.size(), 0); + EXPECT_EQ(interior.mfem_tri_ids_.size(), 0); + EXPECT_EQ(interior.mfem_quad_ids_.size(), 18); + EXPECT_EQ(interior.mfem_tet_ids_.size(), 0); + EXPECT_EQ(interior.mfem_hex_ids_.size(), 0); + + { + BlockElementRestriction H1_BER(H1_fes.get(), interior); + EXPECT_EQ(H1_BER.ESize(), 18 * 9); + } + +} From 5082552de942cb2f7118bd001d078caf94c8e385 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Wed, 11 Sep 2024 11:33:47 -0700 Subject: [PATCH 13/92] include some checks for element restrictions over domains with Hcurl, L2 spaces --- .../tests/element_restriction_tests.cpp | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/src/serac/numerics/functional/tests/element_restriction_tests.cpp b/src/serac/numerics/functional/tests/element_restriction_tests.cpp index 7833514054..952e1a7521 100644 --- a/src/serac/numerics/functional/tests/element_restriction_tests.cpp +++ b/src/serac/numerics/functional/tests/element_restriction_tests.cpp @@ -40,6 +40,12 @@ TEST(patch_test_meshes, triangle_domains) { { BlockElementRestriction H1_BER(H1_fes.get(), whole); EXPECT_EQ(H1_BER.ESize(), 4 * 6); + + BlockElementRestriction Hcurl_BER(Hcurl_fes.get(), whole); + EXPECT_EQ(Hcurl_BER.ESize(), 4 * 8); + + BlockElementRestriction L2_BER(L2_fes.get(), whole); + EXPECT_EQ(L2_BER.ESize(), 4 * 6); } Domain boundary = EntireBoundary(mesh); @@ -52,6 +58,12 @@ TEST(patch_test_meshes, triangle_domains) { { BlockElementRestriction H1_BER(H1_fes.get(), boundary); EXPECT_EQ(H1_BER.ESize(), 4 * 3); + + BlockElementRestriction Hcurl_BER(Hcurl_fes.get(), boundary); + EXPECT_EQ(Hcurl_BER.ESize(), 4 * 2); + + BlockElementRestriction L2_BER(L2_fes.get(), boundary); + EXPECT_EQ(L2_BER.ESize(), 4 * 3); } Domain interior = InteriorFaces(mesh); @@ -64,6 +76,12 @@ TEST(patch_test_meshes, triangle_domains) { { BlockElementRestriction H1_BER(H1_fes.get(), interior); EXPECT_EQ(H1_BER.ESize(), 4 * 3); + + BlockElementRestriction Hcurl_BER(Hcurl_fes.get(), interior); + EXPECT_EQ(Hcurl_BER.ESize(), 4 * 2); + + BlockElementRestriction L2_BER(L2_fes.get(), interior); + EXPECT_EQ(L2_BER.ESize(), 4 * (3 * 2)); } } @@ -92,6 +110,12 @@ TEST(patch_test_meshes, quadrilateral_domains) { { BlockElementRestriction H1_BER(H1_fes.get(), whole); EXPECT_EQ(H1_BER.ESize(), 5 * 9); + + BlockElementRestriction Hcurl_BER(Hcurl_fes.get(), whole); + EXPECT_EQ(Hcurl_BER.ESize(), 5 * 12); + + BlockElementRestriction L2_BER(L2_fes.get(), whole); + EXPECT_EQ(L2_BER.ESize(), 5 * 9); } Domain boundary = EntireBoundary(mesh); @@ -104,6 +128,12 @@ TEST(patch_test_meshes, quadrilateral_domains) { { BlockElementRestriction H1_BER(H1_fes.get(), boundary); EXPECT_EQ(H1_BER.ESize(), 4 * 3); + + BlockElementRestriction Hcurl_BER(Hcurl_fes.get(), boundary); + EXPECT_EQ(Hcurl_BER.ESize(), 4 * 2); + + BlockElementRestriction L2_BER(L2_fes.get(), boundary); + EXPECT_EQ(L2_BER.ESize(), 4 * 3); } Domain interior = InteriorFaces(mesh); @@ -116,6 +146,12 @@ TEST(patch_test_meshes, quadrilateral_domains) { { BlockElementRestriction H1_BER(H1_fes.get(), interior); EXPECT_EQ(H1_BER.ESize(), 8 * 3); + + BlockElementRestriction Hcurl_BER(Hcurl_fes.get(), interior); + EXPECT_EQ(Hcurl_BER.ESize(), 8 * 2); + + BlockElementRestriction L2_BER(L2_fes.get(), interior); + EXPECT_EQ(L2_BER.ESize(), 8 * (3 * 2)); } } @@ -144,6 +180,12 @@ TEST(patch_test_meshes, triangle_and_quadrilateral_domains) { { BlockElementRestriction H1_BER(H1_fes.get(), whole); EXPECT_EQ(H1_BER.ESize(), 2 * 6 + 4 * 9); + + BlockElementRestriction Hcurl_BER(Hcurl_fes.get(), whole); + EXPECT_EQ(Hcurl_BER.ESize(), 2 * 8 + 4 * 12); + + BlockElementRestriction L2_BER(L2_fes.get(), whole); + EXPECT_EQ(L2_BER.ESize(), 2 * 6 + 4 * 9); } Domain boundary = EntireBoundary(mesh); @@ -156,6 +198,12 @@ TEST(patch_test_meshes, triangle_and_quadrilateral_domains) { { BlockElementRestriction H1_BER(H1_fes.get(), boundary); EXPECT_EQ(H1_BER.ESize(), 4 * 3); + + BlockElementRestriction Hcurl_BER(Hcurl_fes.get(), boundary); + EXPECT_EQ(Hcurl_BER.ESize(), 4 * 2); + + BlockElementRestriction L2_BER(L2_fes.get(), boundary); + EXPECT_EQ(L2_BER.ESize(), 4 * 3); } Domain interior = InteriorFaces(mesh); @@ -168,6 +216,12 @@ TEST(patch_test_meshes, triangle_and_quadrilateral_domains) { { BlockElementRestriction H1_BER(H1_fes.get(), interior); EXPECT_EQ(H1_BER.ESize(), 9 * 3); + + BlockElementRestriction Hcurl_BER(Hcurl_fes.get(), interior); + EXPECT_EQ(Hcurl_BER.ESize(), 9 * 2); + + BlockElementRestriction L2_BER(L2_fes.get(), interior); + EXPECT_EQ(L2_BER.ESize(), 9 * (3 * 2)); } } @@ -196,6 +250,12 @@ TEST(patch_test_meshes, tetrahedron_domains) { { BlockElementRestriction H1_BER(H1_fes.get(), whole); EXPECT_EQ(H1_BER.ESize(), 12 * 10); + + BlockElementRestriction Hcurl_BER(Hcurl_fes.get(), whole); + EXPECT_EQ(Hcurl_BER.ESize(), 12 * 20); + + BlockElementRestriction L2_BER(L2_fes.get(), whole); + EXPECT_EQ(L2_BER.ESize(), 12 * 10); } Domain boundary = EntireBoundary(mesh); @@ -208,6 +268,12 @@ TEST(patch_test_meshes, tetrahedron_domains) { { BlockElementRestriction H1_BER(H1_fes.get(), boundary); EXPECT_EQ(H1_BER.ESize(), 12 * 6); + + BlockElementRestriction Hcurl_BER(Hcurl_fes.get(), boundary); + EXPECT_EQ(Hcurl_BER.ESize(), 12 * 8); + + BlockElementRestriction L2_BER(L2_fes.get(), boundary); + EXPECT_EQ(L2_BER.ESize(), 12 * 6); } Domain interior = InteriorFaces(mesh); @@ -220,6 +286,12 @@ TEST(patch_test_meshes, tetrahedron_domains) { { BlockElementRestriction H1_BER(H1_fes.get(), interior); EXPECT_EQ(H1_BER.ESize(), 18 * 6); + + BlockElementRestriction Hcurl_BER(Hcurl_fes.get(), interior); + EXPECT_EQ(Hcurl_BER.ESize(), 18 * 8); + + BlockElementRestriction L2_BER(L2_fes.get(), interior); + EXPECT_EQ(L2_BER.ESize(), 18 * (6 * 2)); } } @@ -248,6 +320,12 @@ TEST(patch_test_meshes, hexahedron_domains) { { BlockElementRestriction H1_BER(H1_fes.get(), whole); EXPECT_EQ(H1_BER.ESize(), 7 * 27); + + BlockElementRestriction Hcurl_BER(Hcurl_fes.get(), whole); + EXPECT_EQ(Hcurl_BER.ESize(), 7 * 54); + + BlockElementRestriction L2_BER(L2_fes.get(), whole); + EXPECT_EQ(L2_BER.ESize(), 7 * 27); } Domain boundary = EntireBoundary(mesh); @@ -260,6 +338,12 @@ TEST(patch_test_meshes, hexahedron_domains) { { BlockElementRestriction H1_BER(H1_fes.get(), boundary); EXPECT_EQ(H1_BER.ESize(), 6 * 9); + + BlockElementRestriction Hcurl_BER(Hcurl_fes.get(), boundary); + EXPECT_EQ(Hcurl_BER.ESize(), 6 * 12); + + BlockElementRestriction L2_BER(L2_fes.get(), boundary); + EXPECT_EQ(L2_BER.ESize(), 6 * 9); } Domain interior = InteriorFaces(mesh); @@ -272,6 +356,12 @@ TEST(patch_test_meshes, hexahedron_domains) { { BlockElementRestriction H1_BER(H1_fes.get(), interior); EXPECT_EQ(H1_BER.ESize(), 18 * 9); + + BlockElementRestriction Hcurl_BER(Hcurl_fes.get(), interior); + EXPECT_EQ(Hcurl_BER.ESize(), 18 * 12); + + BlockElementRestriction L2_BER(L2_fes.get(), interior); + EXPECT_EQ(L2_BER.ESize(), 18 * (9 * 2)); } } From 18af6c88c9969e817125f5c24dd7cab1c470af29 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Fri, 13 Sep 2024 10:47:27 -0700 Subject: [PATCH 14/92] working on updating sparse matrix assembly --- .../functional/element_restriction.cpp | 1 - src/serac/numerics/functional/functional.hpp | 174 ++++++++++++++++++ 2 files changed, 174 insertions(+), 1 deletion(-) diff --git a/src/serac/numerics/functional/element_restriction.cpp b/src/serac/numerics/functional/element_restriction.cpp index 657ead140b..af1f95b922 100644 --- a/src/serac/numerics/functional/element_restriction.cpp +++ b/src/serac/numerics/functional/element_restriction.cpp @@ -756,7 +756,6 @@ mfem::Array BlockElementRestriction::bOffsets() const } else { offsets[g + 1] = offsets[g]; } - // std::cout << g << " " << offsets[g+1] << std::endl; } return offsets; }; diff --git a/src/serac/numerics/functional/functional.hpp b/src/serac/numerics/functional/functional.hpp index 82163e9f45..ee1770c425 100644 --- a/src/serac/numerics/functional/functional.hpp +++ b/src/serac/numerics/functional/functional.hpp @@ -662,6 +662,7 @@ class Functional { } } } + } // Copy the column indices to an auxilliary array as MFEM can mutate these during HypreParMatrix construction @@ -687,6 +688,179 @@ class Functional { return K; }; + void initialize_sparsity(mfem::SparseMatrix & A_local) { + + // TODO + + }; + + /** + * @brief assemble element matrices and return an mfem::HypreParMatrix by-reference + * + * @note: if A is empty, its nonzero sparsity will be determined automatically, but + * if A is nonempty, then we assume its sparsity pattern + * has all of the necessary nonzero entries (which will be true after passing + * in an empty HypreParMatrix to this function). + */ + void assemble(mfem::HypreParMatrix & A) { + + mfem::SparseMatrix A_local; + + // if A is an uninitialized HypreParMatrix + if (A.NNZ() == 0) { + + // then, we need to figure out which entries of the matrix could be nonzero + initialize_sparsity_pattern(A_local); + + } else { + + // otherwise, we can reuse the information in the provided HypreParMatrix + A.GetDiag(A_local); + + // except there is one catch: HypreParMatrix puts the diagonal entries + // first in each row, so we need to "sort" each row again so that we can + // do binary search later on in the actual assembly step + + // TODO + + } + +#if 0 + nd::array test_ids({nodes_per_test_element}); + test_el.indices(test_offsets, connectivity(elements(e)).data(), test_ids.data()); + + nd::array trial_ids({nodes_per_trial_element}); + trial_el.indices(trial_offsets, connectivity(elements(e)).data(), trial_ids.data()); + + for (uint32_t I = 0; I < nodes_per_test_element; I++) { + for (uint32_t i = 0; i < test_space.components; i++) { + int row_id = test_ids[I] * test_components + i; + + int which = row_id % nmutex; + int row_start = row_ptr[row_id]; + int row_end = row_ptr[row_id+1]; + for (uint32_t J = 0; J < nodes_per_trial_element; J++) { + for (uint32_t j = 0; j < trial_space.components; j++) { + int col_id = trial_ids[J] * trial_components + j; + + // find the position of the nonzero entry of this row with the right column + int position = std::lower_bound(&col_ind[row_start], &col_ind[row_end], col_id) - &col_ind[0]; + + values[position] += K_e(J, j, I, i); + } + } + } + } +#endif + + // now that we have a + for (auto & integral : form_.integrals_) { + + } + + #if 0 + + // the CSR graph (sparsity pattern) is reusable, so we cache + // that and ask mfem to not free that memory in ~SparseMatrix() + constexpr bool sparse_matrix_frees_graph_ptrs = false; + + // the CSR values are NOT reusable, so we pass ownership of + // them to the mfem::SparseMatrix, to be freed in ~SparseMatrix() + constexpr bool sparse_matrix_frees_values_ptr = true; + + constexpr bool col_ind_is_sorted = true; + + if (!lookup_tables.initialized) { + lookup_tables.init(form_.G_test_[Domain::Type::Elements], + form_.G_trial_[Domain::Type::Elements][which_argument]); + } + + double* values = new double[lookup_tables.nnz]{}; + + std::map> element_gradients[Domain::num_types]; + + for (auto& integral : form_.integrals_) { + auto& K_elem = element_gradients[integral.domain_.type_]; + auto& test_restrictions = form_.G_test_[integral.domain_.type_].restrictions; + auto& trial_restrictions = form_.G_trial_[integral.domain_.type_][which_argument].restrictions; + + if (K_elem.empty()) { + for (auto& [geom, test_restriction] : test_restrictions) { + auto& trial_restriction = trial_restrictions[geom]; + + K_elem[geom] = ExecArray(test_restriction.num_elements, + trial_restriction.nodes_per_elem * trial_restriction.components, + test_restriction.nodes_per_elem * test_restriction.components); + + detail::zero_out(K_elem[geom]); + } + } + + integral.ComputeElementGradients(K_elem, which_argument); + } + + for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements}) { + auto& K_elem = element_gradients[type]; + auto& test_restrictions = form_.G_test_[type].restrictions; + auto& trial_restrictions = form_.G_trial_[type][which_argument].restrictions; + + if (!K_elem.empty()) { + for (auto [geom, elem_matrices] : K_elem) { + std::vector test_vdofs(test_restrictions[geom].nodes_per_elem * test_restrictions[geom].components); + std::vector trial_vdofs(trial_restrictions[geom].nodes_per_elem * trial_restrictions[geom].components); + + for (axom::IndexType e = 0; e < elem_matrices.shape()[0]; e++) { + test_restrictions[geom].GetElementVDofs(e, test_vdofs); + trial_restrictions[geom].GetElementVDofs(e, trial_vdofs); + + for (uint32_t i = 0; i < uint32_t(elem_matrices.shape()[1]); i++) { + int col = int(trial_vdofs[i].index()); + + for (uint32_t j = 0; j < uint32_t(elem_matrices.shape()[2]); j++) { + int row = int(test_vdofs[j].index()); + + int sign = test_vdofs[j].sign() * trial_vdofs[i].sign(); + + // note: col / row appear backwards here, because the element matrix kernel + // is actually transposed, as a result of being row-major storage. + // + // This is kind of confusing, and will be fixed in a future refactor + // of the element gradient kernel implementation + [[maybe_unused]] auto nz = lookup_tables(row, col); + values[lookup_tables(row, col)] += sign * elem_matrices(e, i, j); + } + } + } + } + } + + } + + // Copy the column indices to an auxilliary array as MFEM can mutate these during HypreParMatrix construction + col_ind_copy_ = lookup_tables.col_ind; + + auto J_local = + mfem::SparseMatrix(lookup_tables.row_ptr.data(), col_ind_copy_.data(), values, form_.output_L_.Size(), + form_.input_L_[which_argument].Size(), sparse_matrix_frees_graph_ptrs, + sparse_matrix_frees_values_ptr, col_ind_is_sorted); + + auto* R = form_.test_space_->Dof_TrueDof_Matrix(); + + auto* A = + new mfem::HypreParMatrix(test_space_->GetComm(), test_space_->GlobalVSize(), trial_space_->GlobalVSize(), + test_space_->GetDofOffsets(), trial_space_->GetDofOffsets(), &J_local); + + auto* P = trial_space_->Dof_TrueDof_Matrix(); + + std::unique_ptr K(mfem::RAP(R, A, P)); + + delete A; + + return K; + + #endif + }; + friend auto assemble(Gradient& g) { return g.assemble(); } private: From 1945881d7d0b6a02be127a0b9ac199921f6af699 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Fri, 13 Sep 2024 16:49:45 -0700 Subject: [PATCH 15/92] more work on new sparse matrix assembly routine --- src/serac/numerics/functional/functional.hpp | 186 +++++++++++++++--- .../tests/functional_basic_h1_scalar.cpp | 25 ++- 2 files changed, 178 insertions(+), 33 deletions(-) diff --git a/src/serac/numerics/functional/functional.hpp b/src/serac/numerics/functional/functional.hpp index ee1770c425..2e5d681492 100644 --- a/src/serac/numerics/functional/functional.hpp +++ b/src/serac/numerics/functional/functional.hpp @@ -665,6 +665,17 @@ class Functional { } + for (int i = 0; i < lookup_tables.col_ind.size(); i++) { + std::cout << lookup_tables.col_ind[i] << " "; + } + std::cout << std::endl; + + for (int i = 0; i < lookup_tables.row_ptr.size(); i++) { + std::cout << lookup_tables.row_ptr[i] << " "; + } + std::cout << std::endl; + + // Copy the column indices to an auxilliary array as MFEM can mutate these during HypreParMatrix construction col_ind_copy_ = lookup_tables.col_ind; @@ -673,6 +684,10 @@ class Functional { form_.input_L_[which_argument].Size(), sparse_matrix_frees_graph_ptrs, sparse_matrix_frees_values_ptr, col_ind_is_sorted); + std::ofstream outfile("K_old.mtx"); + J_local.PrintMM(outfile); + outfile.close(); + auto* R = form_.test_space_->Dof_TrueDof_Matrix(); auto* A = @@ -688,9 +703,87 @@ class Functional { return K; }; - void initialize_sparsity(mfem::SparseMatrix & A_local) { + void initialize_sparsity_pattern(mfem::SparseMatrix & A_local) { + + using row_col = std::tuple; + + std::set< row_col > nonzero_entries; + + for (auto& integral : form_.integrals_) { + auto& test_restrictions = form_.G_test_[integral.domain_.type_].restrictions; + auto& trial_restrictions = form_.G_trial_[integral.domain_.type_][which_argument].restrictions; + + for (auto& [geom, test_restriction] : test_restrictions) { + auto& trial_restriction = trial_restrictions[geom]; + + // the degrees of freedom associated with the rows/columns of the e^th element stiffness matrix + std::vector test_vdofs(test_restriction.nodes_per_elem * test_restriction.components); + std::vector trial_vdofs(trial_restriction.nodes_per_elem * trial_restriction.components); + + auto num_elements = static_cast(test_restriction.num_elements); + for (uint32_t e = 0; e < num_elements; e++) { - // TODO + for (uint32_t i = 0; i < test_restriction.nodes_per_elem; i++) { + auto test_dof = test_restriction.dof_info(e, i); + for (uint32_t j = 0; j < test_restriction.components; j++) { + test_vdofs[i * test_restriction.components + j] = test_restriction.GetVDof(test_dof, j).index(); + std::cout << test_restriction.GetVDof(test_dof, j).index() << " "; + } + } + std::cout << std::endl; + + for (uint32_t i = 0; i < trial_restriction.nodes_per_elem; i++) { + auto trial_dof = trial_restriction.dof_info(e, i); + for (uint32_t j = 0; j < trial_restriction.components; j++) { + trial_vdofs[i * trial_restriction.components + j] = trial_restriction.GetVDof(trial_dof, j).index(); + std::cout << trial_restriction.GetVDof(trial_dof, j).index() << " "; + } + } + std::cout << std::endl; + std::cout << std::endl; + + for (int row : test_vdofs) { + for (int col : trial_vdofs) { + nonzero_entries.insert({row, col}); + } + } + } + } + } + + int nnz = nonzero_entries.size(); + int nrows = form_.output_L_.Size(); + int ncols = form_.input_L_[which_argument].Size(); + + int * row_ptr = new int[nrows + 1]; + int * col_ind = new int[nnz]; + double * values = new double[nnz]; + + int nz = 0; + int last_row = -1; + for (auto [row, col] : nonzero_entries) { + col_ind[nz] = col; + values[nz] = 0; + for (int i = last_row+1; i <= row; i++) { row_ptr[i] = nz; } + last_row = row; + nz++; + } + for (int i = last_row+1; i <= nrows; i++) { row_ptr[i] = nz; } + + for (int i = 0; i < nnz; i++) { + std::cout << col_ind[i] << " "; + } + std::cout << std::endl; + + for (int i = 0; i <= nrows; i++) { + std::cout << row_ptr[i] << " "; + } + std::cout << std::endl; + + constexpr bool sparse_matrix_frees_graph_ptrs = true; + constexpr bool sparse_matrix_frees_values_ptr = true; + constexpr bool col_ind_is_sorted = true; + A_local = mfem::SparseMatrix(row_ptr, col_ind, values, nrows, ncols, sparse_matrix_frees_graph_ptrs, sparse_matrix_frees_values_ptr, col_ind_is_sorted); }; @@ -707,7 +800,7 @@ class Functional { mfem::SparseMatrix A_local; // if A is an uninitialized HypreParMatrix - if (A.NNZ() == 0) { + if (A.Height() == 0) { // then, we need to figure out which entries of the matrix could be nonzero initialize_sparsity_pattern(A_local); @@ -725,39 +818,84 @@ class Functional { } -#if 0 - nd::array test_ids({nodes_per_test_element}); - test_el.indices(test_offsets, connectivity(elements(e)).data(), test_ids.data()); + std::map> element_gradients[Domain::num_types]; - nd::array trial_ids({nodes_per_trial_element}); - trial_el.indices(trial_offsets, connectivity(elements(e)).data(), trial_ids.data()); + for (auto & integral : form_.integrals_) { - for (uint32_t I = 0; I < nodes_per_test_element; I++) { - for (uint32_t i = 0; i < test_space.components; i++) { - int row_id = test_ids[I] * test_components + i; + auto& K_elem = element_gradients[integral.domain_.type_]; + auto& test_restrictions = form_.G_test_[integral.domain_.type_].restrictions; + auto& trial_restrictions = form_.G_trial_[integral.domain_.type_][which_argument].restrictions; - int which = row_id % nmutex; - int row_start = row_ptr[row_id]; - int row_end = row_ptr[row_id+1]; - for (uint32_t J = 0; J < nodes_per_trial_element; J++) { - for (uint32_t j = 0; j < trial_space.components; j++) { - int col_id = trial_ids[J] * trial_components + j; + if (K_elem.empty()) { + for (auto& [geom, test_restriction] : test_restrictions) { + auto& trial_restriction = trial_restrictions[geom]; - // find the position of the nonzero entry of this row with the right column - int position = std::lower_bound(&col_ind[row_start], &col_ind[row_end], col_id) - &col_ind[0]; + K_elem[geom] = ExecArray(test_restriction.num_elements, + trial_restriction.nodes_per_elem * trial_restriction.components, + test_restriction.nodes_per_elem * test_restriction.components); - values[position] += K_e(J, j, I, i); + detail::zero_out(K_elem[geom]); } } + + integral.ComputeElementGradients(K_elem, which_argument); + } - } -#endif - // now that we have a - for (auto & integral : form_.integrals_) { + int * row_ptr = A_local.GetI(); + int * col_ind = A_local.GetJ(); + double * values = A_local.GetData(); + + for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements}) { + auto& K_elem = element_gradients[type]; + auto& test_restrictions = form_.G_test_[type].restrictions; + auto& trial_restrictions = form_.G_trial_[type][which_argument].restrictions; + + if (!K_elem.empty()) { + for (auto [geom, elem_matrices] : K_elem) { + std::vector test_vdofs(test_restrictions[geom].nodes_per_elem * test_restrictions[geom].components); + std::vector trial_vdofs(trial_restrictions[geom].nodes_per_elem * trial_restrictions[geom].components); + + for (axom::IndexType e = 0; e < elem_matrices.shape()[0]; e++) { + test_restrictions[geom].GetElementVDofs(e, test_vdofs); + trial_restrictions[geom].GetElementVDofs(e, trial_vdofs); + + for (uint32_t i = 0; i < uint32_t(elem_matrices.shape()[1]); i++) { + int col = int(trial_vdofs[i].index()); + + for (uint32_t j = 0; j < uint32_t(elem_matrices.shape()[2]); j++) { + int row = int(test_vdofs[j].index()); + + for (int nz = row_ptr[row]; nz < row_ptr[row+1]; nz++) { + // TODO: replace linear search with binary search + if (col_ind[nz] == col) { + values[nz] += elem_matrices(e, i, j); + } + } + + } + } + } + } + } } + std::ofstream outfile("K_new.mtx"); + A_local.PrintMM(outfile); + outfile.close(); + + auto* R = form_.test_space_->Dof_TrueDof_Matrix(); + + auto* tmp = new mfem::HypreParMatrix(test_space_->GetComm(), test_space_->GlobalVSize(), trial_space_->GlobalVSize(), + test_space_->GetDofOffsets(), trial_space_->GetDofOffsets(), &A_local); + + auto* P = trial_space_->Dof_TrueDof_Matrix(); + + A = *mfem::RAP(R, tmp, P); + + delete tmp; + #if 0 // the CSR graph (sparsity pattern) is reusable, so we cache diff --git a/src/serac/numerics/functional/tests/functional_basic_h1_scalar.cpp b/src/serac/numerics/functional/tests/functional_basic_h1_scalar.cpp index 09d63a8bf6..bf09f22bae 100644 --- a/src/serac/numerics/functional/tests/functional_basic_h1_scalar.cpp +++ b/src/serac/numerics/functional/tests/functional_basic_h1_scalar.cpp @@ -78,6 +78,13 @@ void thermal_test_impl(std::unique_ptr& mesh) residual.AddBoundaryIntegral(Dimension{}, DependsOn<0>{}, TestThermalModelTwo{}, *mesh); double t = 0.0; +#if 1 + auto [value, dfdU] = residual(t, serac::differentiate_wrt(U)); + std::unique_ptr dfdU_matrix = assemble(dfdU); + + mfem::HypreParMatrix dfdU_new; + dfdU.assemble(dfdU_new); +#endif check_gradient(residual, t, U); } @@ -96,15 +103,15 @@ void thermal_test(std::string meshfile) } TEST(basic, thermal_tris) { thermal_test<1, 1>("/data/meshes/patch2D_tris.mesh"); } -TEST(basic, thermal_quads) { thermal_test<1, 1>("/data/meshes/patch2D_quads.mesh"); } -TEST(basic, thermal_tris_and_quads) { thermal_test<1, 1>("/data/meshes/patch2D_tris_and_quads.mesh"); } - -TEST(basic, thermal_tets) { thermal_test<1, 1>("/data/meshes/patch3D_tets.mesh"); } -TEST(basic, thermal_hexes) { thermal_test<1, 1>("/data/meshes/patch3D_hexes.mesh"); } -TEST(basic, thermal_tets_and_hexes) { thermal_test<1, 1>("/data/meshes/patch3D_tets_and_hexes.mesh"); } - -TEST(mixed, thermal_tris_and_quads) { thermal_test<2, 1>("/data/meshes/patch2D_tris_and_quads.mesh"); } -TEST(mixed, thermal_tets_and_hexes) { thermal_test<2, 1>("/data/meshes/patch3D_tets_and_hexes.mesh"); } +//TEST(basic, thermal_quads) { thermal_test<1, 1>("/data/meshes/patch2D_quads.mesh"); } +//TEST(basic, thermal_tris_and_quads) { thermal_test<1, 1>("/data/meshes/patch2D_tris_and_quads.mesh"); } +// +//TEST(basic, thermal_tets) { thermal_test<1, 1>("/data/meshes/patch3D_tets.mesh"); } +//TEST(basic, thermal_hexes) { thermal_test<1, 1>("/data/meshes/patch3D_hexes.mesh"); } +//TEST(basic, thermal_tets_and_hexes) { thermal_test<1, 1>("/data/meshes/patch3D_tets_and_hexes.mesh"); } +// +//TEST(mixed, thermal_tris_and_quads) { thermal_test<2, 1>("/data/meshes/patch2D_tris_and_quads.mesh"); } +//TEST(mixed, thermal_tets_and_hexes) { thermal_test<2, 1>("/data/meshes/patch3D_tets_and_hexes.mesh"); } int main(int argc, char* argv[]) { From 0bf223590abda09827d85561debaf49068e60601 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Fri, 4 Oct 2024 17:09:49 -0700 Subject: [PATCH 16/92] big changes to move ElementTestrictions into Domain classes --- src/serac/infrastructure/accelerator.hpp | 7 + src/serac/numerics/functional/CMakeLists.txt | 1 - .../functional/boundary_integral_kernels.hpp | 30 +- .../numerics/functional/dof_numbering.hpp | 382 ------------- src/serac/numerics/functional/domain.cpp | 14 + src/serac/numerics/functional/domain.hpp | 27 + .../functional/domain_integral_kernels.hpp | 32 +- .../functional/element_restriction.cpp | 62 +-- .../functional/element_restriction.hpp | 19 +- .../numerics/functional/finite_element.hpp | 14 + src/serac/numerics/functional/functional.hpp | 521 ++++++------------ .../numerics/functional/geometric_factors.cpp | 186 +++++-- .../numerics/functional/geometric_factors.hpp | 15 +- src/serac/numerics/functional/integral.hpp | 37 +- .../functional/tests/functional_basic_dg.cpp | 7 +- .../tests/functional_basic_h1_scalar.cpp | 30 +- 16 files changed, 439 insertions(+), 945 deletions(-) delete mode 100644 src/serac/numerics/functional/dof_numbering.hpp diff --git a/src/serac/infrastructure/accelerator.hpp b/src/serac/infrastructure/accelerator.hpp index 180416d0dd..6da2af89df 100644 --- a/src/serac/infrastructure/accelerator.hpp +++ b/src/serac/infrastructure/accelerator.hpp @@ -118,6 +118,13 @@ void zero_out(axom::Array& arr) { std::memset(arr.data(), 0, static_cast(arr.size()) * sizeof(T)); } + +/// @brief set the contents of an array to zero, byte-wise +template +void zero_out(axom::ArrayView& arr) +{ + std::memset(arr.data(), 0, static_cast(arr.size()) * sizeof(T)); +} #ifdef __CUDACC__ /// @overload template diff --git a/src/serac/numerics/functional/CMakeLists.txt b/src/serac/numerics/functional/CMakeLists.txt index bd2f124f47..cfb07ef54b 100644 --- a/src/serac/numerics/functional/CMakeLists.txt +++ b/src/serac/numerics/functional/CMakeLists.txt @@ -11,7 +11,6 @@ blt_list_append(TO functional_depends ELEMENTS blt::cuda IF ENABLE_CUDA) set(functional_headers differentiate_wrt.hpp boundary_integral_kernels.hpp - dof_numbering.hpp element_restriction.hpp geometry.hpp geometric_factors.hpp diff --git a/src/serac/numerics/functional/boundary_integral_kernels.hpp b/src/serac/numerics/functional/boundary_integral_kernels.hpp index 9af85ba8dd..a8bff44e74 100644 --- a/src/serac/numerics/functional/boundary_integral_kernels.hpp +++ b/src/serac/numerics/functional/boundary_integral_kernels.hpp @@ -161,7 +161,7 @@ template & inputs, double* outputs, const double* positions, const double* jacobians, lambda_type qf, [[maybe_unused]] derivative_type* qf_derivatives, - const int* elements, uint32_t num_elements, camp::int_seq) + uint32_t num_elements, camp::int_seq) { // mfem provides this information as opaque arrays of doubles, // so we reinterpret the pointer with @@ -185,7 +185,7 @@ void evaluation_kernel_impl(trial_element_type trial_elements, test_element, dou // batch-calculate values / derivatives of each trial space, at each quadrature point [[maybe_unused]] tuple qf_inputs = {promote_each_to_dual_when( - get(trial_elements).interpolate(get(u)[elements[e]], rule))...}; + get(trial_elements).interpolate(get(u)[e], rule))...}; // (batch) evalute the q-function at each quadrature point auto qf_outputs = batch_apply_qf(qf, t, x_e, J_e, get(qf_inputs)...); @@ -200,7 +200,7 @@ void evaluation_kernel_impl(trial_element_type trial_elements, test_element, dou } // (batch) integrate the material response against the test-space basis functions - test_element::integrate(get_value(qf_outputs), rule, &r[elements[e]]); + test_element::integrate(get_value(qf_outputs), rule, &r[e]); } } @@ -250,8 +250,7 @@ SERAC_HOST_DEVICE auto batch_apply_chain_rule(derivative_type* qf_derivatives, c * @param[in] num_elements The number of elements in the mesh */ template -void action_of_gradient_kernel(const double* dU, double* dR, derivatives_type* qf_derivatives, const int* elements, - std::size_t num_elements) +void action_of_gradient_kernel(const double* dU, double* dR, derivatives_type* qf_derivatives, std::size_t num_elements) { using test_element = finite_element; using trial_element = finite_element; @@ -266,13 +265,13 @@ void action_of_gradient_kernel(const double* dU, double* dR, derivatives_type* q // for each element in the domain for (uint32_t e = 0; e < num_elements; e++) { // (batch) interpolate each quadrature point's value - auto qf_inputs = trial_element::interpolate(du[elements[e]], rule); + auto qf_inputs = trial_element::interpolate(du[e], rule); // (batch) evalute the q-function at each quadrature point auto qf_outputs = batch_apply_chain_rule(qf_derivatives + e * nqp, qf_inputs); // (batch) integrate the material response against the test-space basis functions - test_element::integrate(qf_outputs, rule, &dr[elements[e]]); + test_element::integrate(qf_outputs, rule, &dr[e]); } } @@ -298,8 +297,7 @@ void action_of_gradient_kernel(const double* dU, double* dR, derivatives_type* q * @param[in] num_elements The number of elements in the mesh */ template -void element_gradient_kernel(ExecArrayView dK, derivatives_type* qf_derivatives, - const int* elements, std::size_t num_elements) +void element_gradient_kernel(ExecArrayView dK, derivatives_type* qf_derivatives, std::size_t num_elements) { using test_element = finite_element; using trial_element = finite_element; @@ -310,7 +308,7 @@ void element_gradient_kernel(ExecArrayView dK, d // for each element in the domain for (uint32_t e = 0; e < num_elements; e++) { - auto* output_ptr = reinterpret_cast(&dK(elements[e], 0, 0)); + auto* output_ptr = reinterpret_cast(&dK(e, 0, 0)); tensor derivatives{}; for (int q = 0; q < nquad; q++) { @@ -327,35 +325,35 @@ void element_gradient_kernel(ExecArrayView dK, d template auto evaluation_kernel(signature s, lambda_type qf, const double* positions, const double* jacobians, - std::shared_ptr qf_derivatives, const int* elements, uint32_t num_elements) + std::shared_ptr qf_derivatives, uint32_t num_elements) { auto trial_elements = trial_elements_tuple(s); auto test_element = get_test_element(s); return [=](double time, const std::vector& inputs, double* outputs, bool /* update state */) { evaluation_kernel_impl(trial_elements, test_element, time, inputs, outputs, positions, jacobians, qf, - qf_derivatives.get(), elements, num_elements, s.index_seq); + qf_derivatives.get(), num_elements, s.index_seq); }; } template std::function jacobian_vector_product_kernel( - signature, std::shared_ptr qf_derivatives, const int* elements, uint32_t num_elements) + signature, std::shared_ptr qf_derivatives, uint32_t num_elements) { return [=](const double* du, double* dr) { using test_space = typename signature::return_type; using trial_space = typename std::tuple_element::type; - action_of_gradient_kernel(du, dr, qf_derivatives.get(), elements, num_elements); + action_of_gradient_kernel(du, dr, qf_derivatives.get(), num_elements); }; } template std::function)> element_gradient_kernel( - signature, std::shared_ptr qf_derivatives, const int* elements, uint32_t num_elements) + signature, std::shared_ptr qf_derivatives, uint32_t num_elements) { return [=](ExecArrayView K_elem) { using test_space = typename signature::return_type; using trial_space = typename std::tuple_element::type; - element_gradient_kernel(K_elem, qf_derivatives.get(), elements, num_elements); + element_gradient_kernel(K_elem, qf_derivatives.get(), num_elements); }; } diff --git a/src/serac/numerics/functional/dof_numbering.hpp b/src/serac/numerics/functional/dof_numbering.hpp deleted file mode 100644 index 089e3065c3..0000000000 --- a/src/serac/numerics/functional/dof_numbering.hpp +++ /dev/null @@ -1,382 +0,0 @@ -// Copyright (c) 2019-2024, Lawrence Livermore National Security, LLC and -// other Serac Project Developers. See the top-level LICENSE file for -// details. -// -// SPDX-License-Identifier: (BSD-3-Clause) -#pragma once - -#include "mfem.hpp" - -#include "serac/infrastructure/accelerator.hpp" - -#include "serac/numerics/functional/integral.hpp" -#include "serac/numerics/functional/element_restriction.hpp" - -namespace serac { - -/** - * @brief a (poorly named) tuple of quantities used to discover the sparsity - * pattern associated with element and boundary element matrices. - * - * It stores information about how the entries of an element "stiffness" matrix - * map to the global stiffness. The operator< definition allows us to sort them - * lexicographically, to facilitate creating the CSR matrix graph. - */ -struct ElemInfo { - uint32_t global_row_; ///< The global row number - uint32_t global_col_; ///< The global column number - uint32_t local_row_; ///< The local row number - uint32_t local_col_; ///< The global column number - uint32_t element_id_; ///< The element ID - int sign_; ///< The orientation of the element - Domain::Type type; ///< Which kind of Integral this entry comes from -}; - -/** - * @brief operator for sorting lexicographically by {global_row, global_col} - * @param x the ElemInfo on the left - * @param y the ElemInfo on the right - */ -inline bool operator<(const ElemInfo& x, const ElemInfo& y) -{ - return (x.global_row_ < y.global_row_) || (x.global_row_ == y.global_row_ && x.global_col_ < y.global_col_); -} - -/** - * @brief operator determining inequality by {global_row, global_col} - * @param x the ElemInfo on the left - * @param y the ElemInfo on the right - */ -inline bool operator!=(const ElemInfo& x, const ElemInfo& y) -{ - return (x.global_row_ != y.global_row_) || (x.global_col_ != y.global_col_); -} - -/** - * @brief this type explicitly stores sign (typically used conveying edge/face orientation) and index values - * - * TODO: investigate implementation via bitfield (should have smaller memory footprint, better readability than mfem's - * {sign, index} int32_t encoding) - */ -struct SignedIndex { - /// the actual index of some quantity - uint32_t index_; - - /// whether or not the value associated with this index is positive or negative - int sign_; - - /// the implicit conversion to int extracts only the index - operator uint32_t() { return index_; } -}; - -/** - * @brief mfem will frequently encode {sign, index} into a single int32_t. - * This function decodes those values. - * - * @param i an integer that mfem has encoded to contain two separate pieces of information - */ -inline SignedIndex decodeSignedIndex(int i) -{ - return SignedIndex{static_cast((i >= 0) ? i : -1 - i), (i >= 0) ? 1 : -1}; -} - -/** - * @brief return whether or not the underlying function space is Hcurl or not - * - * @param fes the finite element space in question - */ -inline bool isHcurl(const mfem::ParFiniteElementSpace& fes) -{ - return (fes.FEColl()->GetContType() == mfem::FiniteElementCollection::TANGENTIAL); -} - -/** - * @brief return whether or not the underlying function space is L2 or not - * - * @param fes the finite element space in question - */ -inline bool isL2(const mfem::ParFiniteElementSpace& fes) -{ - return (fes.FEColl()->GetContType() == mfem::FiniteElementCollection::DISCONTINUOUS); -} - -/** - * @brief attempt to characterize which FiniteElementSpaces - * mfem::FaceRestriction actually works with - * - * @param fes the finite element space in question - */ -inline bool compatibleWithFaceRestriction(const mfem::ParFiniteElementSpace& fes) -{ - return !(isHcurl(fes) && fes.GetMesh()->Dimension() == 2) && !(isHcurl(fes) && fes.GetMesh()->Dimension() == 3) && - !(isL2(fes)) && fes.GetMesh()->GetNBE() > 0; -} - -/** - * @brief this is a (hopefully) temporary measure to work around the fact that mfem's - * support for querying information about boundary elements is inconsistent, or entirely - * unimplemented. If the finite element spaces both work with mfem::FaceRestriction, it will - * return a 3D array sized to store the boundary element gradient matrices, else the 3D array - * will have dimensions 0x0x0 to indicate that it is unused. - * - * @param trial_fes the trial finite element space - * @param test_fes the test finite element space - * - * known issues: getting dofs/ids for boundary elements in 2D w/ Hcurl spaces - * getting dofs/ids for boundary elements in 2D,3D w/ L2 spaces - */ -template -ExecArray allocateMemoryForBdrElementGradients(const mfem::ParFiniteElementSpace& trial_fes, - const mfem::ParFiniteElementSpace& test_fes) -{ - auto* test_BE = test_fes.GetBE(0); - auto* trial_BE = trial_fes.GetBE(0); - return {static_cast(trial_fes.GetNFbyType(mfem::FaceType::Boundary)), - static_cast(test_BE->GetDof() * test_fes.GetVDim()), - static_cast(trial_BE->GetDof() * trial_fes.GetVDim())}; -} - -/// @overload -template -ExecArray allocateMemoryForBdrElementGradients(const mfem::ParFiniteElementSpace& fes) -{ - if (compatibleWithFaceRestriction(fes)) { - auto* BE = fes.GetBE(0); - return {static_cast(fes.GetNFbyType(mfem::FaceType::Boundary)), - static_cast(BE->GetDof() * fes.GetVDim())}; - } else { - return {0, 0}; - } -} - -/** - * @brief this object extracts the dofs for each element in a FiniteElementSpace as a 2D array such that - * element_dofs_(e, i) will be the `i`th dof of element `e`. - * - * Note: due to an internal inconsistency between mfem::FiniteElementSpace and mfem::FaceRestriction, - * we choose to use the Restriction operator as the "source of truth", since we are also using its - * convention for quadrature point numbering. - */ - -struct DofNumbering { - /** - * @param fespace the finite element space to extract dof numbers from - * - * @brief create lookup tables of which degrees of freedom correspond to - * each element and boundary element - */ - DofNumbering(const mfem::ParFiniteElementSpace& fespace) - : element_dofs_(static_cast(fespace.GetNE()), - static_cast(fespace.GetFE(0)->GetDof() * fespace.GetVDim())), - bdr_element_dofs_(allocateMemoryForBdrElementGradients(fespace)) - { - int dim = fespace.GetMesh()->Dimension(); - mfem::Geometry::Type elem_geom[4] = {mfem::Geometry::INVALID, mfem::Geometry::SEGMENT, mfem::Geometry::SQUARE, - mfem::Geometry::CUBE}; - ElementRestriction dofs(&fespace, elem_geom[dim]); - ElementRestriction boundary_dofs(&fespace, elem_geom[dim - 1], FaceType::BOUNDARY); - - { - auto elem_restriction = fespace.GetElementRestriction(mfem::ElementDofOrdering::LEXICOGRAPHIC); - - mfem::Vector iota(elem_restriction->Width()); - mfem::Vector dof_ids(elem_restriction->Height()); - dof_ids = 0.0; - for (int i = 0; i < iota.Size(); i++) { - iota[i] = i + 1; // note: 1-based index - } - - // we're using Mult() to reveal the locations nonzero entries - // in the restriction operator, since that information is not - // made available through its public interface - // - // TODO: investigate refactoring mfem's restriction operators - // to provide this information in more natural way. - elem_restriction->Mult(iota, dof_ids); - const double* dof_ids_h = dof_ids.HostRead(); - - int index = 0; - for (axom::IndexType e = 0; e < element_dofs_.shape()[0]; e++) { - for (axom::IndexType i = 0; i < element_dofs_.shape()[1]; i++) { - uint32_t dof_id = static_cast(fabs(dof_ids_h[index])); // note: 1-based index - int dof_sign = dof_ids[index] > 0 ? +1 : -1; - element_dofs_(e, i) = {dof_id - 1, dof_sign}; // subtract 1 to get back to 0-based index - index++; - } - } - } - - if (bdr_element_dofs_.size() > 0) { - auto face_restriction = fespace.GetFaceRestriction(mfem::ElementDofOrdering::LEXICOGRAPHIC, - mfem::FaceType::Boundary, mfem::L2FaceValues::SingleValued); - - mfem::Vector iota(face_restriction->Width()); - mfem::Vector dof_ids(face_restriction->Height()); - for (int i = 0; i < iota.Size(); i++) { - iota[i] = i + 1; // note: 1-based index - } - - face_restriction->Mult(iota, dof_ids); - const double* dof_ids_h = dof_ids.HostRead(); - - int index = 0; - for (axom::IndexType e = 0; e < bdr_element_dofs_.shape()[0]; e++) { - for (axom::IndexType i = 0; i < bdr_element_dofs_.shape()[1]; i++) { - uint32_t dof_id = static_cast(fabs(dof_ids_h[index])); // note: 1-based index - int dof_sign = dof_ids[index] > 0 ? +1 : -1; - bdr_element_dofs_(e, i) = {dof_id - 1, dof_sign}; // subtract 1 to get back to 0-based index - index++; - } - } - } - } - - /// @brief element_dofs_(e, i) stores the `i`th dof of element `e`. - CPUArray element_dofs_; - - /// @brief bdr_element_dofs_(b, i) stores the `i`th dof of boundary element `b`. - CPUArray bdr_element_dofs_; -}; - -/** - * @brief this object figures out the sparsity pattern associated with a finite element discretization - * of the given test and trial function spaces, and records which nonzero each element "stiffness" - * matrix maps to, to facilitate assembling the element matrices into the global sparse matrix. e.g. - * - * element_nonzero_LUT(e, i, j) says where (in the global sparse matrix) - * to put the (i,j) component of the matrix associated with element element matrix `e` - * - * Note: due to an internal inconsistency between mfem::FiniteElementSpace and mfem::FaceRestriction, - * we choose to use the Restriction operator as the "source of truth", since we are also using its - * convention for quadrature point numbering. - */ -struct GradientAssemblyLookupTables { - /// @brief a type for representing a nonzero entry in a sparse matrix - struct Entry { - uint32_t row; ///< row value for this nonzero Entry - uint32_t column; ///< column value for this nonzero Entry - - /// operator< is used when sorting `Entry`. Lexicographical ordering - bool operator<(const Entry& other) const - { - return (row < other.row) || ((row == other.row) && (column < other.column)); - } - - /// operator== is required for use in `std::unordered_map` - bool operator==(const Entry& other) const { return (row == other.row && column == other.column); } - - /// hash functor required for use in `std::unordered_map` - struct Hasher { - /// @brief a hash function implementation for `Entry` - std::size_t operator()(const Entry& k) const - { - std::size_t seed = std::hash()(k.row); - seed ^= std::hash()(k.column) + 0x9e3779b9 + (seed << 6) + (seed >> 2); - return seed; - } - }; - }; - - /// dummy default ctor to enable deferred initialization - GradientAssemblyLookupTables() : initialized{false} {}; - - /** - * @param block_test_dofs object containing information about dofs for the test space - * @param block_trial_dofs object containing information about dofs for the trial space - * - * @brief create lookup tables describing which degrees of freedom - * correspond to each domain/boundary element - */ - void init(const serac::BlockElementRestriction& block_test_dofs, - const serac::BlockElementRestriction& block_trial_dofs) - { - // we start by having each element and boundary element emit the (i,j) entry that it - // touches in the global "stiffness matrix", and also keep track of some metadata about - // which element and which dof are associated with that particular nonzero entry - for (const auto& [geometry, trial_dofs] : block_trial_dofs.restrictions) { - const auto& test_dofs = block_test_dofs.restrictions.at(geometry); - - std::vector test_vdofs(test_dofs.nodes_per_elem * test_dofs.components); - std::vector trial_vdofs(trial_dofs.nodes_per_elem * trial_dofs.components); - - auto num_elements = static_cast(trial_dofs.num_elements); - for (uint32_t e = 0; e < num_elements; e++) { - for (uint64_t i = 0; i < uint64_t(test_dofs.dof_info.shape()[1]); i++) { - auto test_dof = test_dofs.dof_info(e, i); - - for (uint64_t j = 0; j < uint64_t(trial_dofs.dof_info.shape()[1]); j++) { - auto trial_dof = trial_dofs.dof_info(e, j); - - for (uint64_t k = 0; k < test_dofs.components; k++) { - uint32_t test_global_id = uint32_t(test_dofs.GetVDof(test_dof, k).index()); - for (uint64_t l = 0; l < trial_dofs.components; l++) { - uint32_t trial_global_id = uint32_t(trial_dofs.GetVDof(trial_dof, l).index()); - nz_LUT[{test_global_id, trial_global_id}] = 0; // just store the keys initially - } - } - } - } - } - } - - std::vector entries(nz_LUT.size()); - - uint32_t count = 0; - for (auto [key, value] : nz_LUT) { - entries[count++] = key; - } - - std::sort(entries.begin(), entries.end()); - - nnz = static_cast(nz_LUT.size()); - row_ptr.resize(static_cast(block_test_dofs.LSize() + 1)); - col_ind.resize(nnz); - - row_ptr[0] = 0; - col_ind[0] = int(entries[0].column); - - for (uint32_t i = 1; i < nnz; i++) { - nz_LUT[entries[i]] = i; - col_ind[i] = int(entries[i].column); - - // if the new entry has a different row, then the row_ptr offsets must be set as well - for (uint32_t j = entries[i - 1].row; j < entries[i].row; j++) { - row_ptr[j + 1] = int(i); - } - } - - row_ptr.back() = static_cast(nnz); - - initialized = true; - } - - /** - * @brief return the index (into the nonzero entries) corresponding to entry (i,j) - * @param i the row - * @param j the column - */ - uint32_t operator()(int i, int j) const { return nz_LUT.at({uint32_t(i), uint32_t(j)}); } - - /// @brief how many nonzero entries appear in the sparse matrix - uint32_t nnz; - - /** - * @brief array holding the offsets for a given row of the sparse matrix - * i.e. row r corresponds to the indices [row_ptr[r], row_ptr[r+1]) - */ - std::vector row_ptr; - - /// @brief array holding the column associated with each nonzero entry - std::vector col_ind; - - /** - * @brief `nz_LUT` returns the index of the `col_ind` / `value` CSR arrays - * corresponding to the (i,j) entry - */ - std::unordered_map nz_LUT; - - /// @brief specifies if the table has already been initialized or not - bool initialized; -}; - -} // namespace serac diff --git a/src/serac/numerics/functional/domain.cpp b/src/serac/numerics/functional/domain.cpp index 8385ef8d76..9a424dd9f6 100644 --- a/src/serac/numerics/functional/domain.cpp +++ b/src/serac/numerics/functional/domain.cpp @@ -453,6 +453,20 @@ mfem::Array Domain::dof_list(mfem::FiniteElementSpace* fes) const return uniq_dof_ids; } +void Domain::insert_restriction(const mfem::FiniteElementSpace * fes, FunctionSpace space) { + + // if we don't already have a BlockElementRestriction for this FunctionSpace, make one + if (restriction_operators.count(space) == 0) { + restriction_operators[space] = BlockElementRestriction(fes, *this); + } + +} + +const BlockElementRestriction & Domain::get_restriction(FunctionSpace space) { + return restriction_operators.at(space); +}; + + /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/serac/numerics/functional/domain.hpp b/src/serac/numerics/functional/domain.hpp index 072169f2ee..9408b68373 100644 --- a/src/serac/numerics/functional/domain.hpp +++ b/src/serac/numerics/functional/domain.hpp @@ -7,18 +7,24 @@ #pragma once #include + #include "mfem.hpp" #include "serac/numerics/functional/tensor.hpp" +#include "serac/numerics/functional/finite_element.hpp" +#include "serac/numerics/functional/element_restriction.hpp" namespace serac { +struct BlockElementRestriction; + /** * @brief a class for representing a geometric region that can be used for integration * * This region can be an entire mesh or some subset of its elements */ struct Domain { + /// @brief enum describing what kind of elements are included in a Domain enum Type { @@ -59,6 +65,8 @@ struct Domain { std::vector mfem_hex_ids_; /// @endcond + std::map< FunctionSpace, BlockElementRestriction > restriction_operators; + Domain(const mfem::Mesh& m, int d, Type type = Domain::Type::Elements) : mesh_(m), dim_(d), type_(type) {} /** @@ -131,8 +139,27 @@ struct Domain { exit(1); } + /// @brief get elements by geometry type + const std::vector& get_mfem_ids(mfem::Geometry::Type geom) const + { + if (geom == mfem::Geometry::SEGMENT) return mfem_edge_ids_; + if (geom == mfem::Geometry::TRIANGLE) return mfem_tri_ids_; + if (geom == mfem::Geometry::SQUARE) return mfem_quad_ids_; + if (geom == mfem::Geometry::TETRAHEDRON) return mfem_tet_ids_; + if (geom == mfem::Geometry::CUBE) return mfem_hex_ids_; + + exit(1); + } + /// @brief get mfem degree of freedom list for a given FiniteElementSpace mfem::Array dof_list(mfem::FiniteElementSpace* fes) const; + + /// @brief TODO + void insert_restriction(const mfem::FiniteElementSpace * fes, FunctionSpace space); + + /// @brief TODO + const BlockElementRestriction & get_restriction(FunctionSpace space); + }; /// @brief constructs a domain from all the elements in a mesh diff --git a/src/serac/numerics/functional/domain_integral_kernels.hpp b/src/serac/numerics/functional/domain_integral_kernels.hpp index c218025fbe..acc250ac8b 100644 --- a/src/serac/numerics/functional/domain_integral_kernels.hpp +++ b/src/serac/numerics/functional/domain_integral_kernels.hpp @@ -156,7 +156,7 @@ void evaluation_kernel_impl(trial_element_tuple trial_elements, test_element, do const std::vector& inputs, double* outputs, const double* positions, const double* jacobians, lambda_type qf, [[maybe_unused]] axom::ArrayView qf_state, - [[maybe_unused]] derivative_type* qf_derivatives, const int* elements, + [[maybe_unused]] derivative_type* qf_derivatives, uint32_t num_elements, bool update_state, camp::int_seq) { // mfem provides this information as opaque arrays of doubles, @@ -180,7 +180,7 @@ void evaluation_kernel_impl(trial_element_tuple trial_elements, test_element, do //[[maybe_unused]] static constexpr trial_element_tuple trial_element_tuple{}; // batch-calculate values / derivatives of each trial space, at each quadrature point [[maybe_unused]] tuple qf_inputs = {promote_each_to_dual_when( - get(trial_elements).interpolate(get(u)[elements[e]], rule))...}; + get(trial_elements).interpolate(get(u)[e], rule))...}; // use J_e to transform values / derivatives on the parent element // to the to the corresponding values / derivatives on the physical element @@ -213,7 +213,7 @@ void evaluation_kernel_impl(trial_element_tuple trial_elements, test_element, do } // (batch) integrate the material response against the test-space basis functions - test_element::integrate(get_value(qf_outputs), rule, &r[elements[e]]); + test_element::integrate(get_value(qf_outputs), rule, &r[e]); } return; @@ -275,9 +275,8 @@ SERAC_HOST_DEVICE auto batch_apply_chain_rule(derivative_type* qf_derivatives, c */ template -void action_of_gradient_kernel(const double* dU, double* dR, derivatives_type* qf_derivatives, const int* elements, - std::size_t num_elements) -{ +void action_of_gradient_kernel(const double* dU, double* dR, derivatives_type* qf_derivatives, std::size_t num_elements) { + using test_element = finite_element; using trial_element = finite_element; @@ -293,13 +292,13 @@ void action_of_gradient_kernel(const double* dU, double* dR, derivatives_type* q // for each element in the domain for (uint32_t e = 0; e < num_elements; e++) { // (batch) interpolate each quadrature point's value - auto qf_inputs = trial_element::interpolate(du[elements[e]], rule); + auto qf_inputs = trial_element::interpolate(du[e], rule); // (batch) evalute the q-function at each quadrature point auto qf_outputs = batch_apply_chain_rule(qf_derivatives + e * num_qpts, qf_inputs); // (batch) integrate the material response against the test-space basis functions - test_element::integrate(qf_outputs, rule, &dr[elements[e]]); + test_element::integrate(qf_outputs, rule, &dr[e]); } } @@ -325,8 +324,7 @@ void action_of_gradient_kernel(const double* dU, double* dR, derivatives_type* q * @param[in] num_elements The number of elements in the mesh */ template -void element_gradient_kernel(ExecArrayView dK, derivatives_type* qf_derivatives, - const int* elements, std::size_t num_elements) +void element_gradient_kernel(ExecArrayView dK, derivatives_type* qf_derivatives, std::size_t num_elements) { // quantities of interest have no flux term, so we pad the derivative // tuple with a "zero" type in the second position to treat it like the standard case @@ -342,7 +340,7 @@ void element_gradient_kernel(ExecArrayView dK, d // for each element in the domain for (uint32_t e = 0; e < num_elements; e++) { - auto* output_ptr = reinterpret_cast(&dK(elements[e], 0, 0)); + auto* output_ptr = reinterpret_cast(&dK(e, 0, 0)); tensor derivatives{}; for (int q = 0; q < nquad; q++) { @@ -364,36 +362,36 @@ template auto evaluation_kernel(signature s, const lambda_type& qf, const double* positions, const double* jacobians, std::shared_ptr> qf_state, - std::shared_ptr qf_derivatives, const int* elements, uint32_t num_elements) + std::shared_ptr qf_derivatives, uint32_t num_elements) { auto trial_elements = trial_elements_tuple(s); auto test_element = get_test_element(s); return [=](double time, const std::vector& inputs, double* outputs, bool update_state) { domain_integral::evaluation_kernel_impl( trial_elements, test_element, time, inputs, outputs, positions, jacobians, qf, (*qf_state)[geom], - qf_derivatives.get(), elements, num_elements, update_state, s.index_seq); + qf_derivatives.get(), num_elements, update_state, s.index_seq); }; } template std::function jacobian_vector_product_kernel( - signature, std::shared_ptr qf_derivatives, const int* elements, uint32_t num_elements) + signature, std::shared_ptr qf_derivatives, uint32_t num_elements) { return [=](const double* du, double* dr) { using test_space = typename signature::return_type; using trial_space = typename std::tuple_element::type; - action_of_gradient_kernel(du, dr, qf_derivatives.get(), elements, num_elements); + action_of_gradient_kernel(du, dr, qf_derivatives.get(), num_elements); }; } template std::function)> element_gradient_kernel( - signature, std::shared_ptr qf_derivatives, const int* elements, uint32_t num_elements) + signature, std::shared_ptr qf_derivatives, uint32_t num_elements) { return [=](ExecArrayView K_elem) { using test_space = typename signature::return_type; using trial_space = typename std::tuple_element::type; - element_gradient_kernel(K_elem, qf_derivatives.get(), elements, num_elements); + element_gradient_kernel(K_elem, qf_derivatives.get(), num_elements); }; } diff --git a/src/serac/numerics/functional/element_restriction.cpp b/src/serac/numerics/functional/element_restriction.cpp index af1f95b922..73e1c8e47e 100644 --- a/src/serac/numerics/functional/element_restriction.cpp +++ b/src/serac/numerics/functional/element_restriction.cpp @@ -587,21 +587,7 @@ ElementRestriction::ElementRestriction(const mfem::FiniteElementSpace* fes, mfem ordering = fes->GetOrdering(); - lsize = uint64_t(fes->GetVSize()); - components = uint64_t(fes->GetVDim()); - num_nodes = lsize / components; - num_elements = uint64_t(dof_info.shape()[0]); - nodes_per_elem = uint64_t(dof_info.shape()[1]); - esize = num_elements * nodes_per_elem * components; - - -} - -ElementRestriction::ElementRestriction(const mfem::FiniteElementSpace* fes, mfem::Geometry::Type elem_geom) -{ - dof_info = GetElementRestriction(fes, elem_geom); - - ordering = fes->GetOrdering(); + element_ids = elem_ids; lsize = uint64_t(fes->GetVSize()); components = uint64_t(fes->GetVDim()); @@ -609,21 +595,7 @@ ElementRestriction::ElementRestriction(const mfem::FiniteElementSpace* fes, mfem num_elements = uint64_t(dof_info.shape()[0]); nodes_per_elem = uint64_t(dof_info.shape()[1]); esize = num_elements * nodes_per_elem * components; -} - -ElementRestriction::ElementRestriction(const mfem::FiniteElementSpace* fes, mfem::Geometry::Type face_geom, - FaceType type) -{ - dof_info = GetFaceDofs(fes, face_geom, type); - ordering = fes->GetOrdering(); - - lsize = uint64_t(fes->GetVSize()); - components = uint64_t(fes->GetVDim()); - num_nodes = lsize / components; - num_elements = uint64_t(dof_info.shape()[0]); - nodes_per_elem = uint64_t(dof_info.shape()[1]); - esize = num_elements * nodes_per_elem * components; } uint64_t ElementRestriction::ESize() const { return esize; } @@ -702,38 +674,6 @@ BlockElementRestriction::BlockElementRestriction(const mfem::FiniteElementSpace* } -BlockElementRestriction::BlockElementRestriction(const mfem::FiniteElementSpace* fes) -{ - int dim = fes->GetMesh()->Dimension(); - - if (dim == 2) { - for (auto geom : {mfem::Geometry::TRIANGLE, mfem::Geometry::SQUARE}) { - restrictions[geom] = ElementRestriction(fes, geom); - } - } - - if (dim == 3) { - for (auto geom : {mfem::Geometry::TETRAHEDRON, mfem::Geometry::CUBE}) { - restrictions[geom] = ElementRestriction(fes, geom); - } - } -} - -BlockElementRestriction::BlockElementRestriction(const mfem::FiniteElementSpace* fes, FaceType type) -{ - int dim = fes->GetMesh()->Dimension(); - - if (dim == 2) { - restrictions[mfem::Geometry::SEGMENT] = ElementRestriction(fes, mfem::Geometry::SEGMENT, type); - } - - if (dim == 3) { - for (auto geom : {mfem::Geometry::TRIANGLE, mfem::Geometry::SQUARE}) { - restrictions[geom] = ElementRestriction(fes, geom, type); - } - } -} - uint64_t BlockElementRestriction::ESize() const { uint64_t total = 0; for (auto & [geom, restriction] : restrictions) { diff --git a/src/serac/numerics/functional/element_restriction.hpp b/src/serac/numerics/functional/element_restriction.hpp index ded9712a9f..ff96e7cd9d 100644 --- a/src/serac/numerics/functional/element_restriction.hpp +++ b/src/serac/numerics/functional/element_restriction.hpp @@ -142,19 +142,15 @@ struct Array2D { namespace serac { +struct Domain; + /// a more complete version of mfem::ElementRestriction that works with {H1, Hcurl, L2} spaces (including on the /// boundary) struct ElementRestriction { /// default ctor leaves this object uninitialized ElementRestriction() {} - ElementRestriction(const mfem::FiniteElementSpace* fes, mfem::Geometry::Type elem_geom, const std::vector & domain); - - /// create an ElementRestriction for all domain-type (geom dim == spatial dim) elements of the specified geometry - ElementRestriction(const mfem::FiniteElementSpace* fes, mfem::Geometry::Type elem_geom); - - /// create an ElementRestriction for all face-type (geom dim == spatial dim) elements of the specified geometry - ElementRestriction(const mfem::FiniteElementSpace* fes, mfem::Geometry::Type face_geom, FaceType type); + ElementRestriction(const mfem::FiniteElementSpace* fes, mfem::Geometry::Type elem_geom, const std::vector & domain_elements); /// the size of the "E-vector" associated with this restriction operator uint64_t ESize() const; @@ -197,6 +193,9 @@ struct ElementRestriction { /// the number of nodes in each element uint64_t nodes_per_elem; + /// an array mapping from domain element ids [0, num_elements) to + std::vector element_ids; + /// a 2D array (num_elements-by-nodes_per_elem) holding the dof info extracted from the finite element space axom::Array dof_info; @@ -216,12 +215,6 @@ struct BlockElementRestriction { /// create a BlockElementRestriction for the elements in a given domain BlockElementRestriction(const mfem::FiniteElementSpace* fes, const Domain & domain); - /// create a BlockElementRestriction for all domain-elements (geom dim == spatial dim) - BlockElementRestriction(const mfem::FiniteElementSpace* fes); - - /// create a BlockElementRestriction for all face-elements (geom dim + 1 == spatial dim) - BlockElementRestriction(const mfem::FiniteElementSpace* fes, FaceType type); - /// the size of the "E-vector" associated with this restriction operator uint64_t ESize() const; diff --git a/src/serac/numerics/functional/finite_element.hpp b/src/serac/numerics/functional/finite_element.hpp index 12b87f6843..5eded85753 100644 --- a/src/serac/numerics/functional/finite_element.hpp +++ b/src/serac/numerics/functional/finite_element.hpp @@ -232,6 +232,20 @@ struct QOI { static constexpr Family family = Family::QOI; ///< the family of the basis functions }; +struct FunctionSpace { + Family family; + int order; + int components; + + std::tuple as_tuple() const { + return std::tuple(int(family), order, components); + } + + bool operator<(FunctionSpace other) const { + return this->as_tuple() < other.as_tuple(); + } +}; + /** * @brief transform information in the parent space (i.e. values and derivatives w.r.t {xi, eta, zeta}) * into the physical space (i.e. values and derivatives w.r.t. {x, y, z}) diff --git a/src/serac/numerics/functional/functional.hpp b/src/serac/numerics/functional/functional.hpp index 2e5d681492..92de2e870c 100644 --- a/src/serac/numerics/functional/functional.hpp +++ b/src/serac/numerics/functional/functional.hpp @@ -20,13 +20,14 @@ #include "serac/numerics/functional/quadrature.hpp" #include "serac/numerics/functional/finite_element.hpp" #include "serac/numerics/functional/integral.hpp" -#include "serac/numerics/functional/dof_numbering.hpp" #include "serac/numerics/functional/differentiate_wrt.hpp" #include "serac/numerics/functional/element_restriction.hpp" #include "serac/numerics/functional/domain.hpp" +#include "serac/infrastructure/mpi_fstream.hpp" + #include #include @@ -218,54 +219,33 @@ class Functional { */ Functional(const mfem::ParFiniteElementSpace* test_fes, std::array trial_fes) - : update_qdata_(false), test_space_(test_fes), trial_space_(trial_fes) + : update_qdata_(false), test_space_(test_fes), trial_space_(trial_fes), mem_type(mfem::Device::GetMemoryType()) { SERAC_MARK_FUNCTION; - auto mem_type = mfem::Device::GetMemoryType(); - - for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements, Domain::Type::InteriorFaces}) { - input_E_[type].resize(num_trial_spaces); - } - for (uint32_t i = 0; i < num_trial_spaces; i++) { P_trial_[i] = trial_space_[i]->GetProlongationMatrix(); input_L_[i].SetSize(P_trial_[i]->Height(), mfem::Device::GetMemoryType()); - // L->E - for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements, Domain::Type::InteriorFaces}) { - if (type == Domain::Type::Elements) { - G_trial_[type][i] = BlockElementRestriction(trial_fes[i]); - } - if (type == Domain::Type::BoundaryElements) { - G_trial_[type][i] = BlockElementRestriction(trial_fes[i], FaceType::BOUNDARY); - } - if (type == Domain::Type::InteriorFaces) { - G_trial_[type][i] = BlockElementRestriction(trial_fes[i], FaceType::INTERIOR); - } - - // note: we have to use "Update" here, as mfem::BlockVector's - // copy assignment ctor (operator=) doesn't let you make changes - // to the block size - input_E_[type][i].Update(G_trial_[type][i].bOffsets(), mem_type); - } + // create the necessary number of empty mfem::Vectors, to be resized later + input_E_.push_back({}); + input_E_buffer_.push_back({}); } - for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements, Domain::Type::InteriorFaces}) { - if (type == Domain::Type::Elements) { - G_test_[type] = BlockElementRestriction(test_fes); - } - if (type == Domain::Type::BoundaryElements) { - G_test_[type] = BlockElementRestriction(test_fes, FaceType::BOUNDARY); - } - if (type == Domain::Type::InteriorFaces) { - G_test_[type] = BlockElementRestriction(test_fes, FaceType::INTERIOR); - } + test_function_space_ = {test::family, test::order, test::components}; - output_E_[type].Update(G_test_[type].bOffsets(), mem_type); + std::array trial_families = {trials::family ...}; + std::array trial_orders = {trials::order ...}; + std::array trial_components = {trials::components ...}; + for (uint32_t i = 0; i < num_trial_spaces; i++) { + trial_function_spaces_[i] = {trial_families[i], trial_orders[i], trial_components[i]}; } + //for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements, Domain::Type::InteriorFaces}) { + // output_E_[type].Update(G_test_[type].bOffsets(), mem_type); + //} + P_test_ = test_space_->GetProlongationMatrix(); output_L_.SetSize(P_test_->Height(), mem_type); @@ -278,6 +258,7 @@ class Functional { for (uint32_t i = 0; i < num_trial_spaces; i++) { grad_.emplace_back(*this, i); } + } /** @@ -291,23 +272,6 @@ class Functional { * and @a spatial_dim template parameter * @param[inout] qdata The data for each quadrature point */ - template - void AddDomainIntegral(Dimension, DependsOn, const Integrand& integrand, mfem::Mesh& domain, - std::shared_ptr> qdata = NoQData) - { - if (domain.GetNE() == 0) return; - - SLIC_ERROR_ROOT_IF(dim != domain.Dimension(), "invalid mesh dimension for domain integral"); - - check_for_unsupported_elements(domain); - check_for_missing_nodal_gridfunc(domain); - - using signature = test(decltype(serac::type(trial_spaces))...); - integrals_.push_back( - MakeDomainIntegral(EntireDomain(domain), integrand, qdata, std::vector{args...})); - } - - /// @overload template void AddDomainIntegral(Dimension, DependsOn, const lambda& integrand, Domain& domain, std::shared_ptr> qdata = NoQData) @@ -319,6 +283,12 @@ class Functional { check_for_unsupported_elements(domain.mesh_); check_for_missing_nodal_gridfunc(domain.mesh_); + std::vector< uint32_t > arg_vec = {args ...}; + for (uint32_t i : arg_vec) { + domain.insert_restriction(trial_space_[i], trial_function_spaces_[i]); + } + domain.insert_restriction(test_space_, test_function_space_); + using signature = test(decltype(serac::type(trial_spaces))...); integrals_.push_back( MakeDomainIntegral(domain, integrand, qdata, std::vector{args...})); @@ -333,22 +303,8 @@ class Functional { * @note The @p Dimension parameters are used to assist in the deduction of the @a geometry_dim * and @a spatial_dim template parameter */ - template - void AddBoundaryIntegral(Dimension, DependsOn, const Integrand& integrand, mfem::Mesh& domain) - { - auto num_bdr_elements = domain.GetNBE(); - if (num_bdr_elements == 0) return; - - check_for_missing_nodal_gridfunc(domain); - - using signature = test(decltype(serac::type(trial_spaces))...); - integrals_.push_back( - MakeBoundaryIntegral(EntireBoundary(domain), integrand, std::vector{args...})); - } - - /// @overload template - void AddBoundaryIntegral(Dimension, DependsOn, const lambda& integrand, const Domain& domain) + void AddBoundaryIntegral(Dimension, DependsOn, const lambda& integrand, Domain& domain) { auto num_bdr_elements = domain.mesh_.GetNBE(); if (num_bdr_elements == 0) return; @@ -357,6 +313,12 @@ class Functional { check_for_missing_nodal_gridfunc(domain.mesh_); + std::vector< uint32_t > arg_vec = {args ...}; + for (uint32_t i : arg_vec) { + domain.insert_restriction(trial_space_[i], trial_function_spaces_[i]); + } + domain.insert_restriction(test_space_, test_function_space_); + using signature = test(decltype(serac::type(trial_spaces))...); integrals_.push_back(MakeBoundaryIntegral(domain, integrand, std::vector{args...})); } @@ -365,13 +327,19 @@ class Functional { * @brief TODO */ template - void AddInteriorFaceIntegral(Dimension, DependsOn, const Integrand& integrand, mfem::Mesh& mesh) + void AddInteriorFaceIntegral(Dimension, DependsOn, const Integrand& integrand, Domain& domain) { - check_for_missing_nodal_gridfunc(mesh); + check_for_missing_nodal_gridfunc(domain.mesh_); + + std::vector< uint32_t > arg_vec = {args ...}; + for (uint32_t i : arg_vec) { + domain.insert_restriction(trial_space_[i], trial_function_spaces_[i]); + } + domain.insert_restriction(test_space_, test_function_space_); using signature = test(decltype(serac::type(trial_spaces))...); integrals_.push_back( - MakeInteriorFaceIntegral(InteriorFaces(mesh), integrand, std::vector{args...})); + MakeInteriorFaceIntegral(domain, integrand, std::vector{args...})); } /** @@ -384,7 +352,7 @@ class Functional { * @param[inout] data The data for each quadrature point */ template - void AddAreaIntegral(DependsOn which_args, const lambda& integrand, mfem::Mesh& domain, + void AddAreaIntegral(DependsOn which_args, const lambda& integrand, Domain& domain, std::shared_ptr> data = NoQData) { AddDomainIntegral(Dimension<2>{}, which_args, integrand, domain, data); @@ -400,7 +368,7 @@ class Functional { * @param[inout] data The data for each quadrature point */ template - void AddVolumeIntegral(DependsOn which_args, const lambda& integrand, mfem::Mesh& domain, + void AddVolumeIntegral(DependsOn which_args, const lambda& integrand, Domain& domain, std::shared_ptr> data = NoQData) { AddDomainIntegral(Dimension<3>{}, which_args, integrand, domain, data); @@ -408,7 +376,7 @@ class Functional { /// @brief alias for Functional::AddBoundaryIntegral(Dimension<2>{}, integrand, domain); template - void AddSurfaceIntegral(DependsOn which_args, const lambda& integrand, mfem::Mesh& domain) + void AddSurfaceIntegral(DependsOn which_args, const lambda& integrand, Domain& domain) { AddBoundaryIntegral(Dimension<2>{}, which_args, integrand, domain); } @@ -430,22 +398,21 @@ class Functional { output_L_ = 0.0; - // this is used to mark when gather operations have been performed, - // to avoid doing them more than once per trial space - bool already_computed[Domain::num_types]{}; // default initializes to `false` - for (auto& integral : integrals_) { - auto type = integral.domain_.type_; + Domain & dom = integral.domain_; - if (!already_computed[type]) { - G_trial_[type][which].Gather(input_L_[which], input_E_[type][which]); - already_computed[type] = true; - } + const serac::BlockElementRestriction & G_trial = dom.get_restriction(trial_function_spaces_[which]); + input_E_buffer_[which].SetSize(int(G_trial.ESize())); + input_E_[which].Update(input_E_buffer_[which], G_trial.bOffsets()); + G_trial.Gather(input_L_[which], input_E_[which]); - integral.GradientMult(input_E_[type][which], output_E_[type], which); + const serac::BlockElementRestriction & G_test = dom.get_restriction(test_function_space_); + output_E_buffer_.SetSize(int(G_test.ESize())); + output_E_.Update(output_E_buffer_, G_test.bOffsets()); + integral.GradientMult(input_E_[which], output_E_, which); // scatter-add to compute residuals on the local processor - G_test_[type].ScatterAdd(output_E_[type], output_L_); + G_test.ScatterAdd(output_E_, output_L_); } // scatter-add to compute global residuals @@ -476,24 +443,25 @@ class Functional { output_L_ = 0.0; - // this is used to mark when operations have been performed, - // to avoid doing them more than once - bool already_computed[Domain::num_types][num_trial_spaces]{}; // default initializes to `false` - for (auto& integral : integrals_) { - auto type = integral.domain_.type_; + Domain & dom = integral.domain_; + + const serac::BlockElementRestriction & G_test = dom.get_restriction(test_function_space_); for (auto i : integral.active_trial_spaces_) { - if (!already_computed[type][i]) { - G_trial_[type][i].Gather(input_L_[i], input_E_[type][i]); - already_computed[type][i] = true; - } + const serac::BlockElementRestriction & G_trial = dom.get_restriction(trial_function_spaces_[i]); + input_E_buffer_[i].SetSize(int(G_trial.ESize())); + input_E_[i].Update(input_E_buffer_[i], G_trial.bOffsets()); + G_trial.Gather(input_L_[i], input_E_[i]); } - integral.Mult(t, input_E_[type], output_E_[type], wrt, update_qdata_); + output_E_buffer_.SetSize(int(G_test.ESize())); + output_E_.Update(output_E_buffer_, G_test.bOffsets()); + integral.Mult(t, input_E_, output_E_, wrt, update_qdata_); // scatter-add to compute residuals on the local processor - G_test_[type].ScatterAdd(output_E_[type], output_L_); + mfem::BlockVector output_EBV(output_E_, G_test.bOffsets()); + G_test.ScatterAdd(output_EBV, output_L_); } // scatter-add to compute global residuals @@ -508,6 +476,7 @@ class Functional { // e.g. auto [value, gradient_wrt_arg1] = my_functional(arg0, differentiate_wrt(arg1)); return {output_T_, grad_[wrt]}; } + if constexpr (wrt == NO_DIFFERENTIATION) { // if the user passes only `mfem::Vector`s then we assume they only want the output value // @@ -586,135 +555,19 @@ class Functional { return df_; } - /// @brief assemble element matrices and form an mfem::HypreParMatrix - std::unique_ptr assemble() - { - // the CSR graph (sparsity pattern) is reusable, so we cache - // that and ask mfem to not free that memory in ~SparseMatrix() - constexpr bool sparse_matrix_frees_graph_ptrs = false; - - // the CSR values are NOT reusable, so we pass ownership of - // them to the mfem::SparseMatrix, to be freed in ~SparseMatrix() - constexpr bool sparse_matrix_frees_values_ptr = true; - - constexpr bool col_ind_is_sorted = true; - - if (!lookup_tables.initialized) { - lookup_tables.init(form_.G_test_[Domain::Type::Elements], - form_.G_trial_[Domain::Type::Elements][which_argument]); - } - - double* values = new double[lookup_tables.nnz]{}; - - std::map> element_gradients[Domain::num_types]; - - for (auto& integral : form_.integrals_) { - auto& K_elem = element_gradients[integral.domain_.type_]; - auto& test_restrictions = form_.G_test_[integral.domain_.type_].restrictions; - auto& trial_restrictions = form_.G_trial_[integral.domain_.type_][which_argument].restrictions; - - if (K_elem.empty()) { - for (auto& [geom, test_restriction] : test_restrictions) { - auto& trial_restriction = trial_restrictions[geom]; - - K_elem[geom] = ExecArray(test_restriction.num_elements, - trial_restriction.nodes_per_elem * trial_restriction.components, - test_restriction.nodes_per_elem * test_restriction.components); - - detail::zero_out(K_elem[geom]); - } - } - - integral.ComputeElementGradients(K_elem, which_argument); - } - - for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements}) { - auto& K_elem = element_gradients[type]; - auto& test_restrictions = form_.G_test_[type].restrictions; - auto& trial_restrictions = form_.G_trial_[type][which_argument].restrictions; - - if (!K_elem.empty()) { - for (auto [geom, elem_matrices] : K_elem) { - std::vector test_vdofs(test_restrictions[geom].nodes_per_elem * test_restrictions[geom].components); - std::vector trial_vdofs(trial_restrictions[geom].nodes_per_elem * trial_restrictions[geom].components); - - for (axom::IndexType e = 0; e < elem_matrices.shape()[0]; e++) { - test_restrictions[geom].GetElementVDofs(e, test_vdofs); - trial_restrictions[geom].GetElementVDofs(e, trial_vdofs); - - for (uint32_t i = 0; i < uint32_t(elem_matrices.shape()[1]); i++) { - int col = int(trial_vdofs[i].index()); - - for (uint32_t j = 0; j < uint32_t(elem_matrices.shape()[2]); j++) { - int row = int(test_vdofs[j].index()); - - int sign = test_vdofs[j].sign() * trial_vdofs[i].sign(); - - // note: col / row appear backwards here, because the element matrix kernel - // is actually transposed, as a result of being row-major storage. - // - // This is kind of confusing, and will be fixed in a future refactor - // of the element gradient kernel implementation - [[maybe_unused]] auto nz = lookup_tables(row, col); - values[lookup_tables(row, col)] += sign * elem_matrices(e, i, j); - } - } - } - } - } - - } - - for (int i = 0; i < lookup_tables.col_ind.size(); i++) { - std::cout << lookup_tables.col_ind[i] << " "; - } - std::cout << std::endl; - - for (int i = 0; i < lookup_tables.row_ptr.size(); i++) { - std::cout << lookup_tables.row_ptr[i] << " "; - } - std::cout << std::endl; - - - // Copy the column indices to an auxilliary array as MFEM can mutate these during HypreParMatrix construction - col_ind_copy_ = lookup_tables.col_ind; - - auto J_local = - mfem::SparseMatrix(lookup_tables.row_ptr.data(), col_ind_copy_.data(), values, form_.output_L_.Size(), - form_.input_L_[which_argument].Size(), sparse_matrix_frees_graph_ptrs, - sparse_matrix_frees_values_ptr, col_ind_is_sorted); - - std::ofstream outfile("K_old.mtx"); - J_local.PrintMM(outfile); - outfile.close(); - - auto* R = form_.test_space_->Dof_TrueDof_Matrix(); - - auto* A = - new mfem::HypreParMatrix(test_space_->GetComm(), test_space_->GlobalVSize(), trial_space_->GlobalVSize(), - test_space_->GetDofOffsets(), trial_space_->GetDofOffsets(), &J_local); - - auto* P = trial_space_->Dof_TrueDof_Matrix(); - - std::unique_ptr K(mfem::RAP(R, A, P)); - - delete A; - - return K; - }; - - void initialize_sparsity_pattern(mfem::SparseMatrix & A_local) { +#if 1 + void initialize_sparsity_pattern() { using row_col = std::tuple; std::set< row_col > nonzero_entries; for (auto& integral : form_.integrals_) { - auto& test_restrictions = form_.G_test_[integral.domain_.type_].restrictions; - auto& trial_restrictions = form_.G_trial_[integral.domain_.type_][which_argument].restrictions; - - for (auto& [geom, test_restriction] : test_restrictions) { - auto& trial_restriction = trial_restrictions[geom]; + Domain & dom = integral.domain_; + const auto& G_test = dom.get_restriction(form_.test_function_space_); + const auto& G_trial = dom.get_restriction(form_.trial_function_spaces_[which_argument]); + for (const auto& [geom, test_restriction] : G_test.restrictions) { + const auto& trial_restriction = G_trial.restrictions.at(geom); // the degrees of freedom associated with the rows/columns of the e^th element stiffness matrix std::vector test_vdofs(test_restriction.nodes_per_elem * test_restriction.components); @@ -726,21 +579,16 @@ class Functional { for (uint32_t i = 0; i < test_restriction.nodes_per_elem; i++) { auto test_dof = test_restriction.dof_info(e, i); for (uint32_t j = 0; j < test_restriction.components; j++) { - test_vdofs[i * test_restriction.components + j] = test_restriction.GetVDof(test_dof, j).index(); - std::cout << test_restriction.GetVDof(test_dof, j).index() << " "; + test_vdofs[i * test_restriction.components + j] = int(test_restriction.GetVDof(test_dof, j).index()); } } - std::cout << std::endl; for (uint32_t i = 0; i < trial_restriction.nodes_per_elem; i++) { auto trial_dof = trial_restriction.dof_info(e, i); for (uint32_t j = 0; j < trial_restriction.components; j++) { - trial_vdofs[i * trial_restriction.components + j] = trial_restriction.GetVDof(trial_dof, j).index(); - std::cout << trial_restriction.GetVDof(trial_dof, j).index() << " "; + trial_vdofs[i * trial_restriction.components + j] = int(trial_restriction.GetVDof(trial_dof, j).index()); } } - std::cout << std::endl; - std::cout << std::endl; for (int row : test_vdofs) { for (int col : trial_vdofs) { @@ -751,153 +599,141 @@ class Functional { } } - int nnz = nonzero_entries.size(); + uint64_t nnz = nonzero_entries.size(); int nrows = form_.output_L_.Size(); - int ncols = form_.input_L_[which_argument].Size(); - int * row_ptr = new int[nrows + 1]; - int * col_ind = new int[nnz]; - double * values = new double[nnz]; + row_ptr.resize(nrows + 1); + col_ind.resize(nnz); int nz = 0; int last_row = -1; for (auto [row, col] : nonzero_entries) { col_ind[nz] = col; - values[nz] = 0; for (int i = last_row+1; i <= row; i++) { row_ptr[i] = nz; } last_row = row; nz++; } for (int i = last_row+1; i <= nrows; i++) { row_ptr[i] = nz; } + }; - for (int i = 0; i < nnz; i++) { - std::cout << col_ind[i] << " "; + uint64_t max_buffer_size() { + uint64_t max_entries = 0; + for (auto & integral : form_.integrals_) { + Domain & dom = integral.domain_; + const auto& G_test = dom.get_restriction(form_.test_function_space_); + const auto& G_trial = dom.get_restriction(form_.trial_function_spaces_[which_argument]); + for (const auto& [geom, test_restriction] : G_test.restrictions) { + const auto& trial_restriction = G_trial.restrictions.at(geom); + uint64_t nrows_per_element = test_restriction.nodes_per_elem * test_restriction.components; + uint64_t ncols_per_element = trial_restriction.nodes_per_elem * trial_restriction.components; + uint64_t entries_per_element = nrows_per_element * ncols_per_element; + uint64_t entries_needed = test_restriction.num_elements * entries_per_element; + max_entries = std::max(entries_needed, max_entries); + } } - std::cout << std::endl; + return max_entries; + } + + std::unique_ptr assemble() { - for (int i = 0; i <= nrows; i++) { - std::cout << row_ptr[i] << " "; + if (row_ptr.empty()) { + initialize_sparsity_pattern(); } - std::cout << std::endl; - constexpr bool sparse_matrix_frees_graph_ptrs = true; - constexpr bool sparse_matrix_frees_values_ptr = true; + // since we own the storage for row_ptr, col_ind, values, + // we ask mfem to not deallocate those pointers in the SparseMatrix dtor + constexpr bool sparse_matrix_frees_graph_ptrs = false; + constexpr bool sparse_matrix_frees_values_ptr = false; constexpr bool col_ind_is_sorted = true; - A_local = mfem::SparseMatrix(row_ptr, col_ind, values, nrows, ncols, sparse_matrix_frees_graph_ptrs, sparse_matrix_frees_values_ptr, col_ind_is_sorted); - - }; - - /** - * @brief assemble element matrices and return an mfem::HypreParMatrix by-reference - * - * @note: if A is empty, its nonzero sparsity will be determined automatically, but - * if A is nonempty, then we assume its sparsity pattern - * has all of the necessary nonzero entries (which will be true after passing - * in an empty HypreParMatrix to this function). - */ - void assemble(mfem::HypreParMatrix & A) { - mfem::SparseMatrix A_local; - - // if A is an uninitialized HypreParMatrix - if (A.Height() == 0) { - - // then, we need to figure out which entries of the matrix could be nonzero - initialize_sparsity_pattern(A_local); - - } else { - - // otherwise, we can reuse the information in the provided HypreParMatrix - A.GetDiag(A_local); - - // except there is one catch: HypreParMatrix puts the diagonal entries - // first in each row, so we need to "sort" each row again so that we can - // do binary search later on in the actual assembly step - - // TODO - - } - - std::map> element_gradients[Domain::num_types]; + // note: we make a copy of col_ind since mfem::HypreParMatrix + // changes it in the constructor + std::vector col_ind_copy = col_ind; + + int nnz = row_ptr.back(); + std::vector values(nnz, 0.0); + auto A_local = mfem::SparseMatrix( + row_ptr.data(), + col_ind_copy.data(), + values.data(), + form_.output_L_.Size(), + form_.input_L_[which_argument].Size(), + sparse_matrix_frees_graph_ptrs, + sparse_matrix_frees_values_ptr, + col_ind_is_sorted + ); + + std::vector K_elem_buffer(max_buffer_size()); for (auto & integral : form_.integrals_) { - auto& K_elem = element_gradients[integral.domain_.type_]; - auto& test_restrictions = form_.G_test_[integral.domain_.type_].restrictions; - auto& trial_restrictions = form_.G_trial_[integral.domain_.type_][which_argument].restrictions; + Domain & dom = integral.domain_; - if (K_elem.empty()) { - for (auto& [geom, test_restriction] : test_restrictions) { - auto& trial_restriction = trial_restrictions[geom]; + // if this integral's derivative isn't identically zero + if (integral.functional_to_integral_index_.count(which_argument) > 0) { - K_elem[geom] = ExecArray(test_restriction.num_elements, - trial_restriction.nodes_per_elem * trial_restriction.components, - test_restriction.nodes_per_elem * test_restriction.components); + int id = integral.functional_to_integral_index_.at(which_argument); + const auto& G_test = dom.get_restriction(form_.test_function_space_); + const auto& G_trial = dom.get_restriction(form_.trial_function_spaces_[which_argument]); + for (const auto& [geom, calculate_element_matrices_func] : integral.element_gradient_[id]) { - detail::zero_out(K_elem[geom]); - } - } + const auto& test_restriction = G_test.restrictions.at(geom); + const auto& trial_restriction = G_trial.restrictions.at(geom); - integral.ComputeElementGradients(K_elem, which_argument); + // prepare a buffer to hold the element matrices + CPUArrayView K_e(K_elem_buffer.data(), + test_restriction.num_elements, + trial_restriction.nodes_per_elem * trial_restriction.components, + test_restriction.nodes_per_elem * test_restriction.components); + detail::zero_out(K_e); - } + // perform the actual calculations + calculate_element_matrices_func(K_e); - int * row_ptr = A_local.GetI(); - int * col_ind = A_local.GetJ(); - double * values = A_local.GetData(); + const std::vector & element_ids = integral.domain_.get(geom); - for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements}) { - auto& K_elem = element_gradients[type]; - auto& test_restrictions = form_.G_test_[type].restrictions; - auto& trial_restrictions = form_.G_trial_[type][which_argument].restrictions; + uint32_t rows_per_elem = uint32_t(test_restriction.nodes_per_elem * test_restriction.components); + uint32_t cols_per_elem = uint32_t(trial_restriction.nodes_per_elem * trial_restriction.components); - if (!K_elem.empty()) { - for (auto [geom, elem_matrices] : K_elem) { - std::vector test_vdofs(test_restrictions[geom].nodes_per_elem * test_restrictions[geom].components); - std::vector trial_vdofs(trial_restrictions[geom].nodes_per_elem * trial_restrictions[geom].components); + std::vector test_vdofs(rows_per_elem); + std::vector trial_vdofs(cols_per_elem); - for (axom::IndexType e = 0; e < elem_matrices.shape()[0]; e++) { - test_restrictions[geom].GetElementVDofs(e, test_vdofs); - trial_restrictions[geom].GetElementVDofs(e, trial_vdofs); + for (uint32_t e = 0; e < element_ids.size(); e++) { + test_restriction.GetElementVDofs(e, test_vdofs); + trial_restriction.GetElementVDofs(e, trial_vdofs); - for (uint32_t i = 0; i < uint32_t(elem_matrices.shape()[1]); i++) { + for (uint32_t i = 0; i < cols_per_elem; i++) { int col = int(trial_vdofs[i].index()); - for (uint32_t j = 0; j < uint32_t(elem_matrices.shape()[2]); j++) { + for (uint32_t j = 0; j < rows_per_elem; j++) { int row = int(test_vdofs[j].index()); - for (int nz = row_ptr[row]; nz < row_ptr[row+1]; nz++) { - // TODO: replace linear search with binary search - if (col_ind[nz] == col) { - values[nz] += elem_matrices(e, i, j); - } - } - + A_local.SearchRow(row, col) += K_e(e, i, j); } } } + } } - } - std::ofstream outfile("K_new.mtx"); - A_local.PrintMM(outfile); - outfile.close(); - auto* R = form_.test_space_->Dof_TrueDof_Matrix(); - auto* tmp = new mfem::HypreParMatrix(test_space_->GetComm(), test_space_->GlobalVSize(), trial_space_->GlobalVSize(), - test_space_->GetDofOffsets(), trial_space_->GetDofOffsets(), &A_local); + auto* A_hypre = new mfem::HypreParMatrix(test_space_->GetComm(), test_space_->GlobalVSize(), trial_space_->GlobalVSize(), + test_space_->GetDofOffsets(), trial_space_->GetDofOffsets(), &A_local); auto* P = trial_space_->Dof_TrueDof_Matrix(); - A = *mfem::RAP(R, tmp, P); - - delete tmp; + std::unique_ptr A(mfem::RAP(R, A_hypre, P)); - #if 0 + delete A_hypre; + return A; + }; +#else + /// @brief assemble element matrices and form an mfem::HypreParMatrix + std::unique_ptr assemble() + { // the CSR graph (sparsity pattern) is reusable, so we cache // that and ask mfem to not free that memory in ~SparseMatrix() constexpr bool sparse_matrix_frees_graph_ptrs = false; @@ -995,9 +831,8 @@ class Functional { delete A; return K; - - #endif }; + #endif friend auto assemble(Gradient& g) { return g.assemble(); } @@ -1005,18 +840,8 @@ class Functional { /// @brief The "parent" @p Functional to calculate gradients with Functional& form_; - /** - * @brief this object has lookup tables for where to place each - * element and boundary element gradient contribution in the global - * sparse matrix - */ - GradientAssemblyLookupTables lookup_tables; - - /** - * @brief Copy of the column indices for sparse matrix assembly - * @note These are mutated by MFEM during HypreParMatrix construction - */ - std::vector col_ind_copy_; + std::vector row_ptr; + std::vector col_ind; /** * @brief this member variable tells us which argument the associated Functional this gradient @@ -1044,7 +869,10 @@ class Functional { const mfem::ParFiniteElementSpace* test_space_; /// @brief Manages DOFs for the trial space - std::array trial_space_; + std::array< const mfem::ParFiniteElementSpace*, num_trial_spaces> trial_space_; + + std::array< FunctionSpace, num_trial_spaces > trial_function_spaces_; + FunctionSpace test_function_space_; /** * @brief Operator that converts true (global) DOF values to local (current rank) DOF values @@ -1055,15 +883,13 @@ class Functional { /// @brief The input set of local DOF values (i.e., on the current rank) mutable mfem::Vector input_L_[num_trial_spaces]; - BlockElementRestriction G_trial_[Domain::num_types][num_trial_spaces]; - - mutable std::vector input_E_[Domain::num_types]; + mutable std::vector input_E_buffer_; + mutable std::vector input_E_; - std::vector integrals_; + mutable std::vector integrals_; - mutable mfem::BlockVector output_E_[Domain::num_types]; - - BlockElementRestriction G_test_[Domain::num_types]; + mutable mfem::Vector output_E_buffer_; + mutable mfem::BlockVector output_E_; /// @brief The output set of local DOF values (i.e., on the current rank) mutable mfem::Vector output_L_; @@ -1074,7 +900,10 @@ class Functional { mutable mfem::Vector output_T_; /// @brief The objects representing the gradients w.r.t. each input argument of the Functional - mutable std::vector grad_; + mutable std::vector< Gradient > grad_; + + const mfem::MemoryType mem_type; + }; } // namespace serac diff --git a/src/serac/numerics/functional/geometric_factors.cpp b/src/serac/numerics/functional/geometric_factors.cpp index 7e8897fa5f..e6f2537cf5 100644 --- a/src/serac/numerics/functional/geometric_factors.cpp +++ b/src/serac/numerics/functional/geometric_factors.cpp @@ -35,8 +35,9 @@ void compute_geometric_factors(mfem::Vector& positions_q, mfem::Vector& jacobian // for each element in the domain for (uint32_t e = 0; e < num_elements; e++) { + // load the positions for the nodes in this element - auto X_e = X[elements[e]]; + auto X_e = X[e]; // calculate the values and derivatives (w.r.t. xi) of X at each quadrature point auto quadrature_values = element_type::interpolate(X_e, rule); @@ -59,84 +60,154 @@ void compute_geometric_factors(mfem::Vector& positions_q, mfem::Vector& jacobian } } -GeometricFactors::GeometricFactors(const Domain& d, int q, mfem::Geometry::Type g) +GeometricFactors::GeometricFactors(const Domain& domain, int q, mfem::Geometry::Type geom) { - auto* nodes = d.mesh_.GetNodes(); + auto* nodes = domain.mesh_.GetNodes(); auto* fes = nodes->FESpace(); - auto restriction = serac::ElementRestriction(fes, g); + const std::vector & element_ids = domain.get_mfem_ids(geom); + + auto restriction = serac::ElementRestriction(fes, geom, element_ids); mfem::Vector X_e(int(restriction.ESize())); restriction.Gather(*nodes, X_e); // assumes all elements are the same order int p = fes->GetElementOrder(0); - int spatial_dim = d.mesh_.SpaceDimension(); - int geometry_dim = dimension_of(g); - int qpts_per_elem = num_quadrature_points(g, q); - - if (g == mfem::Geometry::TRIANGLE) elements = d.tri_ids_; - if (g == mfem::Geometry::SQUARE) elements = d.quad_ids_; - if (g == mfem::Geometry::TETRAHEDRON) elements = d.tet_ids_; - if (g == mfem::Geometry::CUBE) elements = d.hex_ids_; + int spatial_dim = domain.mesh_.SpaceDimension(); + int geometry_dim = dimension_of(geom); + int qpts_per_elem = num_quadrature_points(geom, q); - num_elements = elements.size(); + num_elements = element_ids.size(); X = mfem::Vector(int(num_elements) * qpts_per_elem * spatial_dim); J = mfem::Vector(int(num_elements) * qpts_per_elem * spatial_dim * geometry_dim); -#define DISPATCH_KERNEL(GEOM, P, Q) \ - if (g == mfem::Geometry::GEOM && p == P && q == Q) { \ - compute_geometric_factors >(X, J, X_e, \ - elements); \ - return; \ +#define DISPATCH_KERNEL(GEOM, P, Q, BDR) \ + if (geom == mfem::Geometry::GEOM && p == P && q == Q) { \ + compute_geometric_factors >(X, J, X_e, element_ids); \ + return; \ } - DISPATCH_KERNEL(TRIANGLE, 1, 1); - DISPATCH_KERNEL(TRIANGLE, 1, 2); - DISPATCH_KERNEL(TRIANGLE, 1, 3); - DISPATCH_KERNEL(TRIANGLE, 1, 4); - - DISPATCH_KERNEL(SQUARE, 1, 1); - DISPATCH_KERNEL(SQUARE, 1, 2); - DISPATCH_KERNEL(SQUARE, 1, 3); - DISPATCH_KERNEL(SQUARE, 1, 4); - - DISPATCH_KERNEL(SQUARE, 2, 1); - DISPATCH_KERNEL(SQUARE, 2, 2); - DISPATCH_KERNEL(SQUARE, 2, 3); - DISPATCH_KERNEL(SQUARE, 2, 4); - - DISPATCH_KERNEL(SQUARE, 3, 1); - DISPATCH_KERNEL(SQUARE, 3, 2); - DISPATCH_KERNEL(SQUARE, 3, 3); - DISPATCH_KERNEL(SQUARE, 3, 4); - - DISPATCH_KERNEL(TETRAHEDRON, 1, 1); - DISPATCH_KERNEL(TETRAHEDRON, 1, 2); - DISPATCH_KERNEL(TETRAHEDRON, 1, 3); - DISPATCH_KERNEL(TETRAHEDRON, 1, 4); - - DISPATCH_KERNEL(CUBE, 1, 1); - DISPATCH_KERNEL(CUBE, 1, 2); - DISPATCH_KERNEL(CUBE, 1, 3); - DISPATCH_KERNEL(CUBE, 1, 4); - - DISPATCH_KERNEL(CUBE, 2, 1); - DISPATCH_KERNEL(CUBE, 2, 2); - DISPATCH_KERNEL(CUBE, 2, 3); - DISPATCH_KERNEL(CUBE, 2, 4); - - DISPATCH_KERNEL(CUBE, 3, 1); - DISPATCH_KERNEL(CUBE, 3, 2); - DISPATCH_KERNEL(CUBE, 3, 3); - DISPATCH_KERNEL(CUBE, 3, 4); + DISPATCH_KERNEL(SEGMENT, 1, 1, 1); + DISPATCH_KERNEL(SEGMENT, 1, 2, 1); + DISPATCH_KERNEL(SEGMENT, 1, 3, 1); + DISPATCH_KERNEL(SEGMENT, 1, 4, 1); + + DISPATCH_KERNEL(SEGMENT, 2, 1, 1); + DISPATCH_KERNEL(SEGMENT, 2, 2, 1); + DISPATCH_KERNEL(SEGMENT, 2, 3, 1); + DISPATCH_KERNEL(SEGMENT, 2, 4, 1); + + DISPATCH_KERNEL(SEGMENT, 3, 1, 1); + DISPATCH_KERNEL(SEGMENT, 3, 2, 1); + DISPATCH_KERNEL(SEGMENT, 3, 3, 1); + DISPATCH_KERNEL(SEGMENT, 3, 4, 1); + +/////////////////////////////////////// + + DISPATCH_KERNEL(TRIANGLE, 1, 1, 0); + DISPATCH_KERNEL(TRIANGLE, 1, 2, 0); + DISPATCH_KERNEL(TRIANGLE, 1, 3, 0); + DISPATCH_KERNEL(TRIANGLE, 1, 4, 0); + + DISPATCH_KERNEL(TRIANGLE, 2, 1, 0); + DISPATCH_KERNEL(TRIANGLE, 2, 2, 0); + DISPATCH_KERNEL(TRIANGLE, 2, 3, 0); + DISPATCH_KERNEL(TRIANGLE, 2, 4, 0); + + DISPATCH_KERNEL(TRIANGLE, 3, 1, 0); + DISPATCH_KERNEL(TRIANGLE, 3, 2, 0); + DISPATCH_KERNEL(TRIANGLE, 3, 3, 0); + DISPATCH_KERNEL(TRIANGLE, 3, 4, 0); + + DISPATCH_KERNEL(TRIANGLE, 1, 1, 1); + DISPATCH_KERNEL(TRIANGLE, 1, 2, 1); + DISPATCH_KERNEL(TRIANGLE, 1, 3, 1); + DISPATCH_KERNEL(TRIANGLE, 1, 4, 1); + + DISPATCH_KERNEL(TRIANGLE, 2, 1, 1); + DISPATCH_KERNEL(TRIANGLE, 2, 2, 1); + DISPATCH_KERNEL(TRIANGLE, 2, 3, 1); + DISPATCH_KERNEL(TRIANGLE, 2, 4, 1); + + DISPATCH_KERNEL(TRIANGLE, 3, 1, 1); + DISPATCH_KERNEL(TRIANGLE, 3, 2, 1); + DISPATCH_KERNEL(TRIANGLE, 3, 3, 1); + DISPATCH_KERNEL(TRIANGLE, 3, 4, 1); + +/////////////////////////////////////// + + DISPATCH_KERNEL(SQUARE, 1, 1, 0); + DISPATCH_KERNEL(SQUARE, 1, 2, 0); + DISPATCH_KERNEL(SQUARE, 1, 3, 0); + DISPATCH_KERNEL(SQUARE, 1, 4, 0); + + DISPATCH_KERNEL(SQUARE, 2, 1, 0); + DISPATCH_KERNEL(SQUARE, 2, 2, 0); + DISPATCH_KERNEL(SQUARE, 2, 3, 0); + DISPATCH_KERNEL(SQUARE, 2, 4, 0); + + DISPATCH_KERNEL(SQUARE, 3, 1, 0); + DISPATCH_KERNEL(SQUARE, 3, 2, 0); + DISPATCH_KERNEL(SQUARE, 3, 3, 0); + DISPATCH_KERNEL(SQUARE, 3, 4, 0); + + DISPATCH_KERNEL(SQUARE, 1, 1, 1); + DISPATCH_KERNEL(SQUARE, 1, 2, 1); + DISPATCH_KERNEL(SQUARE, 1, 3, 1); + DISPATCH_KERNEL(SQUARE, 1, 4, 1); + + DISPATCH_KERNEL(SQUARE, 2, 1, 1); + DISPATCH_KERNEL(SQUARE, 2, 2, 1); + DISPATCH_KERNEL(SQUARE, 2, 3, 1); + DISPATCH_KERNEL(SQUARE, 2, 4, 1); + + DISPATCH_KERNEL(SQUARE, 3, 1, 1); + DISPATCH_KERNEL(SQUARE, 3, 2, 1); + DISPATCH_KERNEL(SQUARE, 3, 3, 1); + DISPATCH_KERNEL(SQUARE, 3, 4, 1); + +/////////////////////////////////////// + + DISPATCH_KERNEL(TETRAHEDRON, 1, 1, 0); + DISPATCH_KERNEL(TETRAHEDRON, 1, 2, 0); + DISPATCH_KERNEL(TETRAHEDRON, 1, 3, 0); + DISPATCH_KERNEL(TETRAHEDRON, 1, 4, 0); + + DISPATCH_KERNEL(TETRAHEDRON, 2, 1, 0); + DISPATCH_KERNEL(TETRAHEDRON, 2, 2, 0); + DISPATCH_KERNEL(TETRAHEDRON, 2, 3, 0); + DISPATCH_KERNEL(TETRAHEDRON, 2, 4, 0); + + DISPATCH_KERNEL(TETRAHEDRON, 3, 1, 0); + DISPATCH_KERNEL(TETRAHEDRON, 3, 2, 0); + DISPATCH_KERNEL(TETRAHEDRON, 3, 3, 0); + DISPATCH_KERNEL(TETRAHEDRON, 3, 4, 0); + +/////////////////////////////////////// + + DISPATCH_KERNEL(CUBE, 1, 1, 0); + DISPATCH_KERNEL(CUBE, 1, 2, 0); + DISPATCH_KERNEL(CUBE, 1, 3, 0); + DISPATCH_KERNEL(CUBE, 1, 4, 0); + + DISPATCH_KERNEL(CUBE, 2, 1, 0); + DISPATCH_KERNEL(CUBE, 2, 2, 0); + DISPATCH_KERNEL(CUBE, 2, 3, 0); + DISPATCH_KERNEL(CUBE, 2, 4, 0); + + DISPATCH_KERNEL(CUBE, 3, 1, 0); + DISPATCH_KERNEL(CUBE, 3, 2, 0); + DISPATCH_KERNEL(CUBE, 3, 3, 0); + DISPATCH_KERNEL(CUBE, 3, 4, 0); #undef DISPATCH_KERNEL std::cout << "should never be reached " << std::endl; } +#if 0 GeometricFactors::GeometricFactors(const Domain& d, int q, mfem::Geometry::Type g, FaceType type) { auto* nodes = d.mesh_.GetNodes(); @@ -218,5 +289,6 @@ GeometricFactors::GeometricFactors(const Domain& d, int q, mfem::Geometry::Type std::cout << "should never be reached" << std::endl; } +#endif } // namespace serac diff --git a/src/serac/numerics/functional/geometric_factors.hpp b/src/serac/numerics/functional/geometric_factors.hpp index 5d33ab2560..4a0659c710 100644 --- a/src/serac/numerics/functional/geometric_factors.hpp +++ b/src/serac/numerics/functional/geometric_factors.hpp @@ -14,6 +14,7 @@ namespace serac { * calculations on boundary elements and on simplex elements */ struct GeometricFactors { + /// @brief default ctor, leaving this object uninitialized GeometricFactors(){}; @@ -27,17 +28,6 @@ struct GeometricFactors { */ GeometricFactors(const Domain& domain, int q, mfem::Geometry::Type elem_geom); - /** - * @brief calculate positions and jacobians for quadrature points belonging to - * boundary elements with the specified geometry, belonging to the provided mesh. - * - * @param domain the domain of integration - * @param q a parameter controlling the number of quadrature points per element - * @param elem_geom which kind of element geometry to select - * @param type whether or not the faces are on the boundary (supported) or interior (unsupported) - */ - GeometricFactors(const Domain& domain, int q, mfem::Geometry::Type elem_geom, FaceType type); - // descriptions copied from mfem /// Mapped (physical) coordinates of all quadrature points. @@ -57,9 +47,6 @@ struct GeometricFactors { - NE = number of elements in the mesh. */ mfem::Vector J; - /// @brief list of element indices that are part of the associated domain - std::vector elements; - /// the number of elements in the domain std::size_t num_elements; }; diff --git a/src/serac/numerics/functional/integral.hpp b/src/serac/numerics/functional/integral.hpp index f0ad546321..4ffb16ab5a 100644 --- a/src/serac/numerics/functional/integral.hpp +++ b/src/serac/numerics/functional/integral.hpp @@ -24,7 +24,7 @@ namespace serac { /// @brief a class for representing a Integral calculations and their derivatives struct Integral { /// @brief the number of different kinds of integration domains - static constexpr std::size_t num_types = 2; + static constexpr std::size_t num_types = 3; /** * @brief Construct an "empty" Integral object, whose kernels are to be initialized later @@ -91,14 +91,14 @@ struct Integral { * @param differentiation_index a non-negative value indicates directional derivative with respect to the trial space * with that index. */ - void GradientMult(const mfem::BlockVector& input_E, mfem::BlockVector& output_E, uint32_t differentiation_index) const + void GradientMult(const mfem::BlockVector& dinput_E, mfem::BlockVector& doutput_E, uint32_t differentiation_index) const { - output_E = 0.0; + doutput_E = 0.0; // if this integral actually depends on the specified variable if (functional_to_integral_index_.count(differentiation_index) > 0) { for (auto& [geometry, func] : jvp_[functional_to_integral_index_.at(differentiation_index)]) { - func(input_E.GetBlock(geometry).Read(), output_E.GetBlock(geometry).ReadWrite()); + func(dinput_E.GetBlock(geometry).Read(), doutput_E.GetBlock(geometry).ReadWrite()); } } } @@ -193,13 +193,12 @@ void generate_kernels(FunctionSignature s, Integral& integral, const double* positions = gf.X.Read(); const double* jacobians = gf.J.Read(); - const int* elements = &integral.domain_.get(geom)[0]; const uint32_t num_elements = uint32_t(gf.num_elements); const uint32_t qpts_per_element = num_quadrature_points(geom, Q); std::shared_ptr dummy_derivatives; integral.evaluation_[geom] = domain_integral::evaluation_kernel( - s, qf, positions, jacobians, qdata, dummy_derivatives, elements, num_elements); + s, qf, positions, jacobians, qdata, dummy_derivatives, num_elements); constexpr std::size_t num_args = s.num_args; [[maybe_unused]] static constexpr int dim = dimension_of(geom); @@ -213,12 +212,12 @@ void generate_kernels(FunctionSignature s, Integral& integral, auto ptr = accelerator::make_shared_array(num_elements * qpts_per_element); integral.evaluation_with_AD_[index][geom] = domain_integral::evaluation_kernel( - s, qf, positions, jacobians, qdata, ptr, elements, num_elements); + s, qf, positions, jacobians, qdata, ptr, num_elements); integral.jvp_[index][geom] = - domain_integral::jacobian_vector_product_kernel(s, ptr, elements, num_elements); + domain_integral::jacobian_vector_product_kernel(s, ptr, num_elements); integral.element_gradient_[index][geom] = - domain_integral::element_gradient_kernel(s, ptr, elements, num_elements); + domain_integral::element_gradient_kernel(s, ptr, num_elements); }); } @@ -276,7 +275,7 @@ Integral MakeDomainIntegral(const Domain& domain, const lambda_type& qf, template void generate_bdr_kernels(FunctionSignature s, Integral& integral, const lambda_type& qf) { - integral.geometric_factors_[geom] = GeometricFactors(integral.domain_, Q, geom, FaceType::BOUNDARY); + integral.geometric_factors_[geom] = GeometricFactors(integral.domain_, Q, geom); GeometricFactors& gf = integral.geometric_factors_[geom]; if (gf.num_elements == 0) return; @@ -284,11 +283,10 @@ void generate_bdr_kernels(FunctionSignature s, Integral& integr const double* jacobians = gf.J.Read(); const uint32_t num_elements = uint32_t(gf.num_elements); const uint32_t qpts_per_element = num_quadrature_points(geom, Q); - const int* elements = &gf.elements[0]; std::shared_ptr dummy_derivatives; integral.evaluation_[geom] = boundary_integral::evaluation_kernel( - s, qf, positions, jacobians, dummy_derivatives, elements, num_elements); + s, qf, positions, jacobians, dummy_derivatives, num_elements); constexpr std::size_t num_args = s.num_args; [[maybe_unused]] static constexpr int dim = dimension_of(geom); @@ -302,12 +300,12 @@ void generate_bdr_kernels(FunctionSignature s, Integral& integr auto ptr = accelerator::make_shared_array(num_elements * qpts_per_element); integral.evaluation_with_AD_[index][geom] = - boundary_integral::evaluation_kernel(s, qf, positions, jacobians, ptr, elements, num_elements); + boundary_integral::evaluation_kernel(s, qf, positions, jacobians, ptr, num_elements); integral.jvp_[index][geom] = - boundary_integral::jacobian_vector_product_kernel(s, ptr, elements, num_elements); + boundary_integral::jacobian_vector_product_kernel(s, ptr, num_elements); integral.element_gradient_[index][geom] = - boundary_integral::element_gradient_kernel(s, ptr, elements, num_elements); + boundary_integral::element_gradient_kernel(s, ptr, num_elements); }); } @@ -371,11 +369,10 @@ void generate_interior_face_kernels(FunctionSignature s, Integr const double* jacobians = gf.J.Read(); const uint32_t num_elements = uint32_t(gf.num_elements); const uint32_t qpts_per_element = num_quadrature_points(geom, Q); - const int* elements = &gf.elements[0]; std::shared_ptr dummy_derivatives; integral.evaluation_[geom] = interior_face_integral::evaluation_kernel( - s, qf, positions, jacobians, dummy_derivatives, elements, num_elements); + s, qf, positions, jacobians, dummy_derivatives, num_elements); constexpr std::size_t num_args = s.num_args; [[maybe_unused]] static constexpr int dim = dimension_of(geom); @@ -389,12 +386,12 @@ void generate_interior_face_kernels(FunctionSignature s, Integr auto ptr = accelerator::make_shared_array(num_elements * qpts_per_element); integral.evaluation_with_AD_[index][geom] = - interior_face_integral::evaluation_kernel(s, qf, positions, jacobians, ptr, elements, num_elements); + interior_face_integral::evaluation_kernel(s, qf, positions, jacobians, ptr, num_elements); integral.jvp_[index][geom] = - interior_face_integral::jacobian_vector_product_kernel(s, ptr, elements, num_elements); + interior_face_integral::jacobian_vector_product_kernel(s, ptr, num_elements); integral.element_gradient_[index][geom] = - interior_face_integral::element_gradient_kernel(s, ptr, elements, num_elements); + interior_face_integral::element_gradient_kernel(s, ptr, num_elements); }); } diff --git a/src/serac/numerics/functional/tests/functional_basic_dg.cpp b/src/serac/numerics/functional/tests/functional_basic_dg.cpp index 51f363571f..37b135c003 100644 --- a/src/serac/numerics/functional/tests/functional_basic_dg.cpp +++ b/src/serac/numerics/functional/tests/functional_basic_dg.cpp @@ -61,10 +61,9 @@ void L2_test_2D() auto a = dot(u_2 - u_1, n); - auto s_1 = u_1 * a; - auto s_2 = u_2 * a; - - return serac::tuple{s_1, s_2}; + auto f_1 = u_1 * a; + auto f_2 = u_2 * a; + return serac::tuple{f_1, f_2}; }, *mesh); diff --git a/src/serac/numerics/functional/tests/functional_basic_h1_scalar.cpp b/src/serac/numerics/functional/tests/functional_basic_h1_scalar.cpp index bf09f22bae..e8a96391a9 100644 --- a/src/serac/numerics/functional/tests/functional_basic_h1_scalar.cpp +++ b/src/serac/numerics/functional/tests/functional_basic_h1_scalar.cpp @@ -73,17 +73,19 @@ void thermal_test_impl(std::unique_ptr& mesh) // Construct the new functional object using the known test and trial spaces Functional residual(test_fespace.get(), {trial_fespace.get()}); - residual.AddDomainIntegral(Dimension{}, DependsOn<0>{}, TestThermalModelOne{}, *mesh); + Domain dom = EntireDomain(*mesh); + Domain bdr = EntireBoundary(*mesh); - residual.AddBoundaryIntegral(Dimension{}, DependsOn<0>{}, TestThermalModelTwo{}, *mesh); + residual.AddDomainIntegral(Dimension{}, DependsOn<0>{}, TestThermalModelOne{}, dom); + + residual.AddBoundaryIntegral(Dimension{}, DependsOn<0>{}, TestThermalModelTwo{}, bdr); double t = 0.0; -#if 1 +#if 0 auto [value, dfdU] = residual(t, serac::differentiate_wrt(U)); std::unique_ptr dfdU_matrix = assemble(dfdU); - mfem::HypreParMatrix dfdU_new; - dfdU.assemble(dfdU_new); + std::unique_ptr dfdU_matrix2 = dfdU.assemble2(); #endif check_gradient(residual, t, U); } @@ -103,15 +105,15 @@ void thermal_test(std::string meshfile) } TEST(basic, thermal_tris) { thermal_test<1, 1>("/data/meshes/patch2D_tris.mesh"); } -//TEST(basic, thermal_quads) { thermal_test<1, 1>("/data/meshes/patch2D_quads.mesh"); } -//TEST(basic, thermal_tris_and_quads) { thermal_test<1, 1>("/data/meshes/patch2D_tris_and_quads.mesh"); } -// -//TEST(basic, thermal_tets) { thermal_test<1, 1>("/data/meshes/patch3D_tets.mesh"); } -//TEST(basic, thermal_hexes) { thermal_test<1, 1>("/data/meshes/patch3D_hexes.mesh"); } -//TEST(basic, thermal_tets_and_hexes) { thermal_test<1, 1>("/data/meshes/patch3D_tets_and_hexes.mesh"); } -// -//TEST(mixed, thermal_tris_and_quads) { thermal_test<2, 1>("/data/meshes/patch2D_tris_and_quads.mesh"); } -//TEST(mixed, thermal_tets_and_hexes) { thermal_test<2, 1>("/data/meshes/patch3D_tets_and_hexes.mesh"); } +TEST(basic, thermal_quads) { thermal_test<1, 1>("/data/meshes/patch2D_quads.mesh"); } +TEST(basic, thermal_tris_and_quads) { thermal_test<1, 1>("/data/meshes/patch2D_tris_and_quads.mesh"); } + +TEST(basic, thermal_tets) { thermal_test<1, 1>("/data/meshes/patch3D_tets.mesh"); } +TEST(basic, thermal_hexes) { thermal_test<1, 1>("/data/meshes/patch3D_hexes.mesh"); } +TEST(basic, thermal_tets_and_hexes) { thermal_test<1, 1>("/data/meshes/patch3D_tets_and_hexes.mesh"); } + +TEST(mixed, thermal_tris_and_quads) { thermal_test<2, 1>("/data/meshes/patch2D_tris_and_quads.mesh"); } +TEST(mixed, thermal_tets_and_hexes) { thermal_test<2, 1>("/data/meshes/patch3D_tets_and_hexes.mesh"); } int main(int argc, char* argv[]) { From 62707978621c93a956bd0f44456ffd18d605d650 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Thu, 10 Oct 2024 14:48:12 -0700 Subject: [PATCH 17/92] fix bug in geometric factor kernel macro, fix some warnings --- src/serac/numerics/functional/detail/quadrilateral_L2.inl | 2 +- src/serac/numerics/functional/detail/triangle_L2.inl | 2 +- src/serac/numerics/functional/functional.hpp | 7 +++---- src/serac/numerics/functional/geometric_factors.cpp | 2 +- src/serac/numerics/functional/integral.hpp | 2 +- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/serac/numerics/functional/detail/quadrilateral_L2.inl b/src/serac/numerics/functional/detail/quadrilateral_L2.inl index 356d072d17..cd9c2f765a 100644 --- a/src/serac/numerics/functional/detail/quadrilateral_L2.inl +++ b/src/serac/numerics/functional/detail/quadrilateral_L2.inl @@ -243,7 +243,7 @@ struct finite_element > { // overload for two-sided interior face kernels template - SERAC_HOST_DEVICE static auto interpolate(const dof_type_if& X, const TensorProductQuadratureRule&) + SERAC_HOST_DEVICE static auto interpolate([[maybe_unused]] const dof_type_if& X, const TensorProductQuadratureRule&) { static constexpr bool apply_weights = false; static constexpr auto B = calculate_B(); diff --git a/src/serac/numerics/functional/detail/triangle_L2.inl b/src/serac/numerics/functional/detail/triangle_L2.inl index 90f7070a07..49a752b888 100644 --- a/src/serac/numerics/functional/detail/triangle_L2.inl +++ b/src/serac/numerics/functional/detail/triangle_L2.inl @@ -301,7 +301,7 @@ struct finite_element > { // overload for two-sided interior face kernels template - SERAC_HOST_DEVICE static auto interpolate(const tensor& X, const TensorProductQuadratureRule&) + SERAC_HOST_DEVICE static auto interpolate([[maybe_unused]] const tensor& X, const TensorProductQuadratureRule&) { constexpr auto xi = GaussLegendreNodes(); static constexpr int num_quadrature_points = q * (q + 1) / 2; diff --git a/src/serac/numerics/functional/functional.hpp b/src/serac/numerics/functional/functional.hpp index 92de2e870c..790ecda749 100644 --- a/src/serac/numerics/functional/functional.hpp +++ b/src/serac/numerics/functional/functional.hpp @@ -460,8 +460,7 @@ class Functional { integral.Mult(t, input_E_, output_E_, wrt, update_qdata_); // scatter-add to compute residuals on the local processor - mfem::BlockVector output_EBV(output_E_, G_test.bOffsets()); - G_test.ScatterAdd(output_EBV, output_L_); + G_test.ScatterAdd(output_E_, output_L_); } // scatter-add to compute global residuals @@ -602,7 +601,7 @@ class Functional { uint64_t nnz = nonzero_entries.size(); int nrows = form_.output_L_.Size(); - row_ptr.resize(nrows + 1); + row_ptr.resize(uint32_t(nrows + 1)); col_ind.resize(nnz); int nz = 0; @@ -651,7 +650,7 @@ class Functional { std::vector col_ind_copy = col_ind; int nnz = row_ptr.back(); - std::vector values(nnz, 0.0); + std::vector values(uint32_t(nnz), 0.0); auto A_local = mfem::SparseMatrix( row_ptr.data(), col_ind_copy.data(), diff --git a/src/serac/numerics/functional/geometric_factors.cpp b/src/serac/numerics/functional/geometric_factors.cpp index e6f2537cf5..152af877b0 100644 --- a/src/serac/numerics/functional/geometric_factors.cpp +++ b/src/serac/numerics/functional/geometric_factors.cpp @@ -84,7 +84,7 @@ GeometricFactors::GeometricFactors(const Domain& domain, int q, mfem::Geometry:: J = mfem::Vector(int(num_elements) * qpts_per_elem * spatial_dim * geometry_dim); #define DISPATCH_KERNEL(GEOM, P, Q, BDR) \ - if (geom == mfem::Geometry::GEOM && p == P && q == Q) { \ + if (geom == mfem::Geometry::GEOM && p == P && q == Q && (spatial_dim - geometry_dim) == BDR) { \ compute_geometric_factors >(X, J, X_e, element_ids); \ return; \ } diff --git a/src/serac/numerics/functional/integral.hpp b/src/serac/numerics/functional/integral.hpp index 4ffb16ab5a..839f28eaa3 100644 --- a/src/serac/numerics/functional/integral.hpp +++ b/src/serac/numerics/functional/integral.hpp @@ -361,7 +361,7 @@ Integral MakeBoundaryIntegral(const Domain& domain, const lambda_type& qf, std:: template void generate_interior_face_kernels(FunctionSignature s, Integral& integral, const lambda_type& qf) { - integral.geometric_factors_[geom] = GeometricFactors(integral.domain_, Q, geom, FaceType::BOUNDARY); + integral.geometric_factors_[geom] = GeometricFactors(integral.domain_, Q, geom); GeometricFactors& gf = integral.geometric_factors_[geom]; if (gf.num_elements == 0) return; From e21d752c109ef0901b90fe7fdedb097f9d4d4abd Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Thu, 10 Oct 2024 14:48:35 -0700 Subject: [PATCH 18/92] update some functional tests to use Domains --- .../functional/tests/functional_basic_dg.cpp | 5 +++-- .../tests/functional_basic_h1_vector.cpp | 14 +++++++++----- .../functional/tests/functional_boundary_test.cpp | 8 ++++++-- .../functional/tests/functional_comparison_L2.cpp | 7 +++++-- .../functional/tests/functional_comparisons.cpp | 8 ++++++-- .../functional/tests/functional_multiphysics.cpp | 7 +++++-- .../functional/tests/functional_nonlinear.cpp | 15 +++++++++++---- .../functional/tests/functional_with_domain.cpp | 8 +++++--- 8 files changed, 50 insertions(+), 22 deletions(-) diff --git a/src/serac/numerics/functional/tests/functional_basic_dg.cpp b/src/serac/numerics/functional/tests/functional_basic_dg.cpp index 37b135c003..9d63b65b31 100644 --- a/src/serac/numerics/functional/tests/functional_basic_dg.cpp +++ b/src/serac/numerics/functional/tests/functional_basic_dg.cpp @@ -46,6 +46,8 @@ void L2_test_2D() constexpr int DERIVATIVE = 1; + Domain interior_faces = InteriorFaces(*mesh); + residual.AddInteriorFaceIntegral( Dimension{}, DependsOn<0>{}, [=](double /*t*/, auto X, auto velocity) { @@ -65,8 +67,7 @@ void L2_test_2D() auto f_2 = u_2 * a; return serac::tuple{f_1, f_2}; - }, - *mesh); + }, interior_faces); double t = 0.0; check_gradient(residual, t, U); diff --git a/src/serac/numerics/functional/tests/functional_basic_h1_vector.cpp b/src/serac/numerics/functional/tests/functional_basic_h1_vector.cpp index 1d22877510..4273da15e5 100644 --- a/src/serac/numerics/functional/tests/functional_basic_h1_vector.cpp +++ b/src/serac/numerics/functional/tests/functional_basic_h1_vector.cpp @@ -96,9 +96,10 @@ void weird_mixed_test(std::unique_ptr& mesh) // note: this is not really an elasticity problem, it's testing source and flux // terms that have the appropriate shapes to ensure that all the differentiation // code works as intended - residual.AddDomainIntegral(Dimension{}, DependsOn<0>{}, MixedModelOne{}, *mesh); - - residual.AddBoundaryIntegral(Dimension{}, DependsOn<0>{}, MixedModelTwo{}, *mesh); + Domain dom = EntireDomain(*mesh); + Domain bdr = EntireBoundary(*mesh); + residual.AddDomainIntegral(Dimension{}, DependsOn<0>{}, MixedModelOne{}, dom); + residual.AddBoundaryIntegral(Dimension{}, DependsOn<0>{}, MixedModelTwo{}, bdr); double t = 0.0; check_gradient(residual, t, U); @@ -122,8 +123,11 @@ void elasticity_test(std::unique_ptr& mesh) // note: this is not really an elasticity problem, it's testing source and flux // terms that have the appropriate shapes to ensure that all the differentiation // code works as intended - residual.AddDomainIntegral(Dimension{}, DependsOn<0>{}, ElasticityTestModelOne{}, *mesh); - residual.AddBoundaryIntegral(Dimension{}, DependsOn<0>{}, ElasticityTestModelTwo{}, *mesh); + Domain dom = EntireDomain(*mesh); + Domain bdr = EntireBoundary(*mesh); + + residual.AddDomainIntegral(Dimension{}, DependsOn<0>{}, ElasticityTestModelOne{}, dom); + residual.AddBoundaryIntegral(Dimension{}, DependsOn<0>{}, ElasticityTestModelTwo{}, bdr); double t = 0.0; check_gradient(residual, t, U); diff --git a/src/serac/numerics/functional/tests/functional_boundary_test.cpp b/src/serac/numerics/functional/tests/functional_boundary_test.cpp index f34248f086..b177e37c2f 100644 --- a/src/serac/numerics/functional/tests/functional_boundary_test.cpp +++ b/src/serac/numerics/functional/tests/functional_boundary_test.cpp @@ -92,6 +92,8 @@ void boundary_test(mfem::ParMesh& mesh, H1

test, H1

trial, Dimension) Functional residual(fespace.get(), {fespace.get()}); + Domain bdr = EntireBoundary(mesh); + residual.AddBoundaryIntegral( Dimension{}, DependsOn<0>{}, [&](double /*t*/, auto position, auto temperature) { @@ -103,7 +105,7 @@ void boundary_test(mfem::ParMesh& mesh, H1

test, H1

trial, Dimension) tensor b{sin(X[0]), X[0] * X[1]}; return X[0] * X[1] + dot(b, n) + rho * u; }, - mesh); + bdr); // mfem::Vector r1 = (*J) * U + (*F); mfem::Vector r1(U.Size()); @@ -171,6 +173,8 @@ void boundary_test(mfem::ParMesh& mesh, L2

test, L2

trial, Dimension) Functional residual(fespace.get(), {fespace.get()}); + Domain bdr = EntireBoundary(mesh); + residual.AddBoundaryIntegral( Dimension{}, DependsOn<0>{}, [&](double /*t*/, auto position, auto temperature) { @@ -181,7 +185,7 @@ void boundary_test(mfem::ParMesh& mesh, L2

test, L2

trial, Dimension) // tensor b{sin(x[0]), x[0] * x[1]}; return X[0] * X[1] + /* dot(b, n) +*/ rho * u; }, - mesh); + bdr); // mfem::Vector r1 = (*J) * U + (*F); mfem::Vector r1(U.Size()); diff --git a/src/serac/numerics/functional/tests/functional_comparison_L2.cpp b/src/serac/numerics/functional/tests/functional_comparison_L2.cpp index cf1906092d..416f4835c4 100644 --- a/src/serac/numerics/functional/tests/functional_comparison_L2.cpp +++ b/src/serac/numerics/functional/tests/functional_comparison_L2.cpp @@ -103,7 +103,8 @@ void functional_test(mfem::ParMesh& mesh, L2

test, L2

trial, Dimension residual(fespace.get(), {fespace.get()}); // Add the total domain residual term to the weak form - residual.AddDomainIntegral(Dimension{}, DependsOn<0>{}, test_qfunction{}, mesh); + Domain dom = EntireDomain(mesh); + residual.AddDomainIntegral(Dimension{}, DependsOn<0>{}, test_qfunction{}, dom); // uncomment lines below to verify that compile-time error messages // explain L2 spaces are not currently supported in boundary integrals. @@ -174,8 +175,10 @@ TEST(L2, 2DMixed) auto [H1fespace, H1fec] = serac::generateParFiniteElementSpace(mesh2D.get()); + Domain dom = EntireDomain(*mesh2D); + serac::Functional f(L2fespace.get(), {H1fespace.get()}); - f.AddDomainIntegral(serac::Dimension{}, serac::DependsOn<0>{}, hcurl_qfunction{}, *mesh2D); + f.AddDomainIntegral(serac::Dimension{}, serac::DependsOn<0>{}, hcurl_qfunction{}, dom); } int main(int argc, char* argv[]) diff --git a/src/serac/numerics/functional/tests/functional_comparisons.cpp b/src/serac/numerics/functional/tests/functional_comparisons.cpp index 17bb76a67e..125cc3c0ca 100644 --- a/src/serac/numerics/functional/tests/functional_comparisons.cpp +++ b/src/serac/numerics/functional/tests/functional_comparisons.cpp @@ -105,6 +105,8 @@ void functional_test(mfem::ParMesh& mesh, H1

test, H1

trial, Dimension residual(fespace.get(), {fespace.get()}); + Domain dom = EntireDomain(mesh); + // Add the total domain residual term to the functional residual.AddDomainIntegral( Dimension{}, DependsOn<0>{}, @@ -116,7 +118,7 @@ void functional_test(mfem::ParMesh& mesh, H1

test, H1

trial, Dimension test, H1 trial, Dim Functional residual(fespace.get(), {fespace.get()}); + Domain dom = EntireDomain(mesh); + residual.AddDomainIntegral( Dimension{}, DependsOn<0>{}, [=](double /*t*/, auto /*x*/, auto displacement) { @@ -242,7 +246,7 @@ void functional_test(mfem::ParMesh& mesh, H1 test, H1 trial, Dim auto stress = b * tr(strain) * I + 2.0 * b * strain; return serac::tuple{body_force, stress}; }, - mesh); + dom); // mfem::Vector r1 = (*J_mfem) * U - (*F); mfem::Vector r1(U.Size()); diff --git a/src/serac/numerics/functional/tests/functional_multiphysics.cpp b/src/serac/numerics/functional/tests/functional_multiphysics.cpp index fc750c0c85..12b04514fd 100644 --- a/src/serac/numerics/functional/tests/functional_multiphysics.cpp +++ b/src/serac/numerics/functional/tests/functional_multiphysics.cpp @@ -52,6 +52,9 @@ TEST(FunctionalMultiphysics, NonlinearThermalTest3D) // Construct the new functional object using the known test and trial spaces Functional residual(fespace.get(), {fespace.get(), fespace.get()}); + Domain dom = EntireDomain(*mesh3D); + Domain bdr = EntireBoundary(*mesh3D); + residual.AddVolumeIntegral( DependsOn<0, 1>{}, [=](double /*t*/, auto position, auto temperature, auto dtemperature_dt) { @@ -62,7 +65,7 @@ TEST(FunctionalMultiphysics, NonlinearThermalTest3D) auto flux = kappa * du_dX; return serac::tuple{source, flux}; }, - *mesh3D); + dom); residual.AddSurfaceIntegral( DependsOn<0, 1>{}, @@ -72,7 +75,7 @@ TEST(FunctionalMultiphysics, NonlinearThermalTest3D) auto [du_dt, _1] = dtemperature_dt; return X[0] + X[1] - cos(u) * du_dt; }, - *mesh3D); + bdr); double t = 0.0; mfem::Vector r = residual(t, U, dU_dt); diff --git a/src/serac/numerics/functional/tests/functional_nonlinear.cpp b/src/serac/numerics/functional/tests/functional_nonlinear.cpp index d846df32bb..edab8c7c67 100644 --- a/src/serac/numerics/functional/tests/functional_nonlinear.cpp +++ b/src/serac/numerics/functional/tests/functional_nonlinear.cpp @@ -72,6 +72,9 @@ void functional_test(mfem::ParMesh& mesh, H1

test, H1

trial, Dimension residual(fespace.get(), {fespace.get()}); + Domain dom = EntireDomain(mesh); + Domain bdr = EntireBoundary(mesh); + // Add the total domain residual term to the functional residual.AddDomainIntegral( Dimension{}, DependsOn<0>{}, @@ -82,7 +85,7 @@ void functional_test(mfem::ParMesh& mesh, H1

test, H1

trial, Dimension{}, DependsOn<0>{}, @@ -91,7 +94,7 @@ void functional_test(mfem::ParMesh& mesh, H1

test, H1

trial, Dimension(temperature); return X[0] + X[1] - cos(u); }, - mesh); + bdr); double t = 0.0; check_gradient(residual, t, U); @@ -119,6 +122,9 @@ void functional_test(mfem::ParMesh& mesh, H1 test, H1 trial, Dim // Construct the new functional object using the known test and trial spaces Functional residual(fespace.get(), {fespace.get()}); + Domain dom = EntireDomain(mesh); + Domain bdr = EntireBoundary(mesh); + // Add the total domain residual term to the functional residual.AddDomainIntegral( Dimension{}, DependsOn<0>{}, @@ -129,17 +135,18 @@ void functional_test(mfem::ParMesh& mesh, H1 test, H1 trial, Dim auto flux = b * du_dx; return serac::tuple{source, flux}; }, - mesh); + dom); residual.AddBoundaryIntegral( Dimension{}, DependsOn<0>{}, [=](double /*t*/, auto position, auto displacement) { auto [X, dX_dxi] = position; + std::cout << X << " " << dX_dxi << std::endl; auto u = get<0>(displacement); auto n = normalize(cross(dX_dxi)); return (X[0] + X[1] - cos(u[0])) * n; }, - mesh); + bdr); double t = 0.0; check_gradient(residual, t, U); diff --git a/src/serac/numerics/functional/tests/functional_with_domain.cpp b/src/serac/numerics/functional/tests/functional_with_domain.cpp index cdefd63c5d..1707995df2 100644 --- a/src/serac/numerics/functional/tests/functional_with_domain.cpp +++ b/src/serac/numerics/functional/tests/functional_with_domain.cpp @@ -222,10 +222,12 @@ void partial_mesh_comparison_test_impl(std::unique_ptr& mesh) ////////////// - residual_comparison.AddDomainIntegral(Dimension{}, DependsOn<0>{}, TestThermalIntegratorOne{}, *mesh); + Domain d = EntireDomain(*mesh); + residual_comparison.AddDomainIntegral(Dimension{}, DependsOn<0>{}, TestThermalIntegratorOne{}, d); - residual_comparison.AddBoundaryIntegral(Dimension{}, DependsOn<0>{}, TestThermalIntegratorTwo{}, - *mesh); + + Domain bdr = EntireBoundary(*mesh); + residual_comparison.AddBoundaryIntegral(Dimension{}, DependsOn<0>{}, TestThermalIntegratorTwo{}, bdr); auto r1 = residual_comparison(t, U); From e909e105512b7d9a9fc89bb0e390eb5d6e0b4124 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Tue, 22 Oct 2024 21:33:12 -0700 Subject: [PATCH 19/92] suppress warnings and fix bug in fespace construction --- src/serac/numerics/functional/functional.hpp | 12 ++++---- .../interior_face_integral_kernels.hpp | 30 +++++++++---------- .../functional/tests/functional_basic_dg.cpp | 2 +- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/serac/numerics/functional/functional.hpp b/src/serac/numerics/functional/functional.hpp index 790ecda749..4c7aa0d05d 100644 --- a/src/serac/numerics/functional/functional.hpp +++ b/src/serac/numerics/functional/functional.hpp @@ -607,12 +607,12 @@ class Functional { int nz = 0; int last_row = -1; for (auto [row, col] : nonzero_entries) { - col_ind[nz] = col; - for (int i = last_row+1; i <= row; i++) { row_ptr[i] = nz; } + col_ind[uint32_t(nz)] = col; + for (int i = last_row+1; i <= row; i++) { row_ptr[uint32_t(i)] = nz; } last_row = row; nz++; } - for (int i = last_row+1; i <= nrows; i++) { row_ptr[i] = nz; } + for (int i = last_row+1; i <= nrows; i++) { row_ptr[uint32_t(i)] = nz; } }; uint64_t max_buffer_size() { @@ -671,7 +671,7 @@ class Functional { // if this integral's derivative isn't identically zero if (integral.functional_to_integral_index_.count(which_argument) > 0) { - int id = integral.functional_to_integral_index_.at(which_argument); + uint32_t id = integral.functional_to_integral_index_.at(which_argument); const auto& G_test = dom.get_restriction(form_.test_function_space_); const auto& G_trial = dom.get_restriction(form_.trial_function_spaces_[which_argument]); for (const auto& [geom, calculate_element_matrices_func] : integral.element_gradient_[id]) { @@ -698,8 +698,8 @@ class Functional { std::vector trial_vdofs(cols_per_elem); for (uint32_t e = 0; e < element_ids.size(); e++) { - test_restriction.GetElementVDofs(e, test_vdofs); - trial_restriction.GetElementVDofs(e, trial_vdofs); + test_restriction.GetElementVDofs(int(e), test_vdofs); + trial_restriction.GetElementVDofs(int(e), trial_vdofs); for (uint32_t i = 0; i < cols_per_elem; i++) { int col = int(trial_vdofs[i].index()); diff --git a/src/serac/numerics/functional/interior_face_integral_kernels.hpp b/src/serac/numerics/functional/interior_face_integral_kernels.hpp index 43117a08d2..dba6956c0c 100644 --- a/src/serac/numerics/functional/interior_face_integral_kernels.hpp +++ b/src/serac/numerics/functional/interior_face_integral_kernels.hpp @@ -148,7 +148,7 @@ template & inputs, double* outputs, const double* positions, const double* jacobians, lambda_type qf, [[maybe_unused]] derivative_type* qf_derivatives, - const int* elements, uint32_t num_elements, camp::int_seq) + uint32_t num_elements, camp::int_seq) { // mfem provides this information as opaque arrays of doubles, // so we reinterpret the pointer with @@ -172,7 +172,7 @@ void evaluation_kernel_impl(trial_element_type trial_elements, test_element, dou // batch-calculate values / derivatives of each trial space, at each quadrature point [[maybe_unused]] tuple qf_inputs = {promote_each_to_dual_when( - get(trial_elements).interpolate(get(u)[elements[e]], rule))...}; + get(trial_elements).interpolate(get(u)[e], rule))...}; // (batch) evalute the q-function at each quadrature point auto qf_outputs = batch_apply_qf(qf, t, x_e, J_e, get(qf_inputs)...); @@ -187,7 +187,7 @@ void evaluation_kernel_impl(trial_element_type trial_elements, test_element, dou } // (batch) integrate the material response against the test-space basis functions - test_element::integrate(get_value(qf_outputs), rule, &r[elements[e]]); + test_element::integrate(get_value(qf_outputs), rule, &r[e]); } } @@ -237,8 +237,7 @@ SERAC_HOST_DEVICE auto batch_apply_chain_rule(derivative_type* qf_derivatives, c * @param[in] num_elements The number of elements in the mesh */ template -void action_of_gradient_kernel(const double* dU, double* dR, derivatives_type* qf_derivatives, const int* elements, - std::size_t num_elements) +void action_of_gradient_kernel(const double* dU, double* dR, derivatives_type* qf_derivatives, std::size_t num_elements) { using test_element = finite_element; using trial_element = finite_element; @@ -253,13 +252,13 @@ void action_of_gradient_kernel(const double* dU, double* dR, derivatives_type* q // for each element in the domain for (uint32_t e = 0; e < num_elements; e++) { // (batch) interpolate each quadrature point's value - auto qf_inputs = trial_element::interpolate(du[elements[e]], rule); + auto qf_inputs = trial_element::interpolate(du[e], rule); // (batch) evalute the q-function at each quadrature point auto qf_outputs = batch_apply_chain_rule(qf_derivatives + e * nqp, qf_inputs); // (batch) integrate the material response against the test-space basis functions - test_element::integrate(qf_outputs, rule, &dr[elements[e]]); + test_element::integrate(qf_outputs, rule, &dr[e]); } } @@ -285,8 +284,9 @@ void action_of_gradient_kernel(const double* dU, double* dR, derivatives_type* q * @param[in] num_elements The number of elements in the mesh */ template -void element_gradient_kernel(ExecArrayView dK, derivatives_type* qf_derivatives, - const int* elements, std::size_t num_elements) +void element_gradient_kernel([[maybe_unused]] ExecArrayView dK, + [[maybe_unused]] derivatives_type* qf_derivatives, + [[maybe_unused]] std::size_t num_elements) { #if 0 using test_element = finite_element; @@ -316,35 +316,35 @@ void element_gradient_kernel(ExecArrayView dK, d template auto evaluation_kernel(signature s, lambda_type qf, const double* positions, const double* jacobians, - std::shared_ptr qf_derivatives, const int* elements, uint32_t num_elements) + std::shared_ptr qf_derivatives, uint32_t num_elements) { auto trial_elements = trial_elements_tuple(s); auto test_element = get_test_element(s); return [=](double time, const std::vector& inputs, double* outputs, bool /* update state */) { evaluation_kernel_impl(trial_elements, test_element, time, inputs, outputs, positions, jacobians, qf, - qf_derivatives.get(), elements, num_elements, s.index_seq); + qf_derivatives.get(), num_elements, s.index_seq); }; } template std::function jacobian_vector_product_kernel( - signature, std::shared_ptr qf_derivatives, const int* elements, uint32_t num_elements) + signature, std::shared_ptr qf_derivatives, uint32_t num_elements) { return [=](const double* du, double* dr) { using test_space = typename signature::return_type; using trial_space = typename std::tuple_element::type; - action_of_gradient_kernel(du, dr, qf_derivatives.get(), elements, num_elements); + action_of_gradient_kernel(du, dr, qf_derivatives.get(), num_elements); }; } template std::function)> element_gradient_kernel( - signature, std::shared_ptr qf_derivatives, const int* elements, uint32_t num_elements) + signature, std::shared_ptr qf_derivatives, uint32_t num_elements) { return [=](ExecArrayView K_elem) { using test_space = typename signature::return_type; using trial_space = typename std::tuple_element::type; - element_gradient_kernel(K_elem, qf_derivatives.get(), elements, num_elements); + element_gradient_kernel(K_elem, qf_derivatives.get(), num_elements); }; } diff --git a/src/serac/numerics/functional/tests/functional_basic_dg.cpp b/src/serac/numerics/functional/tests/functional_basic_dg.cpp index 9d63b65b31..4ffdaf4c18 100644 --- a/src/serac/numerics/functional/tests/functional_basic_dg.cpp +++ b/src/serac/numerics/functional/tests/functional_basic_dg.cpp @@ -36,7 +36,7 @@ void L2_test_2D() auto mesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 0); auto fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); - mfem::ParFiniteElementSpace fespace(mesh.get(), &fec); + mfem::ParFiniteElementSpace fespace(mesh.get(), &fec, dim, serac::ordering); mfem::Vector U(fespace.TrueVSize()); U.Randomize(); From 5d4a093898093523e509585757ffbbcef8ce5caf Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Fri, 25 Oct 2024 14:25:09 -0700 Subject: [PATCH 20/92] fix the way the chain rule is computed internally in the interior face JVP --- .../interior_face_integral_kernels.hpp | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/serac/numerics/functional/interior_face_integral_kernels.hpp b/src/serac/numerics/functional/interior_face_integral_kernels.hpp index dba6956c0c..e2bd551723 100644 --- a/src/serac/numerics/functional/interior_face_integral_kernels.hpp +++ b/src/serac/numerics/functional/interior_face_integral_kernels.hpp @@ -192,21 +192,30 @@ void evaluation_kernel_impl(trial_element_type trial_elements, test_element, dou } //clang-format off -template +template SERAC_HOST_DEVICE auto chain_rule(const S& dfdx, const T& dx) { - return serac::chain_rule(serac::get<0>(serac::get<0>(dfdx)), serac::get<0>(dx)) + - serac::chain_rule(serac::get<1>(serac::get<0>(dfdx)), serac::get<1>(dx)); + if constexpr (is_QOI) { + return serac::chain_rule(serac::get<0>(dfdx), serac::get<0>(dx)) + + serac::chain_rule(serac::get<1>(dfdx), serac::get<1>(dx)); + } + + if constexpr (!is_QOI) { + return serac::tuple{serac::chain_rule(serac::get<0>(serac::get<0>(dfdx)), serac::get<0>(dx)) + + serac::chain_rule(serac::get<1>(serac::get<0>(dfdx)), serac::get<1>(dx)), + serac::chain_rule(serac::get<0>(serac::get<1>(dfdx)), serac::get<0>(dx)) + + serac::chain_rule(serac::get<1>(serac::get<1>(dfdx)), serac::get<1>(dx))}; + } } //clang-format on -template +template SERAC_HOST_DEVICE auto batch_apply_chain_rule(derivative_type* qf_derivatives, const tensor& inputs) { - using return_type = decltype(chain_rule(derivative_type{}, T{})); - tensor, n> outputs{}; + using return_type = decltype(chain_rule(derivative_type{}, T{})); + tensor outputs{}; for (int i = 0; i < n; i++) { - get<0>(outputs[i]) = chain_rule(qf_derivatives[i], inputs[i]); + outputs[i] = chain_rule(qf_derivatives[i], inputs[i]); } return outputs; } @@ -244,9 +253,10 @@ void action_of_gradient_kernel(const double* dU, double* dR, derivatives_type* q // mfem provides this information in 1D arrays, so we reshape it // into strided multidimensional arrays before using + constexpr bool is_QOI = (test::family == Family::QOI); constexpr int nqp = num_quadrature_points(geom, Q); - auto du = reinterpret_cast(dU); - auto dr = reinterpret_cast(dR); + auto du = reinterpret_cast(dU); + auto dr = reinterpret_cast(dR); static constexpr TensorProductQuadratureRule rule{}; // for each element in the domain @@ -255,7 +265,7 @@ void action_of_gradient_kernel(const double* dU, double* dR, derivatives_type* q auto qf_inputs = trial_element::interpolate(du[e], rule); // (batch) evalute the q-function at each quadrature point - auto qf_outputs = batch_apply_chain_rule(qf_derivatives + e * nqp, qf_inputs); + auto qf_outputs = batch_apply_chain_rule(qf_derivatives + e * nqp, qf_inputs); // (batch) integrate the material response against the test-space basis functions test_element::integrate(qf_outputs, rule, &dr[e]); From 9a5156e892bf1a3fdb8526c024b1b65ebb13c34c Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Tue, 5 Nov 2024 14:08:27 -0800 Subject: [PATCH 21/92] debugging element matrix calculation --- .../numerics/functional/detail/segment_L2.inl | 63 +++++++++++++++---- .../functional/element_restriction.cpp | 1 + src/serac/numerics/functional/functional.hpp | 4 ++ .../interior_face_integral_kernels.hpp | 10 +-- .../functional/tests/check_gradient.hpp | 6 ++ .../functional/tests/functional_basic_dg.cpp | 49 ++++++++++++++- 6 files changed, 113 insertions(+), 20 deletions(-) diff --git a/src/serac/numerics/functional/detail/segment_L2.inl b/src/serac/numerics/functional/detail/segment_L2.inl index 8abdbc87bb..518f8918ec 100644 --- a/src/serac/numerics/functional/detail/segment_L2.inl +++ b/src/serac/numerics/functional/detail/segment_L2.inl @@ -121,6 +121,35 @@ struct finite_element > { return output; } + template + static auto batch_apply_shape_fn_interior_face(int jx, tensor input, const TensorProductQuadratureRule&) + { + static constexpr bool apply_weights = false; + static constexpr auto B = calculate_B(); + + using source_t = decltype(get<0>(get<0>(T{})) + get<1>(get<0>(T{}))); + + tensor, q> output; + + for (int qx = 0; qx < q; qx++) { + double phi_j = B(qx, jx); + + auto& d00 = get<0>(get<0>(input(qx))); + auto& d01 = get<1>(get<0>(input(qx))); + auto& d10 = get<0>(get<1>(input(qx))); + auto& d11 = get<1>(get<1>(input(qx))); + + output[qx] = {(d00 + d01) * phi_j, (d10 + d11) * phi_j}; + } + + std::cout << input[0] << std::endl; + std::cout << output[0] << std::endl; + + return output; + } + + + template SERAC_HOST_DEVICE static auto interpolate(const dof_type_if& X, const TensorProductQuadratureRule&) { @@ -218,28 +247,38 @@ struct finite_element > { } } - template - SERAC_HOST_DEVICE static void integrate(const tensor, tensor >, q>& qf_output, + template + SERAC_HOST_DEVICE static void integrate(const tensor, q>& qf_output, const TensorProductQuadratureRule&, dof_type_if * element_residual, [[maybe_unused]] int step = 1) { - using buffer_type = tensor< double, q>; + + constexpr int ntrial = size(T{}) / c; + + using buffer_type = tensor; static constexpr bool apply_weights = true; static constexpr auto B = calculate_B(); - for (int i = 0; i < c; i++) { - buffer_type source_0; - buffer_type source_1; + for (int j = 0; j < ntrial; j++) { + for (int i = 0; i < c; i++) { + buffer_type source_0; + buffer_type source_1; - for (int qx = 0; qx < q; qx++) { - source_0(qx) = get<0>(qf_output[qx])[i]; - source_1(qx) = get<1>(qf_output[qx])[i]; - } + for (int qx = 0; qx < q; qx++) { + source_0(qx) = reinterpret_cast(&get<0>(qf_output[qx]))[i * ntrial + j]; + source_1(qx) = reinterpret_cast(&get<1>(qf_output[qx]))[i * ntrial + j]; + } - element_residual[0](i, 0) += dot(source_0, B); - element_residual[0](i, 1) += dot(source_1, B); + //std::cout << j << " " << i << ": " << std::endl; + //std::cout << " " << element_residual[j * step] << std::endl; + element_residual[j * step](i, 0) += dot(source_0, B); + //std::cout << " " << element_residual[j * step] << std::endl; + element_residual[j * step](i, 1) += dot(source_1, B); + //std::cout << " " << element_residual[j * step] << std::endl; + + } } } diff --git a/src/serac/numerics/functional/element_restriction.cpp b/src/serac/numerics/functional/element_restriction.cpp index 73e1c8e47e..99f67ecefa 100644 --- a/src/serac/numerics/functional/element_restriction.cpp +++ b/src/serac/numerics/functional/element_restriction.cpp @@ -615,6 +615,7 @@ void ElementRestriction::GetElementVDofs(int i, std::vector& vdofs) const { for (uint64_t c = 0; c < components; c++) { for (uint64_t j = 0; j < nodes_per_elem; j++) { + std::cout << j << " " << dof_info(i,j).index() << std::endl; vdofs[c * nodes_per_elem + j] = GetVDof(dof_info(i, j), c); } } diff --git a/src/serac/numerics/functional/functional.hpp b/src/serac/numerics/functional/functional.hpp index 4c7aa0d05d..012597b10f 100644 --- a/src/serac/numerics/functional/functional.hpp +++ b/src/serac/numerics/functional/functional.hpp @@ -707,9 +707,13 @@ class Functional { for (uint32_t j = 0; j < rows_per_elem; j++) { int row = int(test_vdofs[j].index()); + std::cout << row << " " << col << " " << K_e(e, i, j) << std::endl; + A_local.SearchRow(row, col) += K_e(e, i, j); } } + + std::cout << std::endl; } } diff --git a/src/serac/numerics/functional/interior_face_integral_kernels.hpp b/src/serac/numerics/functional/interior_face_integral_kernels.hpp index e2bd551723..16e9d0997c 100644 --- a/src/serac/numerics/functional/interior_face_integral_kernels.hpp +++ b/src/serac/numerics/functional/interior_face_integral_kernels.hpp @@ -298,7 +298,6 @@ void element_gradient_kernel([[maybe_unused]] ExecArrayView; using trial_element = finite_element; @@ -308,7 +307,7 @@ void element_gradient_kernel([[maybe_unused]] ExecArrayView(&dK(elements[e], 0, 0)); + auto* output_ptr = reinterpret_cast(&dK(e, 0, 0)); tensor derivatives{}; for (int q = 0; q < nquad; q++) { @@ -316,11 +315,12 @@ void element_gradient_kernel([[maybe_unused]] ExecArrayView& f, double t, const mfem::Vector& U, do double e2 = df1_fd[1].DistanceTo(df_jvp1.GetData()) / denominator; EXPECT_TRUE(fabs(e1 / e2 - 2.0) < 0.1 || fmin(e1, e2) < 1.0e-9); + df1_fd[0].Print(std::cout); + std::cout << std::endl; + df1_fd[1].Print(std::cout); + std::cout << std::endl; + df_jvp1.Print(std::cout); + // halving epsilon should make the error decrease // by about a factor of four for the center-difference stencil double e3 = df1_cd[0].DistanceTo(df_jvp1.GetData()) / denominator; diff --git a/src/serac/numerics/functional/tests/functional_basic_dg.cpp b/src/serac/numerics/functional/tests/functional_basic_dg.cpp index 4ffdaf4c18..7d2dd62da6 100644 --- a/src/serac/numerics/functional/tests/functional_basic_dg.cpp +++ b/src/serac/numerics/functional/tests/functional_basic_dg.cpp @@ -24,6 +24,44 @@ using namespace serac; using namespace serac::profiling; +template +void debug_sparse_matrix(serac::Functional& f, double t, const mfem::Vector& U, double epsilon = 1.0e-4) { + + mfem::Vector dU(U.Size()); + dU = 0.0; + + auto [value, dfdU] = f(t, serac::differentiate_wrt(U)); + std::unique_ptr dfdU_matrix = assemble(dfdU); + + std::cout << "{"; + for (int i = 0; i < U.Size(); i++) { + dU[i] = 1; + mfem::Vector df_jvp = dfdU(dU); // matrix-free + + std::cout << "{"; + for (int j = 0; j < df_jvp.Size(); j++) { + std::cout << df_jvp[j]; + if (j != df_jvp.Size() - 1) { + std::cout << ","; + } else { + std::cout << " "; + } + } + std::cout << "}"; + if (i != U.Size() - 1) { + std::cout << ",\n"; + } else { + std::cout << "\n"; + } + + dU[i] = 0; + } + std::cout << "}" << std::endl; + + dfdU_matrix->Print("K.mtx"); + +} + template void L2_test_2D() { @@ -31,7 +69,8 @@ void L2_test_2D() using test_space = L2; using trial_space = L2; - std::string meshfile = SERAC_REPO_DIR "/data/meshes/patch2D.mesh"; + //std::string meshfile = SERAC_REPO_DIR "/data/meshes/patch2D.mesh"; + std::string meshfile = SERAC_REPO_DIR "/data/meshes/two_tris.mesh"; auto mesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 0); @@ -51,7 +90,7 @@ void L2_test_2D() residual.AddInteriorFaceIntegral( Dimension{}, DependsOn<0>{}, [=](double /*t*/, auto X, auto velocity) { - +#if 0 // compute the surface normal auto dX_dxi = get(X); auto n = normalize(cross(dX_dxi)); @@ -66,12 +105,16 @@ void L2_test_2D() auto f_1 = u_1 * a; auto f_2 = u_2 * a; return serac::tuple{f_1, f_2}; - +#else + return velocity; +#endif }, interior_faces); double t = 0.0; check_gradient(residual, t, U); + debug_sparse_matrix(residual, t, U); + } TEST(basic, L2_test_2D_linear) { L2_test_2D<1>(); } From 938f788aea890571fb6a9490a8ed86ab88eb1c9c Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Tue, 5 Nov 2024 15:13:34 -0800 Subject: [PATCH 22/92] fix bug in element matrix calculation --- .../numerics/functional/detail/segment_L2.inl | 15 ++++++++------- .../functional/tests/functional_basic_dg.cpp | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/serac/numerics/functional/detail/segment_L2.inl b/src/serac/numerics/functional/detail/segment_L2.inl index 518f8918ec..2e222d70ad 100644 --- a/src/serac/numerics/functional/detail/segment_L2.inl +++ b/src/serac/numerics/functional/detail/segment_L2.inl @@ -132,19 +132,20 @@ struct finite_element > { tensor, q> output; for (int qx = 0; qx < q; qx++) { - double phi_j = B(qx, jx); + int j = jx % ndof; + int s = jx / ndof; + + double phi0_j = B(qx, j) * (s == 0); + double phi1_j = B(qx, j) * (s == 1); auto& d00 = get<0>(get<0>(input(qx))); auto& d01 = get<1>(get<0>(input(qx))); auto& d10 = get<0>(get<1>(input(qx))); auto& d11 = get<1>(get<1>(input(qx))); - output[qx] = {(d00 + d01) * phi_j, (d10 + d11) * phi_j}; + output[qx] = {d00 * phi0_j + d01 * phi1_j, d10 * phi0_j + d11 * phi1_j}; } - std::cout << input[0] << std::endl; - std::cout << output[0] << std::endl; - return output; } @@ -256,6 +257,8 @@ struct finite_element > { constexpr int ntrial = size(T{}) / c; + std::cout << "ntrial: " << ntrial << std::endl; + using buffer_type = tensor; static constexpr bool apply_weights = true; @@ -276,8 +279,6 @@ struct finite_element > { element_residual[j * step](i, 0) += dot(source_0, B); //std::cout << " " << element_residual[j * step] << std::endl; element_residual[j * step](i, 1) += dot(source_1, B); - //std::cout << " " << element_residual[j * step] << std::endl; - } } } diff --git a/src/serac/numerics/functional/tests/functional_basic_dg.cpp b/src/serac/numerics/functional/tests/functional_basic_dg.cpp index 7d2dd62da6..45ba8cde65 100644 --- a/src/serac/numerics/functional/tests/functional_basic_dg.cpp +++ b/src/serac/numerics/functional/tests/functional_basic_dg.cpp @@ -90,7 +90,7 @@ void L2_test_2D() residual.AddInteriorFaceIntegral( Dimension{}, DependsOn<0>{}, [=](double /*t*/, auto X, auto velocity) { -#if 0 +#if 1 // compute the surface normal auto dX_dxi = get(X); auto n = normalize(cross(dX_dxi)); From a25b0766634074d8f92cde55af0a8d887f237436 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Tue, 12 Nov 2024 13:45:44 -0800 Subject: [PATCH 23/92] add interior face functions for triangle and placeholders for quad --- .../functional/detail/quadrilateral_L2.inl | 71 ++++++++++++++++ .../numerics/functional/detail/segment_L2.inl | 2 - .../functional/detail/triangle_L2.inl | 85 +++++++++++++++---- .../interior_face_integral_kernels.hpp | 11 ++- .../functional/tests/functional_basic_dg.cpp | 68 +++++++++------ 5 files changed, 191 insertions(+), 46 deletions(-) diff --git a/src/serac/numerics/functional/detail/quadrilateral_L2.inl b/src/serac/numerics/functional/detail/quadrilateral_L2.inl index cd9c2f765a..3478f89a74 100644 --- a/src/serac/numerics/functional/detail/quadrilateral_L2.inl +++ b/src/serac/numerics/functional/detail/quadrilateral_L2.inl @@ -187,6 +187,38 @@ struct finite_element > { return output; } + template + static auto batch_apply_shape_fn_interior_face(int jx, tensor input, const TensorProductQuadratureRule&) + { + + static constexpr bool apply_weights = false; + static constexpr auto B = calculate_B(); + + using source_t = decltype(get<0>(get<0>(T{})) + get<1>(get<0>(T{}))); + + tensor, q*q> output; +#if 0 + for (int qx = 0; qx < q; qx++) { + int j = jx % ndof; + int s = jx / ndof; + + double phi0_j = B(qx, j) * (s == 0); + double phi1_j = B(qx, j) * (s == 1); + + auto& d00 = get<0>(get<0>(input(qx))); + auto& d01 = get<1>(get<0>(input(qx))); + auto& d10 = get<0>(get<1>(input(qx))); + auto& d11 = get<1>(get<1>(input(qx))); + + output[qx] = {d00 * phi0_j + d01 * phi1_j, d10 * phi0_j + d11 * phi1_j}; + } +#endif + + return output; + } + + + // we want to compute the following: // // X_q(u, v) := (B(u, i) * B(v, j)) * X_e(i, j) @@ -320,6 +352,45 @@ struct finite_element > { } } + template + SERAC_HOST_DEVICE static void integrate(const tensor, q*q>& qf_output, + const TensorProductQuadratureRule&, + dof_type_if * element_residual, + [[maybe_unused]] int step = 1) + { + +#if 0 + constexpr int ntrial = size(T{}) / c; + + std::cout << "ntrial: " << ntrial << std::endl; + + using buffer_type = tensor; + + static constexpr bool apply_weights = true; + static constexpr auto B = calculate_B(); + + for (int j = 0; j < ntrial; j++) { + for (int i = 0; i < c; i++) { + buffer_type source_0; + buffer_type source_1; + + for (int qx = 0; qx < q; qx++) { + source_0(qx) = reinterpret_cast(&get<0>(qf_output[qx]))[i * ntrial + j]; + source_1(qx) = reinterpret_cast(&get<1>(qf_output[qx]))[i * ntrial + j]; + } + + //std::cout << j << " " << i << ": " << std::endl; + //std::cout << " " << element_residual[j * step] << std::endl; + element_residual[j * step](i, 0) += dot(source_0, B); + //std::cout << " " << element_residual[j * step] << std::endl; + element_residual[j * step](i, 1) += dot(source_1, B); + } + } +#endif + } + + + #if 0 template diff --git a/src/serac/numerics/functional/detail/segment_L2.inl b/src/serac/numerics/functional/detail/segment_L2.inl index 2e222d70ad..8a7f165c03 100644 --- a/src/serac/numerics/functional/detail/segment_L2.inl +++ b/src/serac/numerics/functional/detail/segment_L2.inl @@ -149,8 +149,6 @@ struct finite_element > { return output; } - - template SERAC_HOST_DEVICE static auto interpolate(const dof_type_if& X, const TensorProductQuadratureRule&) { diff --git a/src/serac/numerics/functional/detail/triangle_L2.inl b/src/serac/numerics/functional/detail/triangle_L2.inl index 49a752b888..6e587b995f 100644 --- a/src/serac/numerics/functional/detail/triangle_L2.inl +++ b/src/serac/numerics/functional/detail/triangle_L2.inl @@ -34,7 +34,7 @@ struct finite_element > { typename std::conditional, tensor >::type; using dof_type = tensor; - using dof_type_if = tensor; + using dof_type_if = tensor; using value_type = typename std::conditional >::type; using derivative_type = @@ -250,7 +250,7 @@ struct finite_element > { } template - static auto batch_apply_shape_fn(int j, tensor input, const TensorProductQuadratureRule&) + static auto batch_apply_shape_fn(int j, const tensor & input, const TensorProductQuadratureRule&) { using source_t = decltype(get<0>(get<0>(in_t{})) + dot(get<1>(get<0>(in_t{})), tensor{})); using flux_t = decltype(get<0>(get<1>(in_t{})) + dot(get<1>(get<1>(in_t{})), tensor{})); @@ -275,6 +275,34 @@ struct finite_element > { return output; } + template + static auto batch_apply_shape_fn_interior_face(int jx, const tensor & input, const TensorProductQuadratureRule&) + { + constexpr auto xi = GaussLegendreNodes(); + + using source_t = decltype(get<0>(get<0>(T{})) + get<1>(get<0>(T{}))); + + static constexpr int Q = q * (q + 1) / 2; + tensor, Q> output; + + for (int i = 0; i < Q; i++) { + int j = jx % ndof; + int s = jx / ndof; + + double phi0_j = shape_function(xi[i], j) * (s == 0); + double phi1_j = shape_function(xi[i], j) * (s == 1); + + auto& d00 = get<0>(get<0>(input(i))); + auto& d01 = get<1>(get<0>(input(i))); + auto& d10 = get<0>(get<1>(input(i))); + auto& d11 = get<1>(get<1>(input(i))); + + output[i] = {d00 * phi0_j + d01 * phi1_j, d10 * phi0_j + d11 * phi1_j}; + } + + return output; + } + template SERAC_HOST_DEVICE static auto interpolate(const tensor& X, const TensorProductQuadratureRule&) { @@ -301,33 +329,24 @@ struct finite_element > { // overload for two-sided interior face kernels template - SERAC_HOST_DEVICE static auto interpolate([[maybe_unused]] const tensor& X, const TensorProductQuadratureRule&) + SERAC_HOST_DEVICE static auto interpolate(const dof_type_if& X, const TensorProductQuadratureRule&) { constexpr auto xi = GaussLegendreNodes(); static constexpr int num_quadrature_points = q * (q + 1) / 2; - tensor< tuple< tensor, tensor >, num_quadrature_points> output; - -#if 0 - // transpose the quadrature data into a flat tensor of tuples - union { - tensor, tensor >, num_quadrature_points> unflattened; - tensor flattened; - } output{}; + tensor< tuple< tensor, tensor >, num_quadrature_points > output{}; + // apply the shape functions for (int i = 0; i < c; i++) { for (int j = 0; j < num_quadrature_points; j++) { for (int k = 0; k < ndof; k++) { - get(output.unflattened[j])[i] += X(i, k) * shape_function(xi[j], k); - get(output.unflattened[j])[i] += X(i, k) * shape_function_gradient(xi[j], k); + get<0>(output[j])[i] += X[i][0][k] * shape_function(xi[j], k); + get<1>(output[j])[i] += X[i][1][k] * shape_function(xi[j], k); } } } - return output.flattened; -#endif return output; - } template @@ -373,5 +392,39 @@ struct finite_element > { } } } + + template + SERAC_HOST_DEVICE static void integrate(const tensor, (q*(q + 1))/2 > & qf_output, + const TensorProductQuadratureRule&, + dof_type_if * element_residual, + [[maybe_unused]] int step = 1) + { + + constexpr int ntrial = size(T{}) / c; + constexpr int num_quadrature_points = (q * (q + 1)) / 2; + constexpr auto integration_points = GaussLegendreNodes(); + constexpr auto integration_weights = GaussLegendreWeights(); + + std::cout << "ntrial: " << ntrial << std::endl; + + for (int j = 0; j < ntrial; j++) { + for (int i = 0; i < c; i++) { + for (int Q = 0; Q < num_quadrature_points; Q++) { + tensor xi = integration_points[Q]; + double wt = integration_weights[Q]; + + double source_0 = reinterpret_cast(&get<0>(qf_output[Q]))[i * ntrial + j]; + double source_1 = reinterpret_cast(&get<1>(qf_output[Q]))[i * ntrial + j]; + + for (int k = 0; k < ndof; k++) { + element_residual[j * step](i, 0, k) += (source_0 * shape_function(xi, k)) * wt; + element_residual[j * step](i, 1, k) += (source_1 * shape_function(xi, k)) * wt; + } + } + + } + } + } + }; /// @endcond diff --git a/src/serac/numerics/functional/interior_face_integral_kernels.hpp b/src/serac/numerics/functional/interior_face_integral_kernels.hpp index 16e9d0997c..8526fd8e95 100644 --- a/src/serac/numerics/functional/interior_face_integral_kernels.hpp +++ b/src/serac/numerics/functional/interior_face_integral_kernels.hpp @@ -309,15 +309,22 @@ void element_gradient_kernel([[maybe_unused]] ExecArrayView(&dK(e, 0, 0)); + auto* vec_ptr = reinterpret_cast< tensor * >(&dK(e, 0, 0)); + tensor derivatives{}; for (int q = 0; q < nquad; q++) { derivatives(q) = qf_derivatives[e * nquad + uint32_t(q)]; } - for (int J = 0; J < trial_element::ndof; J++) { + for (int J = 0; J < 2 * trial_element::ndof; J++) { auto source_and_flux = trial_element::batch_apply_shape_fn_interior_face(J, derivatives, rule); test_element::integrate(source_and_flux, rule, output_ptr + J, 2 * trial_element::ndof); - std::cout << *(output_ptr + J) << std::endl; + + for (int i = 0; i < 8; i++) { + std::cout << vec_ptr[i] << std::endl; + } + std::cout << std::endl; + } } diff --git a/src/serac/numerics/functional/tests/functional_basic_dg.cpp b/src/serac/numerics/functional/tests/functional_basic_dg.cpp index 45ba8cde65..0569bafc86 100644 --- a/src/serac/numerics/functional/tests/functional_basic_dg.cpp +++ b/src/serac/numerics/functional/tests/functional_basic_dg.cpp @@ -69,8 +69,8 @@ void L2_test_2D() using test_space = L2; using trial_space = L2; - //std::string meshfile = SERAC_REPO_DIR "/data/meshes/patch2D.mesh"; - std::string meshfile = SERAC_REPO_DIR "/data/meshes/two_tris.mesh"; + std::string meshfile = SERAC_REPO_DIR "/data/meshes/patch2D.mesh"; + //std::string meshfile = SERAC_REPO_DIR "/data/meshes/two_tris.mesh"; auto mesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 0); @@ -117,49 +117,65 @@ void L2_test_2D() } -TEST(basic, L2_test_2D_linear) { L2_test_2D<1>(); } +//TEST(basic, L2_test_2D_linear) { L2_test_2D<1>(); } +//TEST(basic, L2_test_2D_quadratic) { L2_test_2D<2>(); } -#if 0 template -void hcurl_test_3D() +void L2_test_3D() { constexpr int dim = 3; + using test_space = L2; + using trial_space = L2; - std::string meshfile = SERAC_REPO_DIR "/data/meshes/patch3D.mesh"; + std::string meshfile = SERAC_REPO_DIR "/data/meshes/patch3D_tets.mesh"; - auto mesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 1); + auto mesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 0); - // Create standard MFEM bilinear and linear forms on H1 - auto fec = mfem::ND_FECollection(p, dim); - mfem::ParFiniteElementSpace fespace(mesh.get(), &fec); + auto fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); + mfem::ParFiniteElementSpace fespace(mesh.get(), &fec, dim, serac::ordering); mfem::Vector U(fespace.TrueVSize()); U.Randomize(); - // Define the types for the test and trial spaces using the function arguments - using test_space = Hcurl

; - using trial_space = Hcurl

; - - // Construct the new functional object using the known test and trial spaces + // Construct the new functional object using the specified test and trial spaces Functional residual(&fespace, {&fespace}); + constexpr int DERIVATIVE = 1; + + Domain interior_faces = InteriorFaces(*mesh); + residual.AddInteriorFaceIntegral( - Dimension{}, DependsOn<0>{}, - [=](double /*t*/, auto /*x*/, auto vector_potential) { - auto [A, curl_A] = vector_potential; - auto source = dot(d00, A) + dot(d01, curl_A); - auto flux = dot(d10, A) + dot(d11, curl_A); - return serac::tuple{source, flux}; - }, - *mesh); + Dimension{}, DependsOn<0>{}, + [=](double /*t*/, auto X, auto velocity) { +#if 1 + // compute the surface normal + auto dX_dxi = get(X); + auto n = normalize(cross(dX_dxi)); - check_gradient(residual, t, U); -} + // extract the velocity values from each side of the interface + // note: the orientation convention is such that the normal + // computed as above will point from from side 1->2 + auto [u_1, u_2] = velocity; -TEST(basic, hcurl_test_3D_linear) { hcurl_test_3D<1>(); } + auto a = dot(u_2 - u_1, n); + auto f_1 = u_1 * a; + auto f_2 = u_2 * a; + return serac::tuple{f_1, f_2}; +#else + return velocity; #endif + }, interior_faces); + + double t = 0.0; + check_gradient(residual, t, U); + + debug_sparse_matrix(residual, t, U); + +} +TEST(basic, L2_test_3D_linear) { L2_test_3D<1>(); } +TEST(basic, L2_test_3D_quadratic) { L2_test_3D<2>(); } int main(int argc, char* argv[]) { From 2923c9e21f477b92c61d79e62020a6ec5feec0c5 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Tue, 12 Nov 2024 16:05:16 -0800 Subject: [PATCH 24/92] debugging diff in quadrilateral stiffness matrix calculation --- .../functional/detail/quadrilateral_L2.inl | 97 ++++++++++++------- src/serac/numerics/functional/functional.hpp | 29 ++++++ .../interior_face_integral_kernels.hpp | 10 +- .../functional/tests/check_gradient.hpp | 16 ++- .../functional/tests/functional_basic_dg.cpp | 77 +++------------ 5 files changed, 122 insertions(+), 107 deletions(-) diff --git a/src/serac/numerics/functional/detail/quadrilateral_L2.inl b/src/serac/numerics/functional/detail/quadrilateral_L2.inl index 3478f89a74..1d5af699f2 100644 --- a/src/serac/numerics/functional/detail/quadrilateral_L2.inl +++ b/src/serac/numerics/functional/detail/quadrilateral_L2.inl @@ -32,7 +32,7 @@ struct finite_element > { typename std::conditional, tensor >::type; using dof_type = tensor; - using dof_type_if = tensor; + using dof_type_if = tensor; using value_type = typename std::conditional >::type; using derivative_type = @@ -188,7 +188,7 @@ struct finite_element > { } template - static auto batch_apply_shape_fn_interior_face(int jx, tensor input, const TensorProductQuadratureRule&) + static auto batch_apply_shape_fn_interior_face(int j, tensor input, const TensorProductQuadratureRule&) { static constexpr bool apply_weights = false; @@ -196,23 +196,26 @@ struct finite_element > { using source_t = decltype(get<0>(get<0>(T{})) + get<1>(get<0>(T{}))); + int jx = j % n; + int jy = j / n; + int s = j / ndof; + tensor, q*q> output; -#if 0 - for (int qx = 0; qx < q; qx++) { - int j = jx % ndof; - int s = jx / ndof; - double phi0_j = B(qx, j) * (s == 0); - double phi1_j = B(qx, j) * (s == 1); + for (int qy = 0; qy < q; qy++) { + for (int qx = 0; qx < q; qx++) { + double phi0_j = B(qx, jx) * B(qy, jy) * (s == 0); + double phi1_j = B(qx, jx) * B(qy, jy) * (s == 1); - auto& d00 = get<0>(get<0>(input(qx))); - auto& d01 = get<1>(get<0>(input(qx))); - auto& d10 = get<0>(get<1>(input(qx))); - auto& d11 = get<1>(get<1>(input(qx))); + int Q = qy * q + qx; + auto& d00 = get<0>(get<0>(input(Q))); + auto& d01 = get<1>(get<0>(input(Q))); + auto& d10 = get<0>(get<1>(input(Q))); + auto& d11 = get<1>(get<1>(input(Q))); - output[qx] = {d00 * phi0_j + d01 * phi1_j, d10 * phi0_j + d11 * phi1_j}; + output[Q] = {d00 * phi0_j + d01 * phi1_j, d10 * phi0_j + d11 * phi1_j}; + } } -#endif return output; } @@ -279,27 +282,38 @@ struct finite_element > { { static constexpr bool apply_weights = false; static constexpr auto B = calculate_B(); - static constexpr auto G = calculate_G(); tensor< tuple< tensor, tensor >, q * q> output; -#if 0 tensor value{}; - // apply the shape functions + // side 0 for (int i = 0; i < c; i++) { - auto A0 = contract<1, 1>(X[i], B); + auto A0 = contract<1, 1>(X(i, 0), B); value(i) = contract<0, 1>(A0, B); } for (int qy = 0; qy < q; qy++) { for (int qx = 0; qx < q; qx++) { for (int i = 0; i < c; i++) { - get(output.two_dimensional(qy, qx))[i] = value(i, qy, qx); + get<0>(output[qy * q + qx])[i] = value(i, qy, qx); + } + } + } + + // side 1 + for (int i = 0; i < c; i++) { + auto A0 = contract<1, 1>(X(i, 1), B); + value(i) = contract<0, 1>(A0, B); + } + + for (int qy = 0; qy < q; qy++) { + for (int qx = 0; qx < q; qx++) { + for (int i = 0; i < c; i++) { + get<1>(output[qy * q + qx])[i] = value(i, qy, qx); } } } -#endif return output; } @@ -359,34 +373,43 @@ struct finite_element > { [[maybe_unused]] int step = 1) { -#if 0 constexpr int ntrial = size(T{}) / c; - - std::cout << "ntrial: " << ntrial << std::endl; - - using buffer_type = tensor; - static constexpr bool apply_weights = true; static constexpr auto B = calculate_B(); for (int j = 0; j < ntrial; j++) { for (int i = 0; i < c; i++) { - buffer_type source_0; - buffer_type source_1; + tensor source; - for (int qx = 0; qx < q; qx++) { - source_0(qx) = reinterpret_cast(&get<0>(qf_output[qx]))[i * ntrial + j]; - source_1(qx) = reinterpret_cast(&get<1>(qf_output[qx]))[i * ntrial + j]; + // side 0 + { + for (int qy = 0; qy < q; qy++) { + for (int qx = 0; qx < q; qx++) { + int Q = qy * q + qx; + source(qy, qx) = reinterpret_cast(&get<0>(qf_output[Q]))[i * ntrial + j]; + } + } + + auto A0 = contract<1, 0>(source, B); + element_residual[j * step](i, 0) += contract<0, 0>(A0, B); + } + + // side 1 + { + for (int qy = 0; qy < q; qy++) { + for (int qx = 0; qx < q; qx++) { + int Q = qy * q + qx; + source(qy, qx) = reinterpret_cast(&get<1>(qf_output[Q]))[i * ntrial + j]; + } + } + + auto A0 = contract<1, 0>(source, B); + element_residual[j * step](i, 1) += contract<0, 0>(A0, B); } - //std::cout << j << " " << i << ": " << std::endl; - //std::cout << " " << element_residual[j * step] << std::endl; - element_residual[j * step](i, 0) += dot(source_0, B); - //std::cout << " " << element_residual[j * step] << std::endl; - element_residual[j * step](i, 1) += dot(source_1, B); } } -#endif + } diff --git a/src/serac/numerics/functional/functional.hpp b/src/serac/numerics/functional/functional.hpp index 012597b10f..8d17b9f023 100644 --- a/src/serac/numerics/functional/functional.hpp +++ b/src/serac/numerics/functional/functional.hpp @@ -381,6 +381,17 @@ class Functional { AddBoundaryIntegral(Dimension<2>{}, which_args, integrand, domain); } + void print_vector(const mfem::Vector & v, std::string name) const { + std::cout << name << ": {"; + for (int i = 0; i < v.Size(); i++) { + std::cout << v[i]; + if (i != v.Size() - 1) { + std::cout << ","; + } + } + std::cout << "}\n"; + } + /** * @brief this function computes the directional derivative of `serac::Functional::operator()` * @@ -394,13 +405,19 @@ class Functional { */ void ActionOfGradient(const mfem::Vector& input_T, mfem::Vector& output_T, uint32_t which) const { + + //print_vector(input_T, "input_T"); + P_trial_[which]->Mult(input_T, input_L_[which]); output_L_ = 0.0; + //print_vector(input_L_[which], "input_L_[which]"); + for (auto& integral : integrals_) { Domain & dom = integral.domain_; + const serac::BlockElementRestriction & G_trial = dom.get_restriction(trial_function_spaces_[which]); input_E_buffer_[which].SetSize(int(G_trial.ESize())); input_E_[which].Update(input_E_buffer_[which], G_trial.bOffsets()); @@ -409,14 +426,26 @@ class Functional { const serac::BlockElementRestriction & G_test = dom.get_restriction(test_function_space_); output_E_buffer_.SetSize(int(G_test.ESize())); output_E_.Update(output_E_buffer_, G_test.bOffsets()); + + //print_vector(input_E_[which], "input_E_[which]"); + //print_vector(output_E_, "output_E_"); + integral.GradientMult(input_E_[which], output_E_, which); + //print_vector(output_E_, "output_E_"); + // scatter-add to compute residuals on the local processor G_test.ScatterAdd(output_E_, output_L_); + + //print_vector(output_L_, "output_L_"); + } // scatter-add to compute global residuals P_test_->MultTranspose(output_L_, output_T); + + //print_vector(output_T, "output_T"); + } /** diff --git a/src/serac/numerics/functional/interior_face_integral_kernels.hpp b/src/serac/numerics/functional/interior_face_integral_kernels.hpp index 8526fd8e95..b84690f2e7 100644 --- a/src/serac/numerics/functional/interior_face_integral_kernels.hpp +++ b/src/serac/numerics/functional/interior_face_integral_kernels.hpp @@ -309,22 +309,26 @@ void element_gradient_kernel([[maybe_unused]] ExecArrayView(&dK(e, 0, 0)); - auto* vec_ptr = reinterpret_cast< tensor * >(&dK(e, 0, 0)); + auto* vec_ptr = reinterpret_cast< tensor * >(&dK(e, 0, 0)); tensor derivatives{}; for (int q = 0; q < nquad; q++) { derivatives(q) = qf_derivatives[e * nquad + uint32_t(q)]; } + for (int i = 0; i < 2 * trial_element::ndof * trial_element::components; i++) { + std::cout << vec_ptr[i] << std::endl; + } + std::cout << std::endl; + for (int J = 0; J < 2 * trial_element::ndof; J++) { auto source_and_flux = trial_element::batch_apply_shape_fn_interior_face(J, derivatives, rule); test_element::integrate(source_and_flux, rule, output_ptr + J, 2 * trial_element::ndof); - for (int i = 0; i < 8; i++) { + for (int i = 0; i < 2 * trial_element::ndof * trial_element::components; i++) { std::cout << vec_ptr[i] << std::endl; } std::cout << std::endl; - } } diff --git a/src/serac/numerics/functional/tests/check_gradient.hpp b/src/serac/numerics/functional/tests/check_gradient.hpp index 81352ecf52..197d1597d3 100644 --- a/src/serac/numerics/functional/tests/check_gradient.hpp +++ b/src/serac/numerics/functional/tests/check_gradient.hpp @@ -70,18 +70,32 @@ void check_gradient(serac::Functional& f, double t, const mfem::Vector& U, do // by about a factor of two for the forward-difference stencil double e1 = df1_fd[0].DistanceTo(df_jvp1.GetData()) / denominator; double e2 = df1_fd[1].DistanceTo(df_jvp1.GetData()) / denominator; - EXPECT_TRUE(fabs(e1 / e2 - 2.0) < 0.1 || fmin(e1, e2) < 1.0e-9); df1_fd[0].Print(std::cout); std::cout << std::endl; df1_fd[1].Print(std::cout); std::cout << std::endl; df_jvp1.Print(std::cout); + std::cout << std::endl; + df_jvp2.Print(std::cout); + std::cout << std::endl; + + std::cout << e1 << " " << e2 << std::endl; + EXPECT_TRUE(fabs(e1 / e2 - 2.0) < 0.1 || fmin(e1, e2) < 1.0e-9); + + df1_cd[0].Print(std::cout); + std::cout << std::endl; + df1_cd[1].Print(std::cout); + std::cout << std::endl; + df_jvp1.Print(std::cout); + + std::cout << "denominator: " << denominator << std::endl; // halving epsilon should make the error decrease // by about a factor of four for the center-difference stencil double e3 = df1_cd[0].DistanceTo(df_jvp1.GetData()) / denominator; double e4 = df1_cd[1].DistanceTo(df_jvp1.GetData()) / denominator; + std::cout << e3 << " " << e4 << std::endl; EXPECT_TRUE((fabs(e3 / e4 - 4.0) < 0.1) || fmin(e3, e4) < 1.0e-9); } diff --git a/src/serac/numerics/functional/tests/functional_basic_dg.cpp b/src/serac/numerics/functional/tests/functional_basic_dg.cpp index 0569bafc86..f26d1efa73 100644 --- a/src/serac/numerics/functional/tests/functional_basic_dg.cpp +++ b/src/serac/numerics/functional/tests/functional_basic_dg.cpp @@ -62,16 +62,12 @@ void debug_sparse_matrix(serac::Functional& f, double t, const mfem::Vector& } -template -void L2_test_2D() +template +void L2_test(std::string meshfile) { - constexpr int dim = 2; using test_space = L2; using trial_space = L2; - std::string meshfile = SERAC_REPO_DIR "/data/meshes/patch2D.mesh"; - //std::string meshfile = SERAC_REPO_DIR "/data/meshes/two_tris.mesh"; - auto mesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 0); auto fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); @@ -90,7 +86,7 @@ void L2_test_2D() residual.AddInteriorFaceIntegral( Dimension{}, DependsOn<0>{}, [=](double /*t*/, auto X, auto velocity) { -#if 1 +#if 0 // compute the surface normal auto dX_dxi = get(X); auto n = normalize(cross(dX_dxi)); @@ -117,65 +113,14 @@ void L2_test_2D() } -//TEST(basic, L2_test_2D_linear) { L2_test_2D<1>(); } -//TEST(basic, L2_test_2D_quadratic) { L2_test_2D<2>(); } - -template -void L2_test_3D() -{ - constexpr int dim = 3; - using test_space = L2; - using trial_space = L2; - - std::string meshfile = SERAC_REPO_DIR "/data/meshes/patch3D_tets.mesh"; - - auto mesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 0); - - auto fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); - mfem::ParFiniteElementSpace fespace(mesh.get(), &fec, dim, serac::ordering); - - mfem::Vector U(fespace.TrueVSize()); - U.Randomize(); - - // Construct the new functional object using the specified test and trial spaces - Functional residual(&fespace, {&fespace}); - - constexpr int DERIVATIVE = 1; - - Domain interior_faces = InteriorFaces(*mesh); - - residual.AddInteriorFaceIntegral( - Dimension{}, DependsOn<0>{}, - [=](double /*t*/, auto X, auto velocity) { -#if 1 - // compute the surface normal - auto dX_dxi = get(X); - auto n = normalize(cross(dX_dxi)); - - // extract the velocity values from each side of the interface - // note: the orientation convention is such that the normal - // computed as above will point from from side 1->2 - auto [u_1, u_2] = velocity; - - auto a = dot(u_2 - u_1, n); - - auto f_1 = u_1 * a; - auto f_2 = u_2 * a; - return serac::tuple{f_1, f_2}; -#else - return velocity; -#endif - }, interior_faces); - - double t = 0.0; - check_gradient(residual, t, U); - - debug_sparse_matrix(residual, t, U); - -} - -TEST(basic, L2_test_3D_linear) { L2_test_3D<1>(); } -TEST(basic, L2_test_3D_quadratic) { L2_test_3D<2>(); } +//TEST(basic, L2_test_edges_linear) { L2_test<2, 1>(SERAC_REPO_DIR "/data/meshes/patch2D.mesh"); } +//TEST(basic, L2_test_edges_quadratic) { L2_test<2, 2>(SERAC_REPO_DIR "/data/meshes/patch2D.mesh"); } +// +//TEST(basic, L2_test_tets_linear) { L2_test<3, 1>(SERAC_REPO_DIR "/data/meshes/patch3D_tets.mesh"); } +//TEST(basic, L2_test_tets_quadratic) { L2_test<3, 2>(SERAC_REPO_DIR "/data/meshes/patch3D_tets.mesh"); } +// +TEST(basic, L2_test_hexes_linear) { L2_test<3, 1>(SERAC_REPO_DIR "/data/meshes/patch3D_hexes.mesh"); } +//TEST(basic, L2_test_hexes_quadratic) { L2_test<3, 2>(SERAC_REPO_DIR "/data/meshes/patch3D_hexes.mesh"); } int main(int argc, char* argv[]) { From 303625ab880ac334b3cae433ef82082218d1fffd Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Wed, 13 Nov 2024 17:01:47 -0800 Subject: [PATCH 25/92] this one line change took me like 3 hours to find --- src/serac/numerics/functional/detail/quadrilateral_L2.inl | 2 +- src/serac/numerics/functional/tests/functional_basic_dg.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/serac/numerics/functional/detail/quadrilateral_L2.inl b/src/serac/numerics/functional/detail/quadrilateral_L2.inl index 1d5af699f2..75c5896f0c 100644 --- a/src/serac/numerics/functional/detail/quadrilateral_L2.inl +++ b/src/serac/numerics/functional/detail/quadrilateral_L2.inl @@ -197,7 +197,7 @@ struct finite_element > { using source_t = decltype(get<0>(get<0>(T{})) + get<1>(get<0>(T{}))); int jx = j % n; - int jy = j / n; + int jy = (j % ndof) / n; int s = j / ndof; tensor, q*q> output; diff --git a/src/serac/numerics/functional/tests/functional_basic_dg.cpp b/src/serac/numerics/functional/tests/functional_basic_dg.cpp index f26d1efa73..cefe20d531 100644 --- a/src/serac/numerics/functional/tests/functional_basic_dg.cpp +++ b/src/serac/numerics/functional/tests/functional_basic_dg.cpp @@ -86,7 +86,7 @@ void L2_test(std::string meshfile) residual.AddInteriorFaceIntegral( Dimension{}, DependsOn<0>{}, [=](double /*t*/, auto X, auto velocity) { -#if 0 +#if 1 // compute the surface normal auto dX_dxi = get(X); auto n = normalize(cross(dX_dxi)); @@ -120,7 +120,7 @@ void L2_test(std::string meshfile) //TEST(basic, L2_test_tets_quadratic) { L2_test<3, 2>(SERAC_REPO_DIR "/data/meshes/patch3D_tets.mesh"); } // TEST(basic, L2_test_hexes_linear) { L2_test<3, 1>(SERAC_REPO_DIR "/data/meshes/patch3D_hexes.mesh"); } -//TEST(basic, L2_test_hexes_quadratic) { L2_test<3, 2>(SERAC_REPO_DIR "/data/meshes/patch3D_hexes.mesh"); } +TEST(basic, L2_test_hexes_quadratic) { L2_test<3, 2>(SERAC_REPO_DIR "/data/meshes/patch3D_hexes.mesh"); } int main(int argc, char* argv[]) { From 0e9461928c1e17b5a2907a011b833c07dffdb7ec Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Fri, 15 Nov 2024 15:29:33 -0800 Subject: [PATCH 26/92] plumb new element restrictions through QoI specialization of serac::Functional, delete debugging print statements --- src/serac/numerics/functional/detail/qoi.inl | 1 + .../functional/detail/quadrilateral_L2.inl | 21 +- .../numerics/functional/detail/segment_L2.inl | 10 +- .../functional/detail/triangle_L2.inl | 15 +- src/serac/numerics/functional/domain.hpp | 21 ++ .../functional/element_restriction.cpp | 1 - src/serac/numerics/functional/functional.hpp | 32 --- .../numerics/functional/functional_qoi.inl | 249 +++++++++--------- .../interior_face_integral_kernels.hpp | 23 +- .../functional/tests/check_gradient.hpp | 58 ++-- .../functional/tests/functional_basic_dg.cpp | 108 ++++---- 11 files changed, 273 insertions(+), 266 deletions(-) diff --git a/src/serac/numerics/functional/detail/qoi.inl b/src/serac/numerics/functional/detail/qoi.inl index 33e747ea4e..a44ce34207 100644 --- a/src/serac/numerics/functional/detail/qoi.inl +++ b/src/serac/numerics/functional/detail/qoi.inl @@ -19,6 +19,7 @@ struct finite_element { static constexpr int ndof = 1; using dof_type = double; + using dof_type_if = double; using residual_type = double; SERAC_HOST_DEVICE static constexpr double shape_functions(double /* xi */) { return 1.0; } diff --git a/src/serac/numerics/functional/detail/quadrilateral_L2.inl b/src/serac/numerics/functional/detail/quadrilateral_L2.inl index 75c5896f0c..824a431b78 100644 --- a/src/serac/numerics/functional/detail/quadrilateral_L2.inl +++ b/src/serac/numerics/functional/detail/quadrilateral_L2.inl @@ -175,10 +175,10 @@ struct finite_element > { tensor dphi_j_dxi = {G(qx, jx) * B(qy, jy), B(qx, jx) * G(qy, jy)}; int Q = qy * q + qx; - auto& d00 = get<0>(get<0>(input(Q))); - auto& d01 = get<1>(get<0>(input(Q))); - auto& d10 = get<0>(get<1>(input(Q))); - auto& d11 = get<1>(get<1>(input(Q))); + const auto & d00 = get<0>(get<0>(input(Q))); + const auto & d01 = get<1>(get<0>(input(Q))); + const auto & d10 = get<0>(get<1>(input(Q))); + const auto & d11 = get<1>(get<1>(input(Q))); output[Q] = {d00 * phi_j + dot(d01, dphi_j_dxi), d10 * phi_j + dot(d11, dphi_j_dxi)}; } @@ -194,13 +194,14 @@ struct finite_element > { static constexpr bool apply_weights = false; static constexpr auto B = calculate_B(); - using source_t = decltype(get<0>(get<0>(T{})) + get<1>(get<0>(T{}))); + using source0_t = decltype(get<0>(get<0>(T{})) + get<1>(get<0>(T{}))); + using source1_t = decltype(get<0>(get<1>(T{})) + get<1>(get<1>(T{}))); int jx = j % n; int jy = (j % ndof) / n; int s = j / ndof; - tensor, q*q> output; + tensor, q*q> output; for (int qy = 0; qy < q; qy++) { for (int qx = 0; qx < q; qx++) { @@ -208,10 +209,10 @@ struct finite_element > { double phi1_j = B(qx, jx) * B(qy, jy) * (s == 1); int Q = qy * q + qx; - auto& d00 = get<0>(get<0>(input(Q))); - auto& d01 = get<1>(get<0>(input(Q))); - auto& d10 = get<0>(get<1>(input(Q))); - auto& d11 = get<1>(get<1>(input(Q))); + const auto & d00 = get<0>(get<0>(input(Q))); + const auto & d01 = get<1>(get<0>(input(Q))); + const auto & d10 = get<0>(get<1>(input(Q))); + const auto & d11 = get<1>(get<1>(input(Q))); output[Q] = {d00 * phi0_j + d01 * phi1_j, d10 * phi0_j + d11 * phi1_j}; } diff --git a/src/serac/numerics/functional/detail/segment_L2.inl b/src/serac/numerics/functional/detail/segment_L2.inl index 8a7f165c03..e15451ae2e 100644 --- a/src/serac/numerics/functional/detail/segment_L2.inl +++ b/src/serac/numerics/functional/detail/segment_L2.inl @@ -127,9 +127,10 @@ struct finite_element > { static constexpr bool apply_weights = false; static constexpr auto B = calculate_B(); - using source_t = decltype(get<0>(get<0>(T{})) + get<1>(get<0>(T{}))); + using source0_t = decltype(get<0>(get<0>(T{})) + get<1>(get<0>(T{}))); + using source1_t = decltype(get<0>(get<1>(T{})) + get<1>(get<1>(T{}))); - tensor, q> output; + tensor, q> output; for (int qx = 0; qx < q; qx++) { int j = jx % ndof; @@ -255,8 +256,6 @@ struct finite_element > { constexpr int ntrial = size(T{}) / c; - std::cout << "ntrial: " << ntrial << std::endl; - using buffer_type = tensor; static constexpr bool apply_weights = true; @@ -272,10 +271,7 @@ struct finite_element > { source_1(qx) = reinterpret_cast(&get<1>(qf_output[qx]))[i * ntrial + j]; } - //std::cout << j << " " << i << ": " << std::endl; - //std::cout << " " << element_residual[j * step] << std::endl; element_residual[j * step](i, 0) += dot(source_0, B); - //std::cout << " " << element_residual[j * step] << std::endl; element_residual[j * step](i, 1) += dot(source_1, B); } } diff --git a/src/serac/numerics/functional/detail/triangle_L2.inl b/src/serac/numerics/functional/detail/triangle_L2.inl index 6e587b995f..ee0c549f10 100644 --- a/src/serac/numerics/functional/detail/triangle_L2.inl +++ b/src/serac/numerics/functional/detail/triangle_L2.inl @@ -280,10 +280,11 @@ struct finite_element > { { constexpr auto xi = GaussLegendreNodes(); - using source_t = decltype(get<0>(get<0>(T{})) + get<1>(get<0>(T{}))); + using source0_t = decltype(get<0>(get<0>(T{})) + get<1>(get<0>(T{}))); + using source1_t = decltype(get<0>(get<1>(T{})) + get<1>(get<1>(T{}))); static constexpr int Q = q * (q + 1) / 2; - tensor, Q> output; + tensor, Q> output; for (int i = 0; i < Q; i++) { int j = jx % ndof; @@ -292,10 +293,10 @@ struct finite_element > { double phi0_j = shape_function(xi[i], j) * (s == 0); double phi1_j = shape_function(xi[i], j) * (s == 1); - auto& d00 = get<0>(get<0>(input(i))); - auto& d01 = get<1>(get<0>(input(i))); - auto& d10 = get<0>(get<1>(input(i))); - auto& d11 = get<1>(get<1>(input(i))); + const auto & d00 = get<0>(get<0>(input(i))); + const auto & d01 = get<1>(get<0>(input(i))); + const auto & d10 = get<0>(get<1>(input(i))); + const auto & d11 = get<1>(get<1>(input(i))); output[i] = {d00 * phi0_j + d01 * phi1_j, d10 * phi0_j + d11 * phi1_j}; } @@ -405,8 +406,6 @@ struct finite_element > { constexpr auto integration_points = GaussLegendreNodes(); constexpr auto integration_weights = GaussLegendreWeights(); - std::cout << "ntrial: " << ntrial << std::endl; - for (int j = 0; j < ntrial; j++) { for (int i = 0; i < c; i++) { for (int Q = 0; Q < num_quadrature_points; Q++) { diff --git a/src/serac/numerics/functional/domain.hpp b/src/serac/numerics/functional/domain.hpp index 9408b68373..c0ae911753 100644 --- a/src/serac/numerics/functional/domain.hpp +++ b/src/serac/numerics/functional/domain.hpp @@ -151,6 +151,27 @@ struct Domain { exit(1); } + int total_elements() const { + return int(vertex_ids_.size() + edge_ids_.size() + tri_ids_.size() + quad_ids_.size() + tet_ids_.size() + hex_ids_.size()); + } + + mfem::Array bOffsets() const { + mfem::Array offsets(mfem::Geometry::NUM_GEOMETRIES + 1); + + int total = 0; + offsets[mfem::Geometry::POINT] = total; total += vertex_ids_.size(); + offsets[mfem::Geometry::SEGMENT] = total; total += edge_ids_.size(); + offsets[mfem::Geometry::TRIANGLE] = total; total += tri_ids_.size(); + offsets[mfem::Geometry::SQUARE] = total; total += quad_ids_.size(); + offsets[mfem::Geometry::TETRAHEDRON] = total; total += tet_ids_.size(); + offsets[mfem::Geometry::CUBE] = total; total += hex_ids_.size(); + offsets[mfem::Geometry::PRISM] = total; + offsets[mfem::Geometry::PYRAMID] = total; + offsets[mfem::Geometry::NUM_GEOMETRIES] = total; + + return offsets; + } + /// @brief get mfem degree of freedom list for a given FiniteElementSpace mfem::Array dof_list(mfem::FiniteElementSpace* fes) const; diff --git a/src/serac/numerics/functional/element_restriction.cpp b/src/serac/numerics/functional/element_restriction.cpp index 99f67ecefa..73e1c8e47e 100644 --- a/src/serac/numerics/functional/element_restriction.cpp +++ b/src/serac/numerics/functional/element_restriction.cpp @@ -615,7 +615,6 @@ void ElementRestriction::GetElementVDofs(int i, std::vector& vdofs) const { for (uint64_t c = 0; c < components; c++) { for (uint64_t j = 0; j < nodes_per_elem; j++) { - std::cout << j << " " << dof_info(i,j).index() << std::endl; vdofs[c * nodes_per_elem + j] = GetVDof(dof_info(i, j), c); } } diff --git a/src/serac/numerics/functional/functional.hpp b/src/serac/numerics/functional/functional.hpp index 8d17b9f023..b2d720264d 100644 --- a/src/serac/numerics/functional/functional.hpp +++ b/src/serac/numerics/functional/functional.hpp @@ -381,17 +381,6 @@ class Functional { AddBoundaryIntegral(Dimension<2>{}, which_args, integrand, domain); } - void print_vector(const mfem::Vector & v, std::string name) const { - std::cout << name << ": {"; - for (int i = 0; i < v.Size(); i++) { - std::cout << v[i]; - if (i != v.Size() - 1) { - std::cout << ","; - } - } - std::cout << "}\n"; - } - /** * @brief this function computes the directional derivative of `serac::Functional::operator()` * @@ -405,19 +394,13 @@ class Functional { */ void ActionOfGradient(const mfem::Vector& input_T, mfem::Vector& output_T, uint32_t which) const { - - //print_vector(input_T, "input_T"); - P_trial_[which]->Mult(input_T, input_L_[which]); output_L_ = 0.0; - //print_vector(input_L_[which], "input_L_[which]"); - for (auto& integral : integrals_) { Domain & dom = integral.domain_; - const serac::BlockElementRestriction & G_trial = dom.get_restriction(trial_function_spaces_[which]); input_E_buffer_[which].SetSize(int(G_trial.ESize())); input_E_[which].Update(input_E_buffer_[which], G_trial.bOffsets()); @@ -427,25 +410,15 @@ class Functional { output_E_buffer_.SetSize(int(G_test.ESize())); output_E_.Update(output_E_buffer_, G_test.bOffsets()); - //print_vector(input_E_[which], "input_E_[which]"); - //print_vector(output_E_, "output_E_"); - integral.GradientMult(input_E_[which], output_E_, which); - //print_vector(output_E_, "output_E_"); - // scatter-add to compute residuals on the local processor G_test.ScatterAdd(output_E_, output_L_); - - //print_vector(output_L_, "output_L_"); - } // scatter-add to compute global residuals P_test_->MultTranspose(output_L_, output_T); - //print_vector(output_T, "output_T"); - } /** @@ -735,14 +708,9 @@ class Functional { for (uint32_t j = 0; j < rows_per_elem; j++) { int row = int(test_vdofs[j].index()); - - std::cout << row << " " << col << " " << K_e(e, i, j) << std::endl; - A_local.SearchRow(row, col) += K_e(e, i, j); } } - - std::cout << std::endl; } } diff --git a/src/serac/numerics/functional/functional_qoi.inl b/src/serac/numerics/functional/functional_qoi.inl index 2c0cea5778..aeb43f6a8e 100644 --- a/src/serac/numerics/functional/functional_qoi.inl +++ b/src/serac/numerics/functional/functional_qoi.inl @@ -83,51 +83,26 @@ public: Functional(std::array trial_fes) : test_fec_(0, trial_fes[0]->GetMesh()->Dimension()), test_space_(dynamic_cast(trial_fes[0]->GetMesh()), &test_fec_, 1, serac::ordering), - trial_space_(trial_fes) + trial_space_(trial_fes), + mem_type(mfem::Device::GetMemoryType()) { - auto* mesh = trial_fes[0]->GetMesh(); - - auto mem_type = mfem::Device::GetMemoryType(); - - for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements}) { - input_E_[type].resize(num_trial_spaces); - } + SERAC_MARK_FUNCTION; for (uint32_t i = 0; i < num_trial_spaces; i++) { P_trial_[i] = trial_space_[i]->GetProlongationMatrix(); input_L_[i].SetSize(P_trial_[i]->Height(), mfem::Device::GetMemoryType()); - for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements}) { - if (type == Domain::Type::Elements) { - G_trial_[type][i] = BlockElementRestriction(trial_fes[i]); - } else { - G_trial_[type][i] = BlockElementRestriction(trial_fes[i], FaceType::BOUNDARY); - } - - // note: we have to use "Update" here, as mfem::BlockVector's - // copy assignment ctor (operator=) doesn't let you make changes - // to the block size - input_E_[type][i].Update(G_trial_[type][i].bOffsets(), mem_type); - } + // create the necessary number of empty mfem::Vectors, to be resized later + input_E_.push_back({}); + input_E_buffer_.push_back({}); } - for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements}) { - std::array counts{}; - if (type == Domain::Type::Elements) { - counts = geometry_counts(*mesh); - } else { - counts = boundary_geometry_counts(*mesh); - } - - mfem::Array offsets(mfem::Geometry::NUM_GEOMETRIES + 1); - offsets[0] = 0; - for (int i = 0; i < mfem::Geometry::NUM_GEOMETRIES; i++) { - auto g = mfem::Geometry::Type(i); - offsets[g + 1] = offsets[g] + int(counts[uint32_t(g)]); - } - - output_E_[type].Update(offsets, mem_type); + std::array trial_families = {trials::family ...}; + std::array trial_orders = {trials::order ...}; + std::array trial_components = {trials::components ...}; + for (uint32_t i = 0; i < num_trial_spaces; i++) { + trial_function_spaces_[i] = {trial_families[i], trial_orders[i], trial_components[i]}; } G_test_ = QoIElementRestriction(); @@ -143,6 +118,7 @@ public: for (uint32_t i = 0; i < num_trial_spaces; i++) { grad_.emplace_back(*this, i); } + } /** @@ -151,28 +127,13 @@ public: * @tparam lambda the type of the integrand functor: must implement operator() with an appropriate function signature * @tparam qpt_data_type The type of the data to store for each quadrature point * @param[in] integrand The user-provided quadrature function, see @p Integral - * @param[in] mesh The domain on which to evaluate the integral + * @param[in] domain The domain on which to evaluate the integral * @param[in] qdata The data structure containing per-quadrature-point data * @note The @p Dimension parameters are used to assist in the deduction of the @a geometry_dim * and @a spatial_dim template parameter */ - template - void AddDomainIntegral(Dimension, DependsOn, const lambda& integrand, mfem::Mesh& mesh, - std::shared_ptr> qdata = NoQData) - { - if (mesh.GetNE() == 0) return; - - SLIC_ERROR_ROOT_IF(dim != mesh.Dimension(), "invalid mesh dimension for domain integral"); - - check_for_unsupported_elements(mesh); - check_for_missing_nodal_gridfunc(mesh); - using signature = test(decltype(serac::type(trial_spaces))...); - integrals_.push_back( - MakeDomainIntegral(EntireDomain(mesh), integrand, qdata, std::vector{args...})); - } - /// @overload template void AddDomainIntegral(Dimension, DependsOn, const lambda& integrand, Domain& domain, std::shared_ptr> qdata = NoQData) @@ -184,6 +145,11 @@ public: check_for_unsupported_elements(domain.mesh_); check_for_missing_nodal_gridfunc(domain.mesh_); + std::vector< uint32_t > arg_vec = {args ...}; + for (uint32_t i : arg_vec) { + domain.insert_restriction(trial_space_[i], trial_function_spaces_[i]); + } + using signature = test(decltype(serac::type(trial_spaces))...); integrals_.push_back( MakeDomainIntegral(domain, integrand, qdata, std::vector{args...})); @@ -200,22 +166,8 @@ public: * @note The @p Dimension parameters are used to assist in the deduction of the @a geometry_dim * and @a spatial_dim template parameter */ - template - void AddBoundaryIntegral(Dimension, DependsOn, const lambda& integrand, mfem::Mesh& mesh) - { - auto num_bdr_elements = mesh.GetNBE(); - if (num_bdr_elements == 0) return; - - check_for_missing_nodal_gridfunc(mesh); - - using signature = test(decltype(serac::type(trial_spaces))...); - integrals_.push_back( - MakeBoundaryIntegral(EntireBoundary(mesh), integrand, std::vector{args...})); - } - - /// @overload template - void AddBoundaryIntegral(Dimension, DependsOn, const lambda& integrand, const Domain& domain) + void AddBoundaryIntegral(Dimension, DependsOn, const lambda& integrand, Domain& domain) { auto num_bdr_elements = domain.mesh_.GetNBE(); if (num_bdr_elements == 0) return; @@ -224,10 +176,33 @@ public: check_for_missing_nodal_gridfunc(domain.mesh_); + std::vector< uint32_t > arg_vec = {args ...}; + for (uint32_t i : arg_vec) { + domain.insert_restriction(trial_space_[i], trial_function_spaces_[i]); + } + using signature = test(decltype(serac::type(trial_spaces))...); integrals_.push_back(MakeBoundaryIntegral(domain, integrand, std::vector{args...})); } + /** + * @brief TODO + */ + template + void AddInteriorFaceIntegral(Dimension, DependsOn, const Integrand& integrand, Domain& domain) + { + check_for_missing_nodal_gridfunc(domain.mesh_); + + std::vector< uint32_t > arg_vec = {args ...}; + for (uint32_t i : arg_vec) { + domain.insert_restriction(trial_space_[i], trial_function_spaces_[i]); + } + + using signature = test(decltype(serac::type(trial_spaces))...); + integrals_.push_back( + MakeInteriorFaceIntegral(domain, integrand, std::vector{args...})); + } + /** * @tparam lambda the type of the integrand functor: must implement operator() with an appropriate function signature * @tparam qpt_data_type The type of the data to store for each quadrature point @@ -285,25 +260,24 @@ public: output_L_ = 0.0; - // this is used to mark when gather operations have been performed, - // to avoid doing them more than once per trial space - bool already_computed[Domain::num_types]{}; // default initializes to `false` - for (auto& integral : integrals_) { - auto type = integral.domain_.type_; + Domain & dom = integral.domain_; - if (!already_computed[type]) { - G_trial_[type][which].Gather(input_L_[which], input_E_[type][which]); - already_computed[type] = true; - } + const serac::BlockElementRestriction & G_trial = dom.get_restriction(trial_function_spaces_[which]); + input_E_buffer_[which].SetSize(int(G_trial.ESize())); + input_E_[which].Update(input_E_buffer_[which], G_trial.bOffsets()); + G_trial.Gather(input_L_[which], input_E_[which]); - integral.GradientMult(input_E_[type][which], output_E_[type], which); + output_E_buffer_.SetSize(dom.total_elements()); + output_E_.Update(output_E_buffer_, dom.bOffsets()); - // scatter-add to compute residuals on the local processor - G_test_.ScatterAdd(output_E_[type], output_L_); + integral.GradientMult(input_E_[which], output_E_, which); + + // scatter-add to compute QoI value for the local processor + G_test_.ScatterAdd(output_E_, output_L_); } - // scatter-add to compute global residuals + // compute global QoI value by summing values from different processors P_test_.MultTranspose(output_L_, output_T_); return output_T_[0]; @@ -331,28 +305,27 @@ public: output_L_ = 0.0; - // this is used to mark when operations have been performed, - // to avoid doing them more than once - bool already_computed[Domain::num_types][num_trial_spaces]{}; // default initializes to `false` - for (auto& integral : integrals_) { - auto type = integral.domain_.type_; + Domain & dom = integral.domain_; for (auto i : integral.active_trial_spaces_) { - if (!already_computed[type][i]) { - G_trial_[type][i].Gather(input_L_[i], input_E_[type][i]); - already_computed[type][i] = true; - } + const serac::BlockElementRestriction & G_trial = dom.get_restriction(trial_function_spaces_[i]); + input_E_buffer_[i].SetSize(int(G_trial.ESize())); + input_E_[i].Update(input_E_buffer_[i], G_trial.bOffsets()); + G_trial.Gather(input_L_[i], input_E_[i]); } - const bool update_state = false; - integral.Mult(t, input_E_[type], output_E_[type], wrt, update_state); + output_E_buffer_.SetSize(dom.total_elements()); + output_E_.Update(output_E_buffer_, dom.bOffsets()); + + const bool update_qdata = false; + integral.Mult(t, input_E_, output_E_, wrt, update_qdata); - // scatter-add to compute residuals on the local processor - G_test_.ScatterAdd(output_E_[type], output_L_); + // scatter-add to compute QoI value for the local processor + G_test_.ScatterAdd(output_E_, output_L_); } - // scatter-add to compute global residuals + // compute global QoI value by summing values from different processors P_test_.MultTranspose(output_L_, output_T_); if constexpr (wrt != NO_DIFFERENTIATION) { @@ -419,6 +392,20 @@ private: double operator()(const mfem::Vector& x) const { return form_.ActionOfGradient(x, which_argument); } + uint64_t max_buffer_size() { + uint64_t max_entries = 0; + for (auto & integral : form_.integrals_) { + Domain & dom = integral.domain_; + const auto& G_trial = dom.get_restriction(form_.trial_function_spaces_[which_argument]); + for (const auto& [geom, test_restriction] : G_trial.restrictions) { + const auto& trial_restriction = G_trial.restrictions.at(geom); + uint64_t entries_per_element = trial_restriction.nodes_per_elem * trial_restriction.components; + max_entries = std::max(max_entries, trial_restriction.num_elements * entries_per_element); + } + } + return max_entries; + } + std::unique_ptr assemble() { // The mfem method ParFiniteElementSpace.NewTrueDofVector should really be marked const @@ -427,48 +414,53 @@ private: gradient_L_ = 0.0; + std::vector K_elem_buffer(max_buffer_size()); + std::map> element_gradients[Domain::num_types]; - for (auto& integral : form_.integrals_) { - auto& K_elem = element_gradients[integral.domain_.type_]; - auto& trial_restrictions = form_.G_trial_[integral.domain_.type_][which_argument].restrictions; +//////////////////////////////////////////////////////////////////////////////// - if (K_elem.empty()) { - for (auto& [geom, trial_restriction] : trial_restrictions) { - K_elem[geom] = ExecArray(trial_restriction.num_elements, 1, - trial_restriction.nodes_per_elem * trial_restriction.components); + for (auto & integral : form_.integrals_) { - detail::zero_out(K_elem[geom]); - } - } + Domain & dom = integral.domain_; - integral.ComputeElementGradients(K_elem, which_argument); - } + // if this integral's derivative isn't identically zero + if (integral.functional_to_integral_index_.count(which_argument) > 0) { + + uint32_t id = integral.functional_to_integral_index_.at(which_argument); + const auto& G_trial = dom.get_restriction(form_.trial_function_spaces_[which_argument]); + for (const auto& [geom, calculate_element_gradients] : integral.element_gradient_[id]) { + + const auto& trial_restriction = G_trial.restrictions.at(geom); - for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements}) { - auto& K_elem = element_gradients[type]; - auto& trial_restrictions = form_.G_trial_[type][which_argument].restrictions; + // prepare a buffer to hold the element matrices + CPUArrayView K_e(K_elem_buffer.data(), + trial_restriction.num_elements, 1, + trial_restriction.nodes_per_elem * trial_restriction.components); + detail::zero_out(K_e); + + calculate_element_gradients(K_e); + + const std::vector & element_ids = integral.domain_.get(geom); - if (!K_elem.empty()) { - for (auto [geom, elem_matrices] : K_elem) { - std::vector trial_vdofs(trial_restrictions[geom].nodes_per_elem * trial_restrictions[geom].components); + uint32_t cols_per_elem = uint32_t(trial_restriction.nodes_per_elem * trial_restriction.components); + std::vector trial_vdofs(cols_per_elem); - for (axom::IndexType e = 0; e < elem_matrices.shape()[0]; e++) { - trial_restrictions[geom].GetElementVDofs(e, trial_vdofs); + for (uint32_t e = 0; e < element_ids.size(); e++) { + trial_restriction.GetElementVDofs(int(e), trial_vdofs); - // note: elem_matrices.shape()[1] is 1 for a QoI - for (axom::IndexType i = 0; i < elem_matrices.shape()[1]; i++) { - for (axom::IndexType j = 0; j < elem_matrices.shape()[2]; j++) { - int sign = trial_vdofs[uint32_t(j)].sign(); - int col = int(trial_vdofs[uint32_t(j)].index()); - gradient_L_[col] += sign * elem_matrices(e, i, j); - } + for (uint32_t i = 0; i < cols_per_elem; i++) { + int col = int(trial_vdofs[i].index()); + gradient_L_[col] += K_e(e, 0, i); } } + } } } +//////////////////////////////////////////////////////////////////////////////// + form_.P_trial_[which_argument]->MultTranspose(gradient_L_, *gradient_T); return gradient_T; @@ -494,6 +486,8 @@ private: /// @brief Manages DOFs for the trial space std::array trial_space_; + std::array< FunctionSpace, num_trial_spaces > trial_function_spaces_; + /** * @brief Operator that converts true (global) DOF values to local (current rank) DOF values * for the test space @@ -503,13 +497,13 @@ private: /// @brief The input set of local DOF values (i.e., on the current rank) mutable mfem::Vector input_L_[num_trial_spaces]; - BlockElementRestriction G_trial_[Domain::num_types][num_trial_spaces]; - - mutable std::vector input_E_[Domain::num_types]; + mutable std::vector input_E_buffer_; + mutable std::vector input_E_; - std::vector integrals_; + mutable std::vector integrals_; - mutable mfem::BlockVector output_E_[Domain::num_types]; + mutable mfem::Vector output_E_buffer_; + mutable mfem::BlockVector output_E_; QoIElementRestriction G_test_; @@ -523,6 +517,9 @@ private: /// @brief The objects representing the gradients w.r.t. each input argument of the Functional mutable std::vector grad_; + + const mfem::MemoryType mem_type; + }; } // namespace serac diff --git a/src/serac/numerics/functional/interior_face_integral_kernels.hpp b/src/serac/numerics/functional/interior_face_integral_kernels.hpp index b84690f2e7..0233082099 100644 --- a/src/serac/numerics/functional/interior_face_integral_kernels.hpp +++ b/src/serac/numerics/functional/interior_face_integral_kernels.hpp @@ -301,6 +301,9 @@ void element_gradient_kernel([[maybe_unused]] ExecArrayView; using trial_element = finite_element; + constexpr bool is_QOI = test::family == Family::QOI; + using padded_derivative_type = std::conditional_t, derivatives_type>; + constexpr int nquad = num_quadrature_points(g, Q); static constexpr TensorProductQuadratureRule rule{}; @@ -309,26 +312,18 @@ void element_gradient_kernel([[maybe_unused]] ExecArrayView(&dK(e, 0, 0)); - auto* vec_ptr = reinterpret_cast< tensor * >(&dK(e, 0, 0)); - - tensor derivatives{}; + tensor derivatives{}; for (int q = 0; q < nquad; q++) { - derivatives(q) = qf_derivatives[e * nquad + uint32_t(q)]; - } - - for (int i = 0; i < 2 * trial_element::ndof * trial_element::components; i++) { - std::cout << vec_ptr[i] << std::endl; + if constexpr (is_QOI) { + get<0>(derivatives(q)) = qf_derivatives[e * nquad + uint32_t(q)]; + } else { + derivatives(q) = qf_derivatives[e * nquad + uint32_t(q)]; + } } - std::cout << std::endl; for (int J = 0; J < 2 * trial_element::ndof; J++) { auto source_and_flux = trial_element::batch_apply_shape_fn_interior_face(J, derivatives, rule); test_element::integrate(source_and_flux, rule, output_ptr + J, 2 * trial_element::ndof); - - for (int i = 0; i < 2 * trial_element::ndof * trial_element::components; i++) { - std::cout << vec_ptr[i] << std::endl; - } - std::cout << std::endl; } } diff --git a/src/serac/numerics/functional/tests/check_gradient.hpp b/src/serac/numerics/functional/tests/check_gradient.hpp index 197d1597d3..ea2052b9aa 100644 --- a/src/serac/numerics/functional/tests/check_gradient.hpp +++ b/src/serac/numerics/functional/tests/check_gradient.hpp @@ -12,6 +12,44 @@ #include "serac/serac_config.hpp" #include "serac/numerics/functional/functional.hpp" +template +void debug_sparse_matrix(serac::Functional& f, double t, const mfem::Vector& U, double epsilon = 1.0e-4) { + + mfem::Vector dU(U.Size()); + dU = 0.0; + + auto [value, dfdU] = f(t, serac::differentiate_wrt(U)); + std::unique_ptr dfdU_matrix = assemble(dfdU); + + std::cout << "{"; + for (int i = 0; i < U.Size(); i++) { + dU[i] = 1; + mfem::Vector df_jvp = dfdU(dU); // matrix-free + + std::cout << "{"; + for (int j = 0; j < df_jvp.Size(); j++) { + std::cout << df_jvp[j]; + if (j != df_jvp.Size() - 1) { + std::cout << ","; + } else { + std::cout << " "; + } + } + std::cout << "}"; + if (i != U.Size() - 1) { + std::cout << ",\n"; + } else { + std::cout << "\n"; + } + + dU[i] = 0; + } + std::cout << "}" << std::endl; + + dfdU_matrix->Print("K.mtx"); + +} + template void check_gradient(serac::Functional& f, double t, const mfem::Vector& U, double epsilon = 1.0e-4) { @@ -70,32 +108,12 @@ void check_gradient(serac::Functional& f, double t, const mfem::Vector& U, do // by about a factor of two for the forward-difference stencil double e1 = df1_fd[0].DistanceTo(df_jvp1.GetData()) / denominator; double e2 = df1_fd[1].DistanceTo(df_jvp1.GetData()) / denominator; - - df1_fd[0].Print(std::cout); - std::cout << std::endl; - df1_fd[1].Print(std::cout); - std::cout << std::endl; - df_jvp1.Print(std::cout); - std::cout << std::endl; - df_jvp2.Print(std::cout); - std::cout << std::endl; - - std::cout << e1 << " " << e2 << std::endl; EXPECT_TRUE(fabs(e1 / e2 - 2.0) < 0.1 || fmin(e1, e2) < 1.0e-9); - df1_cd[0].Print(std::cout); - std::cout << std::endl; - df1_cd[1].Print(std::cout); - std::cout << std::endl; - df_jvp1.Print(std::cout); - - std::cout << "denominator: " << denominator << std::endl; - // halving epsilon should make the error decrease // by about a factor of four for the center-difference stencil double e3 = df1_cd[0].DistanceTo(df_jvp1.GetData()) / denominator; double e4 = df1_cd[1].DistanceTo(df_jvp1.GetData()) / denominator; - std::cout << e3 << " " << e4 << std::endl; EXPECT_TRUE((fabs(e3 / e4 - 4.0) < 0.1) || fmin(e3, e4) < 1.0e-9); } diff --git a/src/serac/numerics/functional/tests/functional_basic_dg.cpp b/src/serac/numerics/functional/tests/functional_basic_dg.cpp index cefe20d531..368695c777 100644 --- a/src/serac/numerics/functional/tests/functional_basic_dg.cpp +++ b/src/serac/numerics/functional/tests/functional_basic_dg.cpp @@ -24,43 +24,7 @@ using namespace serac; using namespace serac::profiling; -template -void debug_sparse_matrix(serac::Functional& f, double t, const mfem::Vector& U, double epsilon = 1.0e-4) { - - mfem::Vector dU(U.Size()); - dU = 0.0; - - auto [value, dfdU] = f(t, serac::differentiate_wrt(U)); - std::unique_ptr dfdU_matrix = assemble(dfdU); - - std::cout << "{"; - for (int i = 0; i < U.Size(); i++) { - dU[i] = 1; - mfem::Vector df_jvp = dfdU(dU); // matrix-free - - std::cout << "{"; - for (int j = 0; j < df_jvp.Size(); j++) { - std::cout << df_jvp[j]; - if (j != df_jvp.Size() - 1) { - std::cout << ","; - } else { - std::cout << " "; - } - } - std::cout << "}"; - if (i != U.Size() - 1) { - std::cout << ",\n"; - } else { - std::cout << "\n"; - } - - dU[i] = 0; - } - std::cout << "}" << std::endl; - - dfdU_matrix->Print("K.mtx"); -} template void L2_test(std::string meshfile) @@ -86,7 +50,6 @@ void L2_test(std::string meshfile) residual.AddInteriorFaceIntegral( Dimension{}, DependsOn<0>{}, [=](double /*t*/, auto X, auto velocity) { -#if 1 // compute the surface normal auto dX_dxi = get(X); auto n = normalize(cross(dX_dxi)); @@ -101,27 +64,76 @@ void L2_test(std::string meshfile) auto f_1 = u_1 * a; auto f_2 = u_2 * a; return serac::tuple{f_1, f_2}; -#else - return velocity; -#endif }, interior_faces); double t = 0.0; check_gradient(residual, t, U); - debug_sparse_matrix(residual, t, U); - } -//TEST(basic, L2_test_edges_linear) { L2_test<2, 1>(SERAC_REPO_DIR "/data/meshes/patch2D.mesh"); } -//TEST(basic, L2_test_edges_quadratic) { L2_test<2, 2>(SERAC_REPO_DIR "/data/meshes/patch2D.mesh"); } -// -//TEST(basic, L2_test_tets_linear) { L2_test<3, 1>(SERAC_REPO_DIR "/data/meshes/patch3D_tets.mesh"); } -//TEST(basic, L2_test_tets_quadratic) { L2_test<3, 2>(SERAC_REPO_DIR "/data/meshes/patch3D_tets.mesh"); } -// +TEST(basic, L2_test_tris_and_quads_linear) { L2_test<2, 1>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); } +TEST(basic, L2_test_tris_and_quads_quadratic) { L2_test<2, 2>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); } + +TEST(basic, L2_test_tets_linear) { L2_test<3, 1>(SERAC_REPO_DIR "/data/meshes/patch3D_tets.mesh"); } +TEST(basic, L2_test_tets_quadratic) { L2_test<3, 2>(SERAC_REPO_DIR "/data/meshes/patch3D_tets.mesh"); } + TEST(basic, L2_test_hexes_linear) { L2_test<3, 1>(SERAC_REPO_DIR "/data/meshes/patch3D_hexes.mesh"); } TEST(basic, L2_test_hexes_quadratic) { L2_test<3, 2>(SERAC_REPO_DIR "/data/meshes/patch3D_hexes.mesh"); } +template +void L2_qoi_test(std::string meshfile) +{ + using trial_space = L2; + + auto mesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 0); + + auto fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); + mfem::ParFiniteElementSpace fespace(mesh.get(), &fec, dim, serac::ordering); + + int seed = 0; + mfem::HypreParVector U = *fespace.NewTrueDofVector(); + U.Randomize(seed); + + // Construct the new functional object using the specified test and trial spaces + Functional qoi({&fespace}); + + constexpr int DERIVATIVE = 1; + + Domain interior_faces = InteriorFaces(*mesh); + + qoi.AddInteriorFaceIntegral( + Dimension{}, DependsOn<0>{}, + [=](double /*t*/, auto X, auto velocity) { + // compute the unit surface normal + auto dX_dxi = get(X); + auto n = normalize(cross(dX_dxi)); + + // extract the velocity values from each side of the interface + // note: the orientation convention is such that the normal + // computed as above will point from from side 1->2 + auto [u_1, u_2] = velocity; + + auto a = dot(u_2 - u_1, n); + + auto f_1 = u_1 * a; + auto f_2 = u_2 * a; + return dot(f_1, f_2); + }, interior_faces); + + double t = 0.0; + check_gradient(qoi, t, U); + +} + +TEST(basic, L2_qoi_test_tri_and_quads_linear) { L2_qoi_test<2, 1>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); } +TEST(basic, L2_qoi_test_tri_and_quads_quadratic) { L2_qoi_test<2, 2>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); } + +TEST(basic, L2_qoi_test_tets_linear) { L2_qoi_test<3, 1>(SERAC_REPO_DIR "/data/meshes/patch3D_tets.mesh"); } +TEST(basic, L2_qoi_test_tets_quadratic) { L2_qoi_test<3, 2>(SERAC_REPO_DIR "/data/meshes/patch3D_tets.mesh"); } + +TEST(basic, L2_qoi_test_hexes_linear) { L2_qoi_test<3, 1>(SERAC_REPO_DIR "/data/meshes/patch3D_hexes.mesh"); } +TEST(basic, L2_qoi_test_hexes_quadratic) { L2_qoi_test<3, 2>(SERAC_REPO_DIR "/data/meshes/patch3D_hexes.mesh"); } + int main(int argc, char* argv[]) { int num_procs, myid; From cf724181730aa7c61fb58eb823e890204a0bf73d Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Mon, 18 Nov 2024 11:14:05 -0800 Subject: [PATCH 27/92] updating some tests to use Domains, (rather than meshes) in calls to Add{...}Integral() --- .../numerics/functional/functional_qoi.inl | 6 ++-- .../functional/shape_aware_functional.hpp | 9 +++--- .../functional/tests/check_gradient.hpp | 2 +- .../functional/tests/functional_qoi.cpp | 24 +++++++++------ src/serac/numerics/tests/equationsolver.cpp | 4 ++- .../parameterized_thermomechanics_example.cpp | 30 ++++++++++++++----- 6 files changed, 48 insertions(+), 27 deletions(-) diff --git a/src/serac/numerics/functional/functional_qoi.inl b/src/serac/numerics/functional/functional_qoi.inl index aeb43f6a8e..c863776be8 100644 --- a/src/serac/numerics/functional/functional_qoi.inl +++ b/src/serac/numerics/functional/functional_qoi.inl @@ -214,7 +214,7 @@ public: * @brief Adds an area integral, i.e., over 2D elements in R^2 */ template - void AddAreaIntegral(DependsOn which_args, const lambda& integrand, mfem::Mesh& domain, + void AddAreaIntegral(DependsOn which_args, const lambda& integrand, Domain & domain, std::shared_ptr>& data = NoQData) { AddDomainIntegral(Dimension<2>{}, which_args, integrand, domain, data); @@ -231,7 +231,7 @@ public: * @brief Adds a volume integral, i.e., over 3D elements in R^3 */ template - void AddVolumeIntegral(DependsOn which_args, const lambda& integrand, mfem::Mesh& domain, + void AddVolumeIntegral(DependsOn which_args, const lambda& integrand, Domain & domain, std::shared_ptr>& data = NoQData) { AddDomainIntegral(Dimension<3>{}, which_args, integrand, domain, data); @@ -239,7 +239,7 @@ public: /// @brief alias for Functional::AddBoundaryIntegral(Dimension<2>{}, integrand, domain); template - void AddSurfaceIntegral(DependsOn which_args, const lambda& integrand, mfem::Mesh& domain) + void AddSurfaceIntegral(DependsOn which_args, const lambda& integrand, Domain & domain) { AddBoundaryIntegral(Dimension<2>{}, which_args, integrand, domain); } diff --git a/src/serac/numerics/functional/shape_aware_functional.hpp b/src/serac/numerics/functional/shape_aware_functional.hpp index e1c91aacf3..3753d5195a 100644 --- a/src/serac/numerics/functional/shape_aware_functional.hpp +++ b/src/serac/numerics/functional/shape_aware_functional.hpp @@ -362,7 +362,6 @@ class ShapeAwareFunctional { * @tparam dim The dimension of the element (2 for quad, 3 for hex, etc) * @tparam args The type of the trial function input arguments * @tparam lambda The type of the integrand functor: must implement operator() with an appropriate function signature - * @tparam domain_type The type of the integration domain (either serac::Domain or mfem::Mesh) * @tparam qpt_data_type The type of the data to store for each quadrature point * * @param[in] integrand The user-provided quadrature function, see @p Integral @@ -372,8 +371,8 @@ class ShapeAwareFunctional { * @note The @p Dimension parameters are used to assist in the deduction of the @a geometry_dim * and @a spatial_dim template parameter */ - template - void AddDomainIntegral(Dimension, DependsOn, const lambda& integrand, domain_type& domain, + template + void AddDomainIntegral(Dimension, DependsOn, const lambda& integrand, Domain & domain, std::shared_ptr> qdata = NoQData) { if constexpr (std::is_same_v) { @@ -423,8 +422,8 @@ class ShapeAwareFunctional { * @note The @p Dimension parameters are used to assist in the deduction of the @a geometry_dim * and @a spatial_dim template parameter */ - template - void AddBoundaryIntegral(Dimension, DependsOn, const lambda& integrand, domain_type& domain) + template + void AddBoundaryIntegral(Dimension, DependsOn, const lambda& integrand, Domain & domain) { functional_->AddBoundaryIntegral( Dimension{}, DependsOn<0, (args + 1)...>{}, diff --git a/src/serac/numerics/functional/tests/check_gradient.hpp b/src/serac/numerics/functional/tests/check_gradient.hpp index ea2052b9aa..88957c0406 100644 --- a/src/serac/numerics/functional/tests/check_gradient.hpp +++ b/src/serac/numerics/functional/tests/check_gradient.hpp @@ -13,7 +13,7 @@ #include "serac/numerics/functional/functional.hpp" template -void debug_sparse_matrix(serac::Functional& f, double t, const mfem::Vector& U, double epsilon = 1.0e-4) { +void debug_sparse_matrix(serac::Functional& f, double t, const mfem::Vector& U, [[maybe_unused]] double epsilon = 1.0e-4) { mfem::Vector dU(U.Size()); dU = 0.0; diff --git a/src/serac/numerics/functional/tests/functional_qoi.cpp b/src/serac/numerics/functional/tests/functional_qoi.cpp index ddace8569b..dffcb6989f 100644 --- a/src/serac/numerics/functional/tests/functional_qoi.cpp +++ b/src/serac/numerics/functional/tests/functional_qoi.cpp @@ -210,10 +210,13 @@ void qoi_test(mfem::ParMesh& mesh, H1

trial, Dimension, WhichTest which) mfem::HypreParVector V = *tmp2; V_gf.GetTrueDofs(V); + Domain domain = EntireDomain(mesh); + Domain boundary = EntireBoundary(mesh); + switch (which) { case WhichTest::Measure: { Functional measure({fespace.get()}); - measure.AddDomainIntegral(Dimension{}, DependsOn<>{}, TrivialIntegrator{}, mesh); + measure.AddDomainIntegral(Dimension{}, DependsOn<>{}, TrivialIntegrator{}, domain); constexpr double expected[] = {1.0, 16.0}; @@ -227,7 +230,7 @@ void qoi_test(mfem::ParMesh& mesh, H1

trial, Dimension, WhichTest which) case WhichTest::Moment: { Functional x_moment({fespace.get()}); - x_moment.AddDomainIntegral(Dimension{}, DependsOn<>{}, ZeroIndexIntegrator{}, mesh); + x_moment.AddDomainIntegral(Dimension{}, DependsOn<>{}, ZeroIndexIntegrator{}, domain); constexpr double expected[] = {0.5, 40.0}; @@ -238,7 +241,7 @@ void qoi_test(mfem::ParMesh& mesh, H1

trial, Dimension, WhichTest which) EXPECT_NEAR(0.0, relative_error, 1.0e-10); Functional x_moment_2({fespace.get()}); - x_moment_2.AddDomainIntegral(Dimension{}, DependsOn<0>{}, GetZeroIntegrator{}, mesh); + x_moment_2.AddDomainIntegral(Dimension{}, DependsOn<0>{}, GetZeroIntegrator{}, domain); relative_error = (x_moment_2(t, V) - expected[dim - 2]) / expected[dim - 2]; EXPECT_NEAR(0.0, relative_error, 1.0e-10); @@ -250,8 +253,8 @@ void qoi_test(mfem::ParMesh& mesh, H1

trial, Dimension, WhichTest which) case WhichTest::SumOfMeasures: { Functional sum_of_measures({fespace.get()}); - sum_of_measures.AddDomainIntegral(Dimension{}, DependsOn<>{}, TrivialIntegrator{}, mesh); - sum_of_measures.AddBoundaryIntegral(Dimension{}, DependsOn<>{}, TrivialIntegrator{}, mesh); + sum_of_measures.AddDomainIntegral(Dimension{}, DependsOn<>{}, TrivialIntegrator{}, domain); + sum_of_measures.AddBoundaryIntegral(Dimension{}, DependsOn<>{}, TrivialIntegrator{}, boundary); constexpr double expected[] = {5.0, 64.0}; @@ -265,8 +268,8 @@ void qoi_test(mfem::ParMesh& mesh, H1

trial, Dimension, WhichTest which) case WhichTest::Nonlinear: { Functional f({fespace.get()}); - f.AddDomainIntegral(Dimension{}, DependsOn<0>{}, SineIntegrator{}, mesh); - f.AddBoundaryIntegral(Dimension{}, DependsOn<0>{}, CosineIntegrator{}, mesh); + f.AddDomainIntegral(Dimension{}, DependsOn<0>{}, SineIntegrator{}, domain); + f.AddBoundaryIntegral(Dimension{}, DependsOn<0>{}, CosineIntegrator{}, boundary); constexpr double expected[] = {4.6640262484879, 192400.1149761554}; @@ -314,9 +317,12 @@ void qoi_test(mfem::ParMesh& mesh, H1 trial1, H1 trial2, Dimension) mfem::HypreParVector U2 = *tmp; U2_gf.GetTrueDofs(U2); + Domain domain = EntireDomain(mesh); + Domain boundary = EntireBoundary(mesh); + Functional f({fespace1.get(), fespace2.get()}); - f.AddDomainIntegral(Dimension{}, DependsOn<0, 1>{}, FourArgSineIntegrator{}, mesh); - f.AddBoundaryIntegral(Dimension{}, DependsOn<0, 1>{}, FourArgCosineIntegrator{}, mesh); + f.AddDomainIntegral(Dimension{}, DependsOn<0, 1>{}, FourArgSineIntegrator{}, domain); + f.AddBoundaryIntegral(Dimension{}, DependsOn<0, 1>{}, FourArgCosineIntegrator{}, boundary); // note: these answers are generated by a Mathematica script that // integrates the qoi for these domains to machine precision diff --git a/src/serac/numerics/tests/equationsolver.cpp b/src/serac/numerics/tests/equationsolver.cpp index d48ecf68fd..ddbe3c3939 100644 --- a/src/serac/numerics/tests/equationsolver.cpp +++ b/src/serac/numerics/tests/equationsolver.cpp @@ -58,6 +58,8 @@ TEST_P(EquationSolverSuite, All) x_exact.Randomize(0); + Domain domain = EntireDomain(pmesh); + residual.AddDomainIntegral( Dimension{}, DependsOn<0>{}, [&](double /*t*/, auto, auto scalar) { @@ -66,7 +68,7 @@ TEST_P(EquationSolverSuite, All) auto flux = du_dx; return serac::tuple{source, flux}; }, - pmesh); + domain); StdFunctionOperator residual_opr( fes->TrueVSize(), diff --git a/src/serac/physics/tests/parameterized_thermomechanics_example.cpp b/src/serac/physics/tests/parameterized_thermomechanics_example.cpp index 6886d7ddde..3a12149f4a 100644 --- a/src/serac/physics/tests/parameterized_thermomechanics_example.cpp +++ b/src/serac/physics/tests/parameterized_thermomechanics_example.cpp @@ -20,6 +20,16 @@ using namespace serac; +template +tensor average(std::vector >& positions) +{ + tensor total{}; + for (auto x : positions) { + total += x; + } + return total / double(positions.size()); +} + template auto greenStrain(const tensor& grad_u) { @@ -150,8 +160,13 @@ TEST(Thermomechanics, ParameterizedMaterial) simulation.outputStateToDisk("paraview"); - // define quantities of interest + // create Domains to integrate over + Domain top_surface = Domain::ofBoundaryElements(pmesh, [=](std::vector vertices, int /* attr */) { + // select the faces whose "average" z coordinate is close to the top of the mesh + return average(vertices)[2] > 0.99 * height; + }); + // define quantities of interest Functional)> qoi({&simulation.displacement().space()}); qoi.AddSurfaceIntegral( DependsOn<0>{}, @@ -159,9 +174,10 @@ TEST(Thermomechanics, ParameterizedMaterial) auto [X, dX_dxi] = position; auto [u, du_dxi] = displacement; auto n = normalize(cross(dX_dxi)); - return dot(u, n) * ((X[2] > 0.99 * height) ? 1.0 : 0.0); + return dot(u, n); }, - pmesh); + top_surface + ); double initial_qoi = qoi(time, simulation.displacement()); SLIC_INFO_ROOT(axom::fmt::format("vertical displacement integrated over the top surface: {}", initial_qoi)); @@ -170,11 +186,9 @@ TEST(Thermomechanics, ParameterizedMaterial) Functional)> area({&simulation.displacement().space()}); area.AddSurfaceIntegral( DependsOn<>{}, - [=](double /*t*/, auto position) { - auto [X, dX_dxi] = position; - return (X[2] > 0.99 * height) ? 1.0 : 0.0; - }, - pmesh); + [=](double /*t*/, auto /*position*/) { return 1.0; }, + top_surface + ); double top_area = area(time, simulation.displacement()); From c5032a55fd177190ad2765c6e2c7da3b5c4b4bfa Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Mon, 18 Nov 2024 11:55:20 -0800 Subject: [PATCH 28/92] fix #include order issues, updating more tests --- src/serac/infrastructure/debug_print.hpp | 6 ------ src/serac/numerics/functional/domain.hpp | 17 +++++++++++++++++ src/serac/numerics/functional/functional.hpp | 2 +- src/serac/numerics/functional/geometry.hpp | 19 ------------------- .../functional/tests/check_gradient.hpp | 2 -- .../functional/tests/functional_basic_dg.cpp | 2 -- src/serac/physics/solid_mechanics.hpp | 19 ++----------------- .../physics/tests/solid_dynamics_patch.cpp | 5 +++-- .../physics/tests/solid_statics_patch.cpp | 6 ++++-- 9 files changed, 27 insertions(+), 51 deletions(-) diff --git a/src/serac/infrastructure/debug_print.hpp b/src/serac/infrastructure/debug_print.hpp index 485f058afa..7bfc1f983f 100644 --- a/src/serac/infrastructure/debug_print.hpp +++ b/src/serac/infrastructure/debug_print.hpp @@ -61,12 +61,6 @@ std::ostream& operator<<(std::ostream& out, DoF dof) return out; } -std::ostream& operator<<(std::ostream& out, serac::SignedIndex i) -{ - out << "{" << i.index_ << ", " << i.sign_ << "}"; - return out; -} - /** * @brief write a 2D array of values out to file, in a space-separated format * @tparam T the type of each value in the array diff --git a/src/serac/numerics/functional/domain.hpp b/src/serac/numerics/functional/domain.hpp index c0ae911753..d9e9a48534 100644 --- a/src/serac/numerics/functional/domain.hpp +++ b/src/serac/numerics/functional/domain.hpp @@ -208,4 +208,21 @@ inline auto by_attr(int value) return [value](std::vector >, int attr) { return attr == value; }; } +/** + * @brief count the number of elements of each geometry in a domain + * @param domain the domain to count + */ +inline std::array geometry_counts(const Domain& domain) +{ + std::array counts{}; + + constexpr std::array geometries = {mfem::Geometry::SEGMENT, mfem::Geometry::TRIANGLE, + mfem::Geometry::SQUARE, mfem::Geometry::TETRAHEDRON, + mfem::Geometry::CUBE}; + for (auto geom : geometries) { + counts[uint32_t(geom)] = uint32_t(domain.get(geom).size()); + } + return counts; +} + } // namespace serac diff --git a/src/serac/numerics/functional/functional.hpp b/src/serac/numerics/functional/functional.hpp index 7db463009e..b2d720264d 100644 --- a/src/serac/numerics/functional/functional.hpp +++ b/src/serac/numerics/functional/functional.hpp @@ -273,7 +273,7 @@ class Functional { * @param[inout] qdata The data for each quadrature point */ template - void AddDomainIntegral(Dimension, DependsOn, const lambda& integrand, const Domain& domain, + void AddDomainIntegral(Dimension, DependsOn, const lambda& integrand, Domain& domain, std::shared_ptr> qdata = NoQData) { if (domain.mesh_.GetNE() == 0) return; diff --git a/src/serac/numerics/functional/geometry.hpp b/src/serac/numerics/functional/geometry.hpp index eb4da0b2a3..d56312c22c 100644 --- a/src/serac/numerics/functional/geometry.hpp +++ b/src/serac/numerics/functional/geometry.hpp @@ -2,8 +2,6 @@ #include "mfem.hpp" -#include "serac/numerics/functional/domain.hpp" - namespace serac { /** @@ -78,23 +76,6 @@ inline std::array geometry_counts(cons return counts; } -/** - * @brief count the number of elements of each geometry in a domain - * @param domain the domain to count - */ -inline std::array geometry_counts(const Domain& domain) -{ - std::array counts{}; - - constexpr std::array geometries = {mfem::Geometry::SEGMENT, mfem::Geometry::TRIANGLE, - mfem::Geometry::SQUARE, mfem::Geometry::TETRAHEDRON, - mfem::Geometry::CUBE}; - for (auto geom : geometries) { - counts[uint32_t(geom)] = uint32_t(domain.get(geom).size()); - } - return counts; -} - /** * @brief count the number of boundary elements of each geometry in a mesh * @param mesh the mesh to count diff --git a/src/serac/numerics/functional/tests/check_gradient.hpp b/src/serac/numerics/functional/tests/check_gradient.hpp index 5cd64c2147..ba720729fd 100644 --- a/src/serac/numerics/functional/tests/check_gradient.hpp +++ b/src/serac/numerics/functional/tests/check_gradient.hpp @@ -51,8 +51,6 @@ void debug_sparse_matrix(serac::Functional& f, double t, const mfem::Vector& } -template -void check_gradient(serac::Functional& f, double t, const mfem::Vector& U, double epsilon = 1.0e-4) template void check_gradient(serac::Functional& f, double t, mfem::Vector& U, double epsilon = 1.0e-4) { diff --git a/src/serac/numerics/functional/tests/functional_basic_dg.cpp b/src/serac/numerics/functional/tests/functional_basic_dg.cpp index 368695c777..76942dad99 100644 --- a/src/serac/numerics/functional/tests/functional_basic_dg.cpp +++ b/src/serac/numerics/functional/tests/functional_basic_dg.cpp @@ -24,8 +24,6 @@ using namespace serac; using namespace serac::profiling; - - template void L2_test(std::string meshfile) { diff --git a/src/serac/physics/solid_mechanics.hpp b/src/serac/physics/solid_mechanics.hpp index eec39eb6f6..07a142ff79 100644 --- a/src/serac/physics/solid_mechanics.hpp +++ b/src/serac/physics/solid_mechanics.hpp @@ -910,7 +910,7 @@ class SolidMechanics, std::integer_se * @note This method must be called prior to completeSetup() */ template - void setMaterial(DependsOn, const MaterialType& material, const Domain& domain, + void setMaterial(DependsOn, const MaterialType& material, Domain& domain, qdata_type qdata = EmptyQData) { static_assert(std::is_same_v || std::is_same_v, @@ -926,29 +926,14 @@ class SolidMechanics, std::integer_se std::move(material_functor), domain, qdata); } - /// @overload - template - void setMaterial(DependsOn, const MaterialType& material, - qdata_type qdata = EmptyQData) - { - setMaterial(DependsOn{}, material, EntireDomain(mesh_), qdata); - } - /// @overload template - void setMaterial(const MaterialType& material, const Domain& domain, + void setMaterial(const MaterialType& material, Domain& domain, std::shared_ptr> qdata = EmptyQData) { setMaterial(DependsOn<>{}, material, domain, qdata); } - /// @overload - template - void setMaterial(const MaterialType& material, std::shared_ptr> qdata = EmptyQData) - { - setMaterial(DependsOn<>{}, material, EntireDomain(mesh_), qdata); - } - /** * @brief Set the underlying finite element state to a prescribed displacement * diff --git a/src/serac/physics/tests/solid_dynamics_patch.cpp b/src/serac/physics/tests/solid_dynamics_patch.cpp index 30ea7b354d..a460394893 100644 --- a/src/serac/physics/tests/solid_dynamics_patch.cpp +++ b/src/serac/physics/tests/solid_dynamics_patch.cpp @@ -300,7 +300,7 @@ double solution_error(solution_type exact_solution, PatchBoundaryCondition bc) std::string mesh_tag{"mesh"}; - serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // Construct a functional-based solid mechanics solver serac::NonlinearSolverOptions nonlin_opts{.relative_tol = 1.0e-13, .absolute_tol = 1.0e-13}; @@ -310,7 +310,8 @@ double solution_error(solution_type exact_solution, PatchBoundaryCondition bc) "solid_dynamics", mesh_tag); solid_mechanics::NeoHookean mat{.density = 1.0, .K = 1.0, .G = 1.0}; - solid.setMaterial(mat); + Domain material_block = EntireDomain(pmesh); + solid.setMaterial(mat, material_block); // initial conditions solid.setVelocity([exact_solution](const mfem::Vector& x, mfem::Vector& v) { exact_solution.velocity(x, 0.0, v); }); diff --git a/src/serac/physics/tests/solid_statics_patch.cpp b/src/serac/physics/tests/solid_statics_patch.cpp index 240291891b..f98f2266c4 100644 --- a/src/serac/physics/tests/solid_statics_patch.cpp +++ b/src/serac/physics/tests/solid_statics_patch.cpp @@ -324,7 +324,8 @@ double solution_error(PatchBoundaryCondition bc) SolidMechanics solid(std::move(equation_solver), solid_mechanics::default_quasistatic_options, "solid", mesh_tag); solid_mechanics::NeoHookean mat{.density=1.0, .K=1.0, .G=1.0}; - solid.setMaterial(mat); + Domain material_block = EntireDomain(pmesh); + solid.setMaterial(mat, material_block); exact_displacement.applyLoads(mat, solid, essentialBoundaryAttributes(bc)); @@ -399,7 +400,8 @@ double pressure_error() SolidMechanics solid(std::move(equation_solver), solid_mechanics::default_quasistatic_options, "solid", mesh_tag); solid_mechanics::NeoHookean mat{.density=1.0, .K=1.0, .G=1.0}; - solid.setMaterial(mat); + Domain material_block = EntireDomain(pmesh); + solid.setMaterial(mat, material_block); typename solid_mechanics::NeoHookean::State state; auto H = make_tensor([](int i, int j) { From 726804d2c4971aa23bfa059fc61a935b646e0c11 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Mon, 18 Nov 2024 13:57:26 -0800 Subject: [PATCH 29/92] updating more tests --- src/serac/numerics/functional/domain.hpp | 10 +++++ .../functional/tests/domain_tests.cpp | 9 ----- .../tests/functional_shape_derivatives.cpp | 12 ++++-- .../tests/geometric_factors_tests.cpp | 10 ----- src/serac/physics/solid_mechanics.hpp | 15 +++---- src/serac/physics/tests/solid_finite_diff.cpp | 40 ++++++++----------- src/serac/physics/tests/solid_periodic.cpp | 14 +++---- src/serac/physics/tests/solid_shape.cpp | 10 +++-- 8 files changed, 52 insertions(+), 68 deletions(-) diff --git a/src/serac/numerics/functional/domain.hpp b/src/serac/numerics/functional/domain.hpp index d9e9a48534..c2e590d5d3 100644 --- a/src/serac/numerics/functional/domain.hpp +++ b/src/serac/numerics/functional/domain.hpp @@ -225,4 +225,14 @@ inline std::array geometry_counts(cons return counts; } +template +inline tensor average(std::vector >& positions) +{ + tensor total{}; + for (auto x : positions) { + total += x; + } + return total / double(positions.size()); +} + } // namespace serac diff --git a/src/serac/numerics/functional/tests/domain_tests.cpp b/src/serac/numerics/functional/tests/domain_tests.cpp index dfd1b05720..c841f2258f 100644 --- a/src/serac/numerics/functional/tests/domain_tests.cpp +++ b/src/serac/numerics/functional/tests/domain_tests.cpp @@ -21,15 +21,6 @@ mfem::Mesh import_mesh(std::string meshfile) return mesh; } -template -tensor average(std::vector >& positions) -{ - tensor total{}; - for (auto x : positions) { - total += x; - } - return total / double(positions.size()); -} TEST(domain, of_vertices) { diff --git a/src/serac/numerics/functional/tests/functional_shape_derivatives.cpp b/src/serac/numerics/functional/tests/functional_shape_derivatives.cpp index b79841bc91..28b6480868 100644 --- a/src/serac/numerics/functional/tests/functional_shape_derivatives.cpp +++ b/src/serac/numerics/functional/tests/functional_shape_derivatives.cpp @@ -290,9 +290,11 @@ void functional_test_2D(mfem::ParMesh& mesh, double tolerance) // Construct the new functional object using the known test and trial spaces ShapeAwareFunctional residual(fespace2.get(), fespace1.get(), {fespace1.get()}); - residual.AddDomainIntegral(Dimension{}, DependsOn<>{}, TestFunctorOne{}, mesh); + Domain whole_domain = EntireDomain(mesh); + residual.AddDomainIntegral(Dimension{}, DependsOn<>{}, TestFunctorOne{}, whole_domain); - residual.AddBoundaryIntegral(Dimension{}, DependsOn<>{}, TestFunctorTwo{}, mesh); + Domain whole_boundary = EntireBoundary(mesh); + residual.AddBoundaryIntegral(Dimension{}, DependsOn<>{}, TestFunctorTwo{}, whole_boundary); double t = 0.0; auto [r, drdU2] = residual(t, serac::differentiate_wrt(U2), U1); @@ -333,9 +335,11 @@ void functional_test_3D(mfem::ParMesh& mesh, double tolerance) // Construct the new functional object using the known test and trial spaces ShapeAwareFunctional residual(fespace2.get(), fespace1.get(), {fespace1.get()}); - residual.AddDomainIntegral(Dimension{}, DependsOn<>{}, TestFunctorOne{}, mesh); + Domain whole_domain = EntireDomain(mesh); + residual.AddDomainIntegral(Dimension{}, DependsOn<>{}, TestFunctorOne{}, whole_domain); - residual.AddBoundaryIntegral(Dimension{}, DependsOn<>{}, TestFunctorTwo{}, mesh); + Domain whole_boundary = EntireBoundary(mesh); + residual.AddBoundaryIntegral(Dimension{}, DependsOn<>{}, TestFunctorTwo{}, whole_boundary); double t = 0.0; auto [r, drdU2] = residual(t, serac::differentiate_wrt(U2), U1); diff --git a/src/serac/numerics/functional/tests/geometric_factors_tests.cpp b/src/serac/numerics/functional/tests/geometric_factors_tests.cpp index 9df09ed93f..02e76b382f 100644 --- a/src/serac/numerics/functional/tests/geometric_factors_tests.cpp +++ b/src/serac/numerics/functional/tests/geometric_factors_tests.cpp @@ -21,16 +21,6 @@ mfem::Mesh import_mesh(std::string meshfile) return mesh; } -template -tensor average(std::vector >& positions) -{ - tensor total{}; - for (auto x : positions) { - total += x; - } - return total / double(positions.size()); -} - TEST(geometric_factors, with_2D_domains) { auto mesh = import_mesh("patch2D_tris_and_quads.mesh"); diff --git a/src/serac/physics/solid_mechanics.hpp b/src/serac/physics/solid_mechanics.hpp index 07a142ff79..e081d02152 100644 --- a/src/serac/physics/solid_mechanics.hpp +++ b/src/serac/physics/solid_mechanics.hpp @@ -1017,18 +1017,17 @@ class SolidMechanics, std::integer_se */ template void addBodyForce(DependsOn, BodyForceType body_force, - const std::optional& optional_domain = std::nullopt) + Domain& domain) { - Domain domain = (optional_domain) ? *optional_domain : EntireDomain(mesh_); residual_->AddDomainIntegral(Dimension{}, DependsOn<0, 1, active_parameters + NUM_STATE_VARS...>{}, BodyForceIntegrand(body_force), domain); } /// @overload template - void addBodyForce(BodyForceType body_force, const std::optional& optional_domain = std::nullopt) + void addBodyForce(BodyForceType body_force, Domain & domain) { - addBodyForce(DependsOn<>{}, body_force, optional_domain); + addBodyForce(DependsOn<>{}, body_force, domain); } /** @@ -1056,10 +1055,8 @@ class SolidMechanics, std::integer_se */ template void setTraction(DependsOn, TractionType traction_function, - const std::optional& optional_domain = std::nullopt) + Domain & domain) { - Domain domain = (optional_domain) ? *optional_domain : EntireBoundary(mesh_); - residual_->AddBoundaryIntegral( Dimension{}, DependsOn<0, 1, active_parameters + NUM_STATE_VARS...>{}, [traction_function](double t, auto X, auto /* displacement */, auto /* acceleration */, auto... params) { @@ -1072,9 +1069,9 @@ class SolidMechanics, std::integer_se /// @overload template - void setTraction(TractionType traction_function, const std::optional& optional_domain = std::nullopt) + void setTraction(TractionType traction_function, Domain & domain) { - setTraction(DependsOn<>{}, traction_function, optional_domain); + setTraction(DependsOn<>{}, traction_function, domain); } /** diff --git a/src/serac/physics/tests/solid_finite_diff.cpp b/src/serac/physics/tests/solid_finite_diff.cpp index b700fec3cb..386bc23ffe 100644 --- a/src/serac/physics/tests/solid_finite_diff.cpp +++ b/src/serac/physics/tests/solid_finite_diff.cpp @@ -73,7 +73,8 @@ TEST(SolidMechanics, FiniteDifferenceParameter) constexpr int bulk_parameter_index = 0; solid_mechanics::ParameterizedNeoHookeanSolid mat{1.0, 0.0, 0.0}; - solid_solver.setMaterial(DependsOn<0, 1>{}, mat); + Domain whole_mesh = EntireDomain(pmesh); + solid_solver.setMaterial(DependsOn<0, 1>{}, mat, whole_mesh); // Define the function for the initial displacement and boundary condition auto bc = [](const mfem::Vector&, mfem::Vector& bc_vec) -> void { bc_vec = 0.0; }; @@ -93,7 +94,7 @@ TEST(SolidMechanics, FiniteDifferenceParameter) } solid_mechanics::ConstantBodyForce force{constant_force}; - solid_solver.addBodyForce(force, EntireDomain(pmesh)); + solid_solver.addBodyForce(force, whole_mesh); // Finalize the data structures solid_solver.completeSetup(); @@ -221,7 +222,8 @@ void finite_difference_shape_test(LoadingType load) solid_mechanics::default_quasistatic_options, "solid_functional", mesh_tag); solid_mechanics::NeoHookean mat{1.0, 1.0, 1.0}; - solid_solver.setMaterial(mat); + Domain whole_mesh = EntireDomain(pmesh); + solid_solver.setMaterial(mat, whole_mesh); FiniteElementState shape_displacement(pmesh, H1{}); @@ -235,38 +237,30 @@ void finite_difference_shape_test(LoadingType load) solid_solver.setDisplacementBCs(ess_bdr, bc); solid_solver.setDisplacement(bc); - if (load == LoadingType::BodyForce) { - tensor constant_force; + Domain top_face = Domain::ofBoundaryElements(pmesh, [](std::vector< vec2 > vertices, int attr){ + return average(vertices)[1] > 0.99; // select faces whose y-coordinates are at the top of the mesh + }); - constant_force[0] = 0.0; + if (load == LoadingType::BodyForce) { + tensor constant_force{}; constant_force[1] = 1.0e-1; - if (dim == 3) { - constant_force[2] = 0.0; - } - solid_mechanics::ConstantBodyForce force{constant_force}; - solid_solver.addBodyForce(force, EntireDomain(pmesh)); + solid_solver.addBodyForce(force, whole_mesh); + + } else if (load == LoadingType::Pressure) { solid_solver.setPressure( [](auto& X, double) { - if (X[1] > 0.99) { - return 0.1; - } - return 0.0; + return 0.1; }, - EntireBoundary(pmesh)); + top_face); } else if (load == LoadingType::Traction) { solid_solver.setTraction( [](auto& X, auto, double) { - auto traction = 0.0 * X; - if (X[1] > 0.99) { - traction[0] = 1.0e-2; - traction[1] = 1.0e-2; - } - return traction; + return vec2{0.01, 0.01}; }, - EntireBoundary(pmesh)); + top_face); } // Finalize the data structures diff --git a/src/serac/physics/tests/solid_periodic.cpp b/src/serac/physics/tests/solid_periodic.cpp index 27e6984e81..fd5ba5e59e 100644 --- a/src/serac/physics/tests/solid_periodic.cpp +++ b/src/serac/physics/tests/solid_periodic.cpp @@ -75,8 +75,10 @@ void periodic_test(mfem::Element::Type element_type) solid_solver.setParameter(0, user_defined_bulk_modulus); solid_solver.setParameter(1, user_defined_shear_modulus); + Domain whole_mesh = EntireDomain(pmesh); + solid_mechanics::ParameterizedNeoHookeanSolid mat{1.0, 0.0, 0.0}; - solid_solver.setMaterial(DependsOn<0, 1>{}, mat); + solid_solver.setMaterial(DependsOn<0, 1>{}, mat, whole_mesh); // Boundary conditions: // Prescribe zero displacement at the supported end of the beam @@ -88,17 +90,11 @@ void periodic_test(mfem::Element::Type element_type) auto ini_displacement = [iniDispVal](const mfem::Vector&, mfem::Vector& u) -> void { u = iniDispVal; }; solid_solver.setDisplacement(ini_displacement); - tensor constant_force; - - constant_force[0] = 0.0; + tensor constant_force{}; constant_force[1] = 1.0e-2; - if (dim == 3) { - constant_force[2] = 0.0; - } - solid_mechanics::ConstantBodyForce force{constant_force}; - solid_solver.addBodyForce(force, EntireDomain(pmesh)); + solid_solver.addBodyForce(force, whole_mesh); // Finalize the data structures solid_solver.completeSetup(); diff --git a/src/serac/physics/tests/solid_shape.cpp b/src/serac/physics/tests/solid_shape.cpp index 1b90c9aa1f..49608d6db7 100644 --- a/src/serac/physics/tests/solid_shape.cpp +++ b/src/serac/physics/tests/solid_shape.cpp @@ -120,9 +120,10 @@ void shape_test() solid_solver.setShapeDisplacement(user_defined_shape_displacement); - solid_solver.setMaterial(mat); + Domain whole_mesh = EntireDomain(StateManager::mesh(mesh_tag)); - solid_solver.addBodyForce(force, EntireDomain(StateManager::mesh(mesh_tag))); + solid_solver.setMaterial(mat, whole_mesh); + solid_solver.addBodyForce(force, whole_mesh); // Finalize the data structures solid_solver.completeSetup(); @@ -167,9 +168,10 @@ void shape_test() solid_solver_no_shape.setDisplacementBCs(ess_bdr, bc_pure); solid_solver_no_shape.setDisplacement(bc_pure); - solid_solver_no_shape.setMaterial(mat); + Domain whole_mesh = EntireDomain(StateManager::mesh(new_mesh_tag)); - solid_solver_no_shape.addBodyForce(force, EntireDomain(StateManager::mesh(new_mesh_tag))); + solid_solver_no_shape.setMaterial(mat, whole_mesh); + solid_solver_no_shape.addBodyForce(force, whole_mesh); // Finalize the data structures solid_solver_no_shape.completeSetup(); From 686ea101816b7f0a1a319cb7f4acc0654cbe8185 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Mon, 18 Nov 2024 16:20:52 -0800 Subject: [PATCH 30/92] updating more tests --- .../tests/functional_with_domain.cpp | 10 ------- src/serac/physics/tests/contact_beam.cpp | 5 ++-- src/serac/physics/tests/contact_patch.cpp | 5 ++-- .../physics/tests/contact_patch_tied.cpp | 5 ++-- .../finite_element_vector_set_over_domain.cpp | 10 ------- .../physics/tests/lce_Brighenti_tensile.cpp | 30 +++++++++++-------- 6 files changed, 27 insertions(+), 38 deletions(-) diff --git a/src/serac/numerics/functional/tests/functional_with_domain.cpp b/src/serac/numerics/functional/tests/functional_with_domain.cpp index 1707995df2..8e77b07b83 100644 --- a/src/serac/numerics/functional/tests/functional_with_domain.cpp +++ b/src/serac/numerics/functional/tests/functional_with_domain.cpp @@ -73,16 +73,6 @@ struct TrivialIntegrator { } }; -template -tensor average(std::vector>& positions) -{ - tensor total{}; - for (auto x : positions) { - total += x; - } - return total / double(positions.size()); -} - template void whole_mesh_comparison_test_impl(std::unique_ptr& mesh) { diff --git a/src/serac/physics/tests/contact_beam.cpp b/src/serac/physics/tests/contact_beam.cpp index 5e70158590..de0143735b 100644 --- a/src/serac/physics/tests/contact_beam.cpp +++ b/src/serac/physics/tests/contact_beam.cpp @@ -40,7 +40,7 @@ TEST_P(ContactTest, beam) std::string filename = SERAC_REPO_DIR "/data/meshes/beam-hex-with-contact-block.mesh"; auto mesh = mesh::refineAndDistribute(buildMeshFromFile(filename), 1, 0); - StateManager::setMesh(std::move(mesh), "beam_mesh"); + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), "beam_mesh"); LinearSolverOptions linear_options{.linear_solver = LinearSolver::Strumpack, .print_level = 1}; #ifndef MFEM_USE_STRUMPACK @@ -71,7 +71,8 @@ TEST_P(ContactTest, beam) double K = 10.0; double G = 0.25; solid_mechanics::NeoHookean mat{1.0, K, G}; - solid_solver.setMaterial(mat); + Domain material_block = EntireDomain(pmesh); + solid_solver.setMaterial(mat, material_block); // Pass the BC information to the solver object solid_solver.setDisplacementBCs({1}, [](const mfem::Vector&, mfem::Vector& u) { diff --git a/src/serac/physics/tests/contact_patch.cpp b/src/serac/physics/tests/contact_patch.cpp index 7417c3f866..a5f7dd4020 100644 --- a/src/serac/physics/tests/contact_patch.cpp +++ b/src/serac/physics/tests/contact_patch.cpp @@ -42,7 +42,7 @@ TEST_P(ContactTest, patch) std::string filename = SERAC_REPO_DIR "/data/meshes/twohex_for_contact.mesh"; auto mesh = mesh::refineAndDistribute(buildMeshFromFile(filename), 2, 0); - StateManager::setMesh(std::move(mesh), "patch_mesh"); + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), "patch_mesh"); #ifdef SERAC_USE_PETSC LinearSolverOptions linear_options{ @@ -78,7 +78,8 @@ TEST_P(ContactTest, patch) double K = 10.0; double G = 0.25; solid_mechanics::NeoHookean mat{1.0, K, G}; - solid_solver.setMaterial(mat); + Domain material_block = EntireDomain(pmesh); + solid_solver.setMaterial(mat, material_block); // Define the function for the initial displacement and boundary condition auto zero_disp_bc = [](const mfem::Vector&) { return 0.0; }; diff --git a/src/serac/physics/tests/contact_patch_tied.cpp b/src/serac/physics/tests/contact_patch_tied.cpp index 600cbf4390..65dee67d1b 100644 --- a/src/serac/physics/tests/contact_patch_tied.cpp +++ b/src/serac/physics/tests/contact_patch_tied.cpp @@ -42,7 +42,7 @@ TEST_P(ContactPatchTied, patch) std::string filename = SERAC_REPO_DIR "/data/meshes/twohex_for_contact.mesh"; auto mesh = mesh::refineAndDistribute(buildMeshFromFile(filename), 3, 0); - StateManager::setMesh(std::move(mesh), "patch_mesh"); + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), "patch_mesh"); // TODO: investigate performance with Petsc // #ifdef SERAC_USE_PETSC @@ -79,7 +79,8 @@ TEST_P(ContactPatchTied, patch) double K = 10.0; double G = 0.25; solid_mechanics::NeoHookean mat{1.0, K, G}; - solid_solver.setMaterial(mat); + Domain material_block = EntireDomain(pmesh); + solid_solver.setMaterial(mat, material_block); // Define the function for the initial displacement and boundary condition auto zero_disp_bc = [](const mfem::Vector&) { return 0.0; }; diff --git a/src/serac/physics/tests/finite_element_vector_set_over_domain.cpp b/src/serac/physics/tests/finite_element_vector_set_over_domain.cpp index 795219e9aa..ca860f0005 100644 --- a/src/serac/physics/tests/finite_element_vector_set_over_domain.cpp +++ b/src/serac/physics/tests/finite_element_vector_set_over_domain.cpp @@ -18,16 +18,6 @@ namespace serac { -template -tensor average(std::vector >& positions) -{ - tensor total{}; - for (auto x : positions) { - total += x; - } - return total / double(positions.size()); -} - TEST(FiniteElementVector, SetScalarFieldOver2DDomain) { MPI_Barrier(MPI_COMM_WORLD); diff --git a/src/serac/physics/tests/lce_Brighenti_tensile.cpp b/src/serac/physics/tests/lce_Brighenti_tensile.cpp index 1e32beb52f..7aebf74ecb 100644 --- a/src/serac/physics/tests/lce_Brighenti_tensile.cpp +++ b/src/serac/physics/tests/lce_Brighenti_tensile.cpp @@ -118,7 +118,8 @@ TEST(LiquidCrystalElastomer, Brighenti) LiquidCrystElastomerBrighenti::State initial_state{}; auto qdata = solid_solver.createQuadratureDataBuffer(initial_state); - solid_solver.setMaterial(DependsOn{}, mat, qdata); + Domain whole_mesh = EntireDomain(pmesh); + solid_solver.setMaterial(DependsOn{}, mat, whole_mesh, qdata); // prescribe symmetry conditions auto zeroFunc = [](const mfem::Vector /*x*/) { return 0.0; }; @@ -132,11 +133,17 @@ TEST(LiquidCrystalElastomer, Brighenti) double iniLoadVal = 1.0e0; double maxLoadVal = 4 * 1.3e0 / lx / lz; double loadVal = iniLoadVal + 0.0 * maxLoadVal; + + Domain front_face = Domain::ofBoundaryElements(pmesh, [ly](std::vector< vec3 > vertices, int /* attr */){ + return average(vertices)[1] > 0.99 * ly; + }); + solid_solver.setTraction( - [&loadVal, ly](auto x, auto /*n*/, auto /*t*/) { - return tensor{0, loadVal * (x[1] > 0.99 * ly), 0}; - }, - EntireBoundary(pmesh)); + [&loadVal](auto /*x*/, auto /*n*/, auto /*t*/) { + return tensor{0, loadVal, 0}; + }, + front_face + ); solid_solver.setDisplacement(ini_displacement); @@ -155,18 +162,17 @@ TEST(LiquidCrystalElastomer, Brighenti) auto [X, dX_dxi] = position; auto [u, du_dxi] = displacement; auto n = normalize(cross(dX_dxi)); - return dot(u, n) * ((X[1] > 0.99 * ly) ? 1.0 : 0.0); + return dot(u, n); }, - pmesh); + front_face + ); Functional)> area({&solid_solver.displacement().space()}); area.AddSurfaceIntegral( DependsOn<>{}, - [=](double /*t*/, auto position) { - auto X = get<0>(position); - return (X[1] > 0.99 * ly) ? 1.0 : 0.0; - }, - pmesh); + [=](double /*t*/, auto /*position*/) { return 1.0; }, + front_face + ); double t = 0.0; double initial_area = area(t, solid_solver.displacement()); From e615bedf01ad86e5f691d6674b2893bd0e3438b9 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Mon, 18 Nov 2024 19:00:06 -0800 Subject: [PATCH 31/92] guard against accessing retrictions that weren't needed by an integral --- src/serac/numerics/functional/functional.hpp | 26 +++++++----- .../numerics/functional/functional_qoi.inl | 42 +++++++++++-------- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/src/serac/numerics/functional/functional.hpp b/src/serac/numerics/functional/functional.hpp index b2d720264d..bb3340d838 100644 --- a/src/serac/numerics/functional/functional.hpp +++ b/src/serac/numerics/functional/functional.hpp @@ -399,21 +399,25 @@ class Functional { output_L_ = 0.0; for (auto& integral : integrals_) { - Domain & dom = integral.domain_; - const serac::BlockElementRestriction & G_trial = dom.get_restriction(trial_function_spaces_[which]); - input_E_buffer_[which].SetSize(int(G_trial.ESize())); - input_E_[which].Update(input_E_buffer_[which], G_trial.bOffsets()); - G_trial.Gather(input_L_[which], input_E_[which]); + if (integral.DependsOn(which)) { + Domain & dom = integral.domain_; - const serac::BlockElementRestriction & G_test = dom.get_restriction(test_function_space_); - output_E_buffer_.SetSize(int(G_test.ESize())); - output_E_.Update(output_E_buffer_, G_test.bOffsets()); + const serac::BlockElementRestriction & G_trial = dom.get_restriction(trial_function_spaces_[which]); + input_E_buffer_[which].SetSize(int(G_trial.ESize())); + input_E_[which].Update(input_E_buffer_[which], G_trial.bOffsets()); + G_trial.Gather(input_L_[which], input_E_[which]); - integral.GradientMult(input_E_[which], output_E_, which); + const serac::BlockElementRestriction & G_test = dom.get_restriction(test_function_space_); + output_E_buffer_.SetSize(int(G_test.ESize())); + output_E_.Update(output_E_buffer_, G_test.bOffsets()); + + integral.GradientMult(input_E_[which], output_E_, which); + + // scatter-add to compute residuals on the local processor + G_test.ScatterAdd(output_E_, output_L_); + } - // scatter-add to compute residuals on the local processor - G_test.ScatterAdd(output_E_, output_L_); } // scatter-add to compute global residuals diff --git a/src/serac/numerics/functional/functional_qoi.inl b/src/serac/numerics/functional/functional_qoi.inl index c863776be8..4a0476711f 100644 --- a/src/serac/numerics/functional/functional_qoi.inl +++ b/src/serac/numerics/functional/functional_qoi.inl @@ -182,7 +182,7 @@ public: } using signature = test(decltype(serac::type(trial_spaces))...); - integrals_.push_back(MakeBoundaryIntegral(domain, integrand, std::vector{args...})); + integrals_.push_back(MakeBoundaryIntegral(domain, integrand, arg_vec)); } /** @@ -200,7 +200,7 @@ public: using signature = test(decltype(serac::type(trial_spaces))...); integrals_.push_back( - MakeInteriorFaceIntegral(domain, integrand, std::vector{args...})); + MakeInteriorFaceIntegral(domain, integrand, arg_vec)); } /** @@ -261,20 +261,24 @@ public: output_L_ = 0.0; for (auto& integral : integrals_) { - Domain & dom = integral.domain_; - const serac::BlockElementRestriction & G_trial = dom.get_restriction(trial_function_spaces_[which]); - input_E_buffer_[which].SetSize(int(G_trial.ESize())); - input_E_[which].Update(input_E_buffer_[which], G_trial.bOffsets()); - G_trial.Gather(input_L_[which], input_E_[which]); + if (integral.DependsOn(which)) { + Domain & dom = integral.domain_; - output_E_buffer_.SetSize(dom.total_elements()); - output_E_.Update(output_E_buffer_, dom.bOffsets()); + const serac::BlockElementRestriction & G_trial = dom.get_restriction(trial_function_spaces_[which]); + input_E_buffer_[which].SetSize(int(G_trial.ESize())); + input_E_[which].Update(input_E_buffer_[which], G_trial.bOffsets()); + G_trial.Gather(input_L_[which], input_E_[which]); - integral.GradientMult(input_E_[which], output_E_, which); + output_E_buffer_.SetSize(dom.total_elements()); + output_E_.Update(output_E_buffer_, dom.bOffsets()); + + integral.GradientMult(input_E_[which], output_E_, which); + + // scatter-add to compute QoI value for the local processor + G_test_.ScatterAdd(output_E_, output_L_); + } - // scatter-add to compute QoI value for the local processor - G_test_.ScatterAdd(output_E_, output_L_); } // compute global QoI value by summing values from different processors @@ -395,12 +399,14 @@ private: uint64_t max_buffer_size() { uint64_t max_entries = 0; for (auto & integral : form_.integrals_) { - Domain & dom = integral.domain_; - const auto& G_trial = dom.get_restriction(form_.trial_function_spaces_[which_argument]); - for (const auto& [geom, test_restriction] : G_trial.restrictions) { - const auto& trial_restriction = G_trial.restrictions.at(geom); - uint64_t entries_per_element = trial_restriction.nodes_per_elem * trial_restriction.components; - max_entries = std::max(max_entries, trial_restriction.num_elements * entries_per_element); + if (integral.DependsOn(which_argument)) { + Domain & dom = integral.domain_; + const auto& G_trial = dom.get_restriction(form_.trial_function_spaces_[which_argument]); + for (const auto& [geom, test_restriction] : G_trial.restrictions) { + const auto& trial_restriction = G_trial.restrictions.at(geom); + uint64_t entries_per_element = trial_restriction.nodes_per_elem * trial_restriction.components; + max_entries = std::max(max_entries, trial_restriction.num_elements * entries_per_element); + } } } return max_entries; From ce09966995428e406f0baee8ffdb3cb32527307d Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Mon, 18 Nov 2024 19:00:21 -0800 Subject: [PATCH 32/92] update buckling and contact examples --- examples/buckling/cylinder.cpp | 9 ++++----- examples/contact/beam_bending.cpp | 7 ++++--- examples/contact/ironing.cpp | 6 ++++-- examples/contact/sphere.cpp | 7 ++++--- examples/contact/twist.cpp | 7 ++++--- 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/examples/buckling/cylinder.cpp b/examples/buckling/cylinder.cpp index 732c6148a5..483b454144 100644 --- a/examples/buckling/cylinder.cpp +++ b/examples/buckling/cylinder.cpp @@ -124,9 +124,8 @@ int main(int argc, char* argv[]) // Create and refine mesh std::string filename = SERAC_REPO_DIR "/data/meshes/hollow-cylinder.mesh"; - auto mesh = serac::buildMeshFromFile(filename); - auto pmesh = mesh::refineAndDistribute(std::move(mesh), serial_refinement, parallel_refinement); - serac::StateManager::setMesh(std::move(pmesh), mesh_tag); + auto mesh = mesh::refineAndDistribute(serac::buildMeshFromFile(filename), serial_refinement, parallel_refinement); + auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // Surface attributes for boundary conditions std::set xneg{2}; @@ -160,8 +159,8 @@ int main(int argc, char* argv[]) auto lambda = 1.0; auto G = 0.1; solid_mechanics::NeoHookean mat{.density = 1.0, .K = (3 * lambda + 2 * G) / 3, .G = G}; - - solid_solver->setMaterial(mat); + Domain whole_mesh = EntireDomain(pmesh); + solid_solver->setMaterial(mat, whole_mesh); // Set up essential boundary conditions // Bottom of cylinder is fixed diff --git a/examples/contact/beam_bending.cpp b/examples/contact/beam_bending.cpp index 2c54e80a0c..694c3660e8 100644 --- a/examples/contact/beam_bending.cpp +++ b/examples/contact/beam_bending.cpp @@ -36,7 +36,7 @@ int main(int argc, char* argv[]) std::string filename = SERAC_REPO_DIR "/data/meshes/beam-hex-with-contact-block.mesh"; auto mesh = serac::mesh::refineAndDistribute(serac::buildMeshFromFile(filename), 2, 0); - serac::StateManager::setMesh(std::move(mesh), "beam_mesh"); + auto & pmesh = serac::StateManager::setMesh(std::move(mesh), "beam_mesh"); serac::LinearSolverOptions linear_options{.linear_solver = serac::LinearSolver::Strumpack, .print_level = 1}; #ifndef MFEM_USE_STRUMPACK @@ -78,7 +78,8 @@ int main(int argc, char* argv[]) solid_solver.setParameter(1, G_field); serac::solid_mechanics::ParameterizedNeoHookeanSolid mat{1.0, 0.0, 0.0}; - solid_solver.setMaterial(serac::DependsOn<0, 1>{}, mat); + serac::Domain whole_mesh = serac::EntireDomain(pmesh); + solid_solver.setMaterial(serac::DependsOn<0, 1>{}, mat, whole_mesh); // Pass the BC information to the solver object solid_solver.setDisplacementBCs({1}, [](const mfem::Vector&, mfem::Vector& u) { @@ -117,4 +118,4 @@ int main(int argc, char* argv[]) serac::exitGracefully(); return 0; -} \ No newline at end of file +} diff --git a/examples/contact/ironing.cpp b/examples/contact/ironing.cpp index a8f9da3d19..6257f8003e 100644 --- a/examples/contact/ironing.cpp +++ b/examples/contact/ironing.cpp @@ -36,9 +36,10 @@ int main(int argc, char* argv[]) std::string filename = SERAC_REPO_DIR "/data/meshes/ironing.mesh"; auto mesh = serac::mesh::refineAndDistribute(serac::buildMeshFromFile(filename), 2, 0); - serac::StateManager::setMesh(std::move(mesh), "ironing_mesh"); + auto & pmesh = serac::StateManager::setMesh(std::move(mesh), "ironing_mesh"); serac::LinearSolverOptions linear_options{.linear_solver = serac::LinearSolver::Strumpack, .print_level = 1}; + #ifndef MFEM_USE_STRUMPACK SLIC_INFO_ROOT("Contact requires MFEM built with strumpack."); return 1; @@ -78,7 +79,8 @@ int main(int argc, char* argv[]) solid_solver.setParameter(1, G_field); serac::solid_mechanics::ParameterizedNeoHookeanSolid mat{1.0, 0.0, 0.0}; - solid_solver.setMaterial(serac::DependsOn<0, 1>{}, mat); + serac::Domain whole_mesh = serac::EntireDomain(pmesh); + solid_solver.setMaterial(serac::DependsOn<0, 1>{}, mat, whole_mesh); // Pass the BC information to the solver object solid_solver.setDisplacementBCs({5}, [](const mfem::Vector&, mfem::Vector& u) { diff --git a/examples/contact/sphere.cpp b/examples/contact/sphere.cpp index 840d39d9b0..a5d23a3d28 100644 --- a/examples/contact/sphere.cpp +++ b/examples/contact/sphere.cpp @@ -52,7 +52,7 @@ int main(int argc, char* argv[]) std::vector mesh_ptrs{&ball_mesh, &cube_mesh}; auto mesh = serac::mesh::refineAndDistribute(mfem::Mesh(mesh_ptrs.data(), static_cast(mesh_ptrs.size())), 0, 0); - serac::StateManager::setMesh(std::move(mesh), "sphere_mesh"); + auto & pmesh = serac::StateManager::setMesh(std::move(mesh), "sphere_mesh"); serac::LinearSolverOptions linear_options{.linear_solver = serac::LinearSolver::Strumpack, .print_level = 1}; #ifndef MFEM_USE_STRUMPACK @@ -75,7 +75,8 @@ int main(int argc, char* argv[]) nonlinear_options, linear_options, serac::solid_mechanics::default_quasistatic_options, name, "sphere_mesh"); serac::solid_mechanics::NeoHookean mat{1.0, 10.0, 0.25}; - solid_solver.setMaterial(mat); + serac::Domain whole_mesh = serac::EntireDomain(pmesh); + solid_solver.setMaterial(mat, whole_mesh); // Pass the BC information to the solver object solid_solver.setDisplacementBCs({3}, [](const mfem::Vector&, mfem::Vector& u) { @@ -122,4 +123,4 @@ int main(int argc, char* argv[]) serac::exitGracefully(); return 0; -} \ No newline at end of file +} diff --git a/examples/contact/twist.cpp b/examples/contact/twist.cpp index d38a0526c8..ca096c4178 100644 --- a/examples/contact/twist.cpp +++ b/examples/contact/twist.cpp @@ -38,7 +38,7 @@ int main(int argc, char* argv[]) std::string filename = SERAC_REPO_DIR "/data/meshes/twohex_for_contact.mesh"; auto mesh = serac::mesh::refineAndDistribute(serac::buildMeshFromFile(filename), 3, 0); - serac::StateManager::setMesh(std::move(mesh), "twist_mesh"); + auto & pmesh = serac::StateManager::setMesh(std::move(mesh), "twist_mesh"); serac::LinearSolverOptions linear_options{.linear_solver = serac::LinearSolver::Strumpack, .print_level = 1}; #ifndef MFEM_USE_STRUMPACK @@ -61,7 +61,8 @@ int main(int argc, char* argv[]) nonlinear_options, linear_options, serac::solid_mechanics::default_quasistatic_options, name, "twist_mesh"); serac::solid_mechanics::NeoHookean mat{1.0, 10.0, 10.0}; - solid_solver.setMaterial(mat); + serac::Domain whole_mesh = serac::EntireDomain(pmesh); + solid_solver.setMaterial(mat, whole_mesh); // Pass the BC information to the solver object solid_solver.setDisplacementBCs({3}, [](const mfem::Vector&, mfem::Vector& u) { @@ -108,4 +109,4 @@ int main(int argc, char* argv[]) serac::exitGracefully(); return 0; -} \ No newline at end of file +} From e824db3fe7faac44582a2d3d1cef0c908fff485e Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Mon, 18 Nov 2024 19:01:12 -0800 Subject: [PATCH 33/92] update more examples and tests, add convenience function for checking which arguments an Integral depends on --- src/serac/numerics/functional/integral.hpp | 13 +++++++++++ .../functional/tests/bug_boundary_qoi.cpp | 4 +++- .../functional/tests/functional_qoi.cpp | 19 +++++++++++---- .../numerics/tests/equationsolver_petsc.cpp | 4 +++- src/serac/physics/tests/beam_bending.cpp | 13 +++++++---- .../parameterized_thermomechanics_example.cpp | 14 ++--------- .../physics/tests/solid_reaction_adjoint.cpp | 23 ++++++++----------- 7 files changed, 53 insertions(+), 37 deletions(-) diff --git a/src/serac/numerics/functional/integral.hpp b/src/serac/numerics/functional/integral.hpp index 839f28eaa3..7827ab5773 100644 --- a/src/serac/numerics/functional/integral.hpp +++ b/src/serac/numerics/functional/integral.hpp @@ -120,6 +120,19 @@ struct Integral { } } } + + /** + * @brief returns whether or not this integral depends on argument `which` + * + * @param which an argument index + * @return true when if this Integral object was created with a DependsOn<...> statement that includes `i` + */ + bool DependsOn(uint32_t which) const { + for (uint32_t i : active_trial_spaces_) { + if (which == i) return true; + } + return false; + } /// @brief information about which elements to integrate over Domain domain_; diff --git a/src/serac/numerics/functional/tests/bug_boundary_qoi.cpp b/src/serac/numerics/functional/tests/bug_boundary_qoi.cpp index 97e6a6159d..38f0932e91 100644 --- a/src/serac/numerics/functional/tests/bug_boundary_qoi.cpp +++ b/src/serac/numerics/functional/tests/bug_boundary_qoi.cpp @@ -48,8 +48,10 @@ TEST(BoundaryIntegralQOI, AttrBug) using shapeFES = serac::H1; auto [shape_fes, shape_fec] = serac::generateParFiniteElementSpace(pmesh.get()); + Domain whole_boundary = EntireBoundary(*pmesh); + serac::ShapeAwareFunctional totalSurfArea(shape_fes.get(), {}); - totalSurfArea.AddBoundaryIntegral(serac::Dimension<2 - 1>{}, serac::DependsOn<>{}, IdentityFunctor{}, *pmesh); + totalSurfArea.AddBoundaryIntegral(serac::Dimension<2 - 1>{}, serac::DependsOn<>{}, IdentityFunctor{}, whole_boundary); serac::FiniteElementState shape(*shape_fes); double totalSurfaceArea = totalSurfArea(0.0, shape); diff --git a/src/serac/numerics/functional/tests/functional_qoi.cpp b/src/serac/numerics/functional/tests/functional_qoi.cpp index 98c1eba7ca..f6e3b1fefd 100644 --- a/src/serac/numerics/functional/tests/functional_qoi.cpp +++ b/src/serac/numerics/functional/tests/functional_qoi.cpp @@ -363,8 +363,10 @@ TEST(QoI, DependsOnVectorValuedInput) mfem::HypreParVector U = *tmp; U_gf.GetTrueDofs(U); + Domain whole_mesh = EntireDomain(mesh); + Functional f({fespace.get()}); - f.AddVolumeIntegral(DependsOn<0>{}, GetNormZeroIntegrator{}, mesh); + f.AddVolumeIntegral(DependsOn<0>{}, GetNormZeroIntegrator{}, whole_mesh); double exact_answer = 141.3333333333333; double relative_error = (f(t, U) - exact_answer) / exact_answer; @@ -393,7 +395,9 @@ TEST(QoI, AddAreaIntegral) U_gf.GetTrueDofs(U); Functional measure({fespace.get()}); - measure.AddAreaIntegral(DependsOn<>{}, TrivialIntegrator{}, mesh); + + Domain whole_mesh = EntireDomain(mesh); + measure.AddAreaIntegral(DependsOn<>{}, TrivialIntegrator{}, whole_mesh); double relative_error = (measure(t, U) - measure_mfem(mesh)) / measure(t, U); EXPECT_NEAR(0.0, relative_error, 1.0e-10); @@ -421,7 +425,9 @@ TEST(QoI, AddVolumeIntegral) U_gf.GetTrueDofs(U); Functional measure({fespace.get()}); - measure.AddVolumeIntegral(DependsOn<>{}, TrivialIntegrator{}, mesh); + + Domain whole_mesh = EntireDomain(mesh); + measure.AddVolumeIntegral(DependsOn<>{}, TrivialIntegrator{}, whole_mesh); double relative_error = (measure(t, U) - measure_mfem(mesh)) / measure(t, U); EXPECT_NEAR(0.0, relative_error, 1.0e-10); @@ -451,8 +457,11 @@ TEST(QoI, UsingL2) // this tests a fix for the QoI constructor segfaulting when using L2 spaces Functional f({fespace_0.get(), fespace_1.get()}); - f.AddVolumeIntegral(DependsOn<1>{}, TrivialVariadicIntegrator{}, mesh); - f.AddSurfaceIntegral(DependsOn<0>{}, TrivialVariadicIntegrator{}, mesh); + Domain whole_mesh = EntireDomain(mesh); + Domain whole_boundary = EntireBoundary(mesh); + + f.AddVolumeIntegral(DependsOn<1>{}, TrivialVariadicIntegrator{}, whole_mesh); + f.AddSurfaceIntegral(DependsOn<0>{}, TrivialVariadicIntegrator{}, whole_boundary); check_gradient(f, t, *U0, *U1); } diff --git a/src/serac/numerics/tests/equationsolver_petsc.cpp b/src/serac/numerics/tests/equationsolver_petsc.cpp index c92fbededf..208efa50ae 100644 --- a/src/serac/numerics/tests/equationsolver_petsc.cpp +++ b/src/serac/numerics/tests/equationsolver_petsc.cpp @@ -61,6 +61,8 @@ TEST_P(EquationSolverSuite, All) x_exact.Randomize(0); + Domain domain = EntireDomain(pmesh); + residual.AddDomainIntegral( Dimension{}, DependsOn<0>{}, [&](double /*t*/, auto, auto scalar) { @@ -69,7 +71,7 @@ TEST_P(EquationSolverSuite, All) auto flux = du_dx; return serac::tuple{source, flux}; }, - pmesh); + domain); StdFunctionOperator residual_opr( fes.TrueVSize(), diff --git a/src/serac/physics/tests/beam_bending.cpp b/src/serac/physics/tests/beam_bending.cpp index 557bb7a210..78c366ec70 100644 --- a/src/serac/physics/tests/beam_bending.cpp +++ b/src/serac/physics/tests/beam_bending.cpp @@ -40,7 +40,7 @@ TEST(BeamBending, TwoDimensional) std::string mesh_tag{"mesh"}; - serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); serac::LinearSolverOptions linear_options{.linear_solver = LinearSolver::GMRES, .preconditioner = Preconditioner::HypreAMG, @@ -69,7 +69,8 @@ TEST(BeamBending, TwoDimensional) double K = 1.91666666666667; double G = 1.0; solid_mechanics::StVenantKirchhoff mat{1.0, K, G}; - solid_solver.setMaterial(mat); + Domain material_block = EntireDomain(pmesh); + solid_solver.setMaterial(mat, material_block); // Define the function for the initial displacement and boundary condition auto bc = [](const mfem::Vector&, mfem::Vector& bc_vec) -> void { bc_vec = 0.0; }; @@ -79,8 +80,12 @@ TEST(BeamBending, TwoDimensional) solid_solver.setDisplacementBCs(ess_bdr, bc); solid_solver.setDisplacement(bc); - solid_solver.setTraction([](const auto& x, const auto& n, const double) { return -0.01 * n * (x[1] > 0.99); }, - EntireBoundary(StateManager::mesh(mesh_tag))); + + Domain top_face = Domain::ofBoundaryElements(pmesh, [](std::vector vertices, int/*attr*/) { + return (average(vertices)[1] > 0.99); + }); + + solid_solver.setTraction([](auto /*x*/, auto n, auto /*t*/) { return -0.01 * n; }, top_face); // Finalize the data structures solid_solver.completeSetup(); diff --git a/src/serac/physics/tests/parameterized_thermomechanics_example.cpp b/src/serac/physics/tests/parameterized_thermomechanics_example.cpp index 89aa0ec0d4..085a6bb82a 100644 --- a/src/serac/physics/tests/parameterized_thermomechanics_example.cpp +++ b/src/serac/physics/tests/parameterized_thermomechanics_example.cpp @@ -20,16 +20,6 @@ using namespace serac; -template -tensor average(std::vector >& positions) -{ - tensor total{}; - for (auto x : positions) { - total += x; - } - return total / double(positions.size()); -} - template auto greenStrain(const tensor& grad_u) { @@ -118,8 +108,8 @@ TEST(Thermomechanics, ParameterizedMaterial) double theta_ref = 0.0; ///< datum temperature for thermal expansion ParameterizedThermoelasticMaterial material{density, E, nu, theta_ref}; - - simulation.setMaterial(DependsOn<0, 1>{}, material); + Domain material_block = EntireDomain(pmesh); + simulation.setMaterial(DependsOn<0, 1>{}, material, material_block); double deltaT = 1.0; FiniteElementState temperature(pmesh, H1

{}, "theta"); diff --git a/src/serac/physics/tests/solid_reaction_adjoint.cpp b/src/serac/physics/tests/solid_reaction_adjoint.cpp index 6b65fea58f..75edbc6335 100644 --- a/src/serac/physics/tests/solid_reaction_adjoint.cpp +++ b/src/serac/physics/tests/solid_reaction_adjoint.cpp @@ -37,7 +37,7 @@ constexpr double boundary_disp = 0.013; constexpr double shear_modulus_value = 1.0; constexpr double bulk_modulus_value = 1.0; -std::unique_ptr createNonlinearSolidMechanicsSolver(mfem::ParMesh&, +std::unique_ptr createNonlinearSolidMechanicsSolver(mfem::ParMesh& pmesh, const NonlinearSolverOptions& nonlinear_opts, const SolidMaterial& mat) { @@ -57,29 +57,24 @@ std::unique_ptr createNonlinearSolidMechanicsSolver(mfem::Pa solid->setParameter(0, user_defined_bulk_modulus); solid->setParameter(1, user_defined_shear_modulus); - solid->setMaterial(DependsOn<0, 1>{}, mat); - solid->setDisplacementBCs({1}, [](const mfem::Vector&, mfem::Vector& disp) { disp = boundary_disp; }); + Domain whole_mesh = EntireDomain(pmesh); + + solid->setMaterial(DependsOn<0, 1>{}, mat, whole_mesh); + solid->addBodyForce([](auto X, auto /* t */) { auto Y = X; Y[0] = 0.1 + 0.1 * X[0] + 0.3 * X[1]; Y[1] = -0.05 - 0.2 * X[0] + 0.15 * X[1]; return 0.1 * X + Y; - }); + }, whole_mesh); + + solid->setDisplacementBCs({1}, [](const mfem::Vector&, mfem::Vector& disp) { disp = boundary_disp; }); + solid->completeSetup(); return solid; } -template -tensor average(std::vector>& positions) -{ - tensor total{}; - for (auto x : positions) { - total += x; - } - return total / double(positions.size()); -} - FiniteElementState createReactionDirection(const BasePhysics& solid_solver, int direction) { const FiniteElementDual& reactions = solid_solver.dual("reactions"); From f78da844868ef9a2099d3ab11fcbe10c109ba3d6 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Tue, 19 Nov 2024 07:15:24 -0800 Subject: [PATCH 34/92] updating heat_transfer module + tests, some other examples --- .../tests/functional_tet_quality.cpp | 6 ++- src/serac/physics/fit.hpp | 12 ++--- src/serac/physics/heat_transfer.hpp | 44 +++++++++---------- .../physics/tests/thermal_dynamics_patch.cpp | 19 ++++---- .../physics/tests/thermal_finite_diff.cpp | 31 +++++++------ .../physics/tests/thermal_nonlinear_solve.cpp | 21 +++++---- .../physics/tests/thermal_robin_condition.cpp | 31 +++++++------ src/serac/physics/tests/thermal_shape.cpp | 12 ++--- 8 files changed, 94 insertions(+), 82 deletions(-) diff --git a/src/serac/numerics/functional/tests/functional_tet_quality.cpp b/src/serac/numerics/functional/tests/functional_tet_quality.cpp index 36a2373778..5b90eb71ea 100644 --- a/src/serac/numerics/functional/tests/functional_tet_quality.cpp +++ b/src/serac/numerics/functional/tests/functional_tet_quality.cpp @@ -49,6 +49,8 @@ TEST(QoI, TetrahedronQuality) auto [fes, fec] = generateParFiniteElementSpace(mesh.get()); + Domain whole_domain = EntireDomain(*mesh); + // Define the shape-aware QOI objects serac::ShapeAwareFunctional saf_qoi(fes.get(), {}); @@ -60,7 +62,7 @@ TEST(QoI, TetrahedronQuality) auto [x, dx_dxi] = position; return mu(dot(regular_tet_correction, dx_dxi)); }, - *mesh); + whole_domain); serac::Functional qoi({fes.get()}); @@ -78,7 +80,7 @@ TEST(QoI, TetrahedronQuality) auto dx_dxi = dX_dxi + dot(du_dX, dX_dxi); return mu(dot(regular_tet_correction, dx_dxi)); }, - *mesh); + whole_domain); std::unique_ptr u(fes->NewTrueDofVector()); *u = 0.0; diff --git a/src/serac/physics/fit.hpp b/src/serac/physics/fit.hpp index a83c63b48d..a0e5d20991 100644 --- a/src/serac/physics/fit.hpp +++ b/src/serac/physics/fit.hpp @@ -17,13 +17,13 @@ namespace detail { /// @overload template -FiniteElementState fit(std::integer_sequence, func f, mfem::ParMesh& mesh, const T&... solution_fields) +FiniteElementState fit(std::integer_sequence, func f, Domain & domain, const T&... solution_fields) { // signature looks like return_type(arg0_type, arg1_type); // so this unpacks the return type using output_space = typename FunctionSignature::return_type; - FiniteElementState fitted_field(mesh, output_space{}); + FiniteElementState fitted_field(domain.mesh_, output_space{}); fitted_field = 0.0; // mass term @@ -33,13 +33,13 @@ FiniteElementState fit(std::integer_sequence, func f, mfem::ParMesh& [](double /*t*/, auto /*x*/, auto u) { return tuple{get<0>(u), zero{}}; }, - mesh); + domain); auto M = get<1>(phi_phi(DifferentiateWRT<0>{}, 0.0 /* t */, fitted_field)); // rhs std::array trial_spaces = {&solution_fields.space()...}; serac::Functional phi_f(&fitted_field.space(), trial_spaces); - phi_f.AddDomainIntegral(Dimension{}, DependsOn{}, f, mesh); + phi_f.AddDomainIntegral(Dimension{}, DependsOn{}, f, domain); mfem::Vector b = phi_f(0.0, solution_fields...); mfem::CGSolver cg(MPI_COMM_WORLD); @@ -63,10 +63,10 @@ FiniteElementState fit(std::integer_sequence, func f, mfem::ParMesh& * @note: mesh is passed by non-const ref because mfem mutates the mesh when creating ParGridFunctions */ template -FiniteElementState fit(func f, mfem::ParMesh& mesh, const T&... solution_fields) +FiniteElementState fit(func f, Domain & domain, const T&... solution_fields) { auto iseq = std::make_integer_sequence{}; - return detail::fit(iseq, f, mesh, solution_fields...); + return detail::fit(iseq, f, domain, solution_fields...); } } // namespace serac diff --git a/src/serac/physics/heat_transfer.hpp b/src/serac/physics/heat_transfer.hpp index 0147658bdd..1207979763 100644 --- a/src/serac/physics/heat_transfer.hpp +++ b/src/serac/physics/heat_transfer.hpp @@ -429,17 +429,17 @@ class HeatTransfer, std::integer_sequ * @note This method must be called prior to completeSetup() */ template - void setMaterial(DependsOn, const MaterialType& material) + void setMaterial(DependsOn, const MaterialType& material, Domain & domain) { residual_->AddDomainIntegral(Dimension{}, DependsOn<0, 1, NUM_STATE_VARS + active_parameters...>{}, - ThermalMaterialIntegrand(material), mesh_); + ThermalMaterialIntegrand(material), domain); } /// @overload template - void setMaterial(const MaterialType& material) + void setMaterial(const MaterialType& material, Domain & domain) { - setMaterial(DependsOn<>{}, material); + setMaterial(DependsOn<>{}, material, domain); } /** @@ -486,10 +486,8 @@ class HeatTransfer, std::integer_sequ */ template void setSource(DependsOn, SourceType source_function, - const std::optional& optional_domain = std::nullopt) + Domain & domain) { - Domain domain = (optional_domain) ? *optional_domain : EntireDomain(mesh_); - residual_->AddDomainIntegral( Dimension{}, DependsOn<0, 1, active_parameters + NUM_STATE_VARS...>{}, [source_function](double t, auto x, auto temperature, auto /* dtemp_dt */, auto... params) { @@ -506,9 +504,9 @@ class HeatTransfer, std::integer_sequ /// @overload template - void setSource(SourceType source_function, const std::optional& optional_domain = std::nullopt) + void setSource(SourceType source_function, Domain & domain) { - setSource(DependsOn<>{}, source_function, optional_domain); + setSource(DependsOn<>{}, source_function, domain); } /** @@ -536,10 +534,8 @@ class HeatTransfer, std::integer_sequ */ template void setFluxBCs(DependsOn, FluxType flux_function, - const std::optional& optional_domain = std::nullopt) + Domain & domain) { - Domain domain = (optional_domain) ? *optional_domain : EntireBoundary(mesh_); - residual_->AddBoundaryIntegral( Dimension{}, DependsOn<0, 1, active_parameters + NUM_STATE_VARS...>{}, [flux_function](double t, auto X, auto u, auto /* dtemp_dt */, auto... params) { @@ -553,9 +549,9 @@ class HeatTransfer, std::integer_sequ /// @overload template - void setFluxBCs(FluxType flux_function, const std::optional& optional_domain = std::nullopt) + void setFluxBCs(FluxType flux_function, Domain & domain) { - setFluxBCs(DependsOn<>{}, flux_function, optional_domain); + setFluxBCs(DependsOn<>{}, flux_function, domain); } /** @@ -651,17 +647,19 @@ class HeatTransfer, std::integer_sequ * * @tparam active_parameters a list of indices, describing which parameters to pass to the q-function * @param qfunction a callable that returns the normal heat flux on a boundary surface - * @param optional_domain The domain over which the integral is computed + * @param domain The domain over which the integral is computed * * ~~~ {.cpp} * * heat_transfer.addCustomBoundaryIntegral( - * DependsOn<>{}, - * [](double t, auto position, auto temperature, auto temperature_rate) { - * auto [T, dT_dxi] = temperature; - * auto q = 5.0*(T-25.0); - * return q; // define a temperature-proportional heat-flux - * }); + * DependsOn<>{}, + * [](double t, auto position, auto temperature, auto temperature_rate) { + * auto [T, dT_dxi] = temperature; + * auto q = 5.0*(T-25.0); + * return q; // define a temperature-proportional heat-flux + * }, + * domain + * ); * * ~~~ * @@ -669,10 +667,8 @@ class HeatTransfer, std::integer_sequ */ template void addCustomBoundaryIntegral(DependsOn, callable qfunction, - const std::optional& optional_domain = std::nullopt) + Domain & domain) { - Domain domain = (optional_domain) ? *optional_domain : EntireBoundary(mesh_); - residual_->AddBoundaryIntegral(Dimension{}, DependsOn<0, 1, active_parameters + NUM_STATE_VARS...>{}, qfunction, domain); } diff --git a/src/serac/physics/tests/thermal_dynamics_patch.cpp b/src/serac/physics/tests/thermal_dynamics_patch.cpp index 0dfd6b8187..e496d46787 100644 --- a/src/serac/physics/tests/thermal_dynamics_patch.cpp +++ b/src/serac/physics/tests/thermal_dynamics_patch.cpp @@ -99,12 +99,10 @@ double dynamic_solution_error(const ExactSolution& exact_solution, PatchBoundary static_assert(dim == 2 || dim == 3, "Dimension must be 2 or 3 for heat transfer test"); + std::string mesh_tag{"mesh"}; std::string filename = std::string(SERAC_REPO_DIR) + "/data/meshes/patch" + std::to_string(dim) + "D.mesh"; auto mesh = mesh::refineAndDistribute(buildMeshFromFile(filename)); - - std::string mesh_tag{"mesh"}; - - serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // Construct a heat transfer solver NonlinearSolverOptions nonlinear_opts{.relative_tol = 5.0e-13, .absolute_tol = 5.0e-13}; @@ -114,14 +112,17 @@ double dynamic_solution_error(const ExactSolution& exact_solution, PatchBoundary HeatTransfer thermal(nonlinear_opts, heat_transfer::direct_linear_options, dyn_opts, "thermal", mesh_tag); + Domain whole_domain = EntireDomain(pmesh); + Domain whole_boundary = EntireBoundary(pmesh); + heat_transfer::LinearIsotropicConductor mat(1.0, 1.0, 1.0); - thermal.setMaterial(mat); + thermal.setMaterial(mat, whole_domain); // initial conditions thermal.setTemperature([exact_solution](const mfem::Vector& x, double) { return exact_solution(x, 0.0); }); // forcing terms - exact_solution.applyLoads(mat, thermal, essentialBoundaryAttributes(bc)); + exact_solution.applyLoads(mat, thermal, whole_domain, whole_boundary, essentialBoundaryAttributes(bc)); // Finalize the data structures thermal.completeSetup(); @@ -190,7 +191,7 @@ class LinearSolution { * @param essential_boundaries Boundary attributes on which essential boundary conditions are desired */ template - void applyLoads(const Material& material, HeatTransfer& thermal, std::set essential_boundaries) const + void applyLoads(const Material& material, HeatTransfer& thermal, Domain & dom, Domain & bdr, std::set essential_boundaries) const { // essential BCs auto ebc_func = [*this](const auto& X, double t) { return this->operator()(X, t); }; @@ -205,13 +206,13 @@ class LinearSolution { return dot(flux, n0); }; - thermal.setFluxBCs(flux_function, EntireBoundary(thermal.mesh())); + thermal.setFluxBCs(flux_function, bdr); // volumetric source auto source_function = [temp_rate_grad](auto position, auto /* time */, auto /* u */, auto /* du_dx */) { return dot(get(position), temp_rate_grad); }; - thermal.setSource(source_function, EntireDomain(thermal.mesh())); + thermal.setSource(source_function, dom); } private: diff --git a/src/serac/physics/tests/thermal_finite_diff.cpp b/src/serac/physics/tests/thermal_finite_diff.cpp index 6b69791fe0..028a636232 100644 --- a/src/serac/physics/tests/thermal_finite_diff.cpp +++ b/src/serac/physics/tests/thermal_finite_diff.cpp @@ -65,9 +65,20 @@ TEST(Thermal, FiniteDifference) thermal_solver.setParameter(0, user_defined_conductivity); + Domain whole_domain = EntireDomain(pmesh); + Domain whole_boundary = EntireBoundary(pmesh); + // Construct a potentially user-defined parameterized material and send it to the thermal module heat_transfer::ParameterizedLinearIsotropicConductor mat; - thermal_solver.setMaterial(DependsOn<0>{}, mat); + thermal_solver.setMaterial(DependsOn<0>{}, mat, whole_domain); + + // Define a constant source term + heat_transfer::ConstantSource source{1.0}; + thermal_solver.setSource(source, whole_domain); + + // Set the flux term to zero for testing code paths + heat_transfer::ConstantFlux flux_bc{0.0}; + thermal_solver.setFluxBCs(flux_bc, whole_boundary); // Define the function for the initial temperature and boundary condition auto bdr_temp = [](const mfem::Vector& x, double) -> double { return (x[0] < 0.5 || x[1] < 0.5) ? 1.0 : 0.0; }; @@ -76,14 +87,6 @@ TEST(Thermal, FiniteDifference) thermal_solver.setTemperatureBCs(ess_bdr, bdr_temp); thermal_solver.setTemperature(bdr_temp); - // Define a constant source term - heat_transfer::ConstantSource source{1.0}; - thermal_solver.setSource(source, EntireDomain(pmesh)); - - // Set the flux term to zero for testing code paths - heat_transfer::ConstantFlux flux_bc{0.0}; - thermal_solver.setFluxBCs(flux_bc, EntireBoundary(pmesh)); - // Finalize the data structures thermal_solver.completeSetup(); @@ -190,7 +193,12 @@ TEST(HeatTransfer, FiniteDifferenceShape) heat_transfer::LinearIsotropicConductor mat(1.0, 1.0, 1.0); - thermal_solver.setMaterial(mat); + Domain whole_domain = EntireDomain(pmesh); + + thermal_solver.setMaterial(mat, whole_domain); + + heat_transfer::ConstantSource source{1.0}; + thermal_solver.setSource(source, whole_domain); FiniteElementState shape_displacement(pmesh, H1{}); @@ -204,9 +212,6 @@ TEST(HeatTransfer, FiniteDifferenceShape) thermal_solver.setTemperatureBCs(ess_bdr, one); thermal_solver.setTemperature(one); - heat_transfer::ConstantSource source{1.0}; - thermal_solver.setSource(source, EntireDomain(pmesh)); - // Finalize the data structures thermal_solver.completeSetup(); diff --git a/src/serac/physics/tests/thermal_nonlinear_solve.cpp b/src/serac/physics/tests/thermal_nonlinear_solve.cpp index 32cc4a1c5f..89f7321f6a 100644 --- a/src/serac/physics/tests/thermal_nonlinear_solve.cpp +++ b/src/serac/physics/tests/thermal_nonlinear_solve.cpp @@ -41,7 +41,7 @@ void functional_thermal_test_nonlinear() std::string mesh_tag{"mesh"}; auto mesh = mesh::refineAndDistribute(buildMeshFromFile(filename), serial_refinement, parallel_refinement); - serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // _solver_params_start serac::NonlinearSolverOptions nonlinear_options{.nonlin_solver = NonlinearSolver::NewtonLineSearch, @@ -60,12 +60,13 @@ void functional_thermal_test_nonlinear() 21.0 // isotropic thermal conductivity }; - thermal_solver.setMaterial(mat); + Domain whole_domain = EntireDomain(pmesh); + Domain whole_boundary = EntireBoundary(pmesh); - // prescribe zero temperature at one end of the beam - std::set support = {1}; - auto zero = [](const mfem::Vector&, double) -> double { return 42.0; }; - thermal_solver.setTemperatureBCs(support, zero); + thermal_solver.setMaterial(mat, whole_domain); + + // set heat source + thermal_solver.setSource([](auto, auto, auto, auto) { return 2.0; }, whole_domain); // clang-format off thermal_solver.addCustomBoundaryIntegral(serac::DependsOn<>{}, [&](auto, auto, auto temperature, auto) { @@ -74,11 +75,13 @@ void functional_thermal_test_nonlinear() using std::pow; auto T = serac::get<0>(temperature); return radiateConstant * (pow(T, 4.0) - pow(T0, 4.0)); - }); + }, whole_boundary); // clang-format on - // set heat source - thermal_solver.setSource([](auto, auto, auto, auto) { return 2.0; }); + // prescribe zero temperature at one end of the beam + std::set support = {1}; + auto zero = [](const mfem::Vector&, double) -> double { return 42.0; }; + thermal_solver.setTemperatureBCs(support, zero); // Finalize the data structures thermal_solver.completeSetup(); diff --git a/src/serac/physics/tests/thermal_robin_condition.cpp b/src/serac/physics/tests/thermal_robin_condition.cpp index 6a2d7b6146..27a757d3e9 100644 --- a/src/serac/physics/tests/thermal_robin_condition.cpp +++ b/src/serac/physics/tests/thermal_robin_condition.cpp @@ -41,7 +41,7 @@ void functional_thermal_test_robin_condition() std::string mesh_tag{"mesh"}; auto mesh = mesh::refineAndDistribute(buildMeshFromFile(filename), serial_refinement, parallel_refinement); - serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // _solver_params_start serac::NonlinearSolverOptions nonlinear_options{.nonlin_solver = NonlinearSolver::Newton, @@ -60,24 +60,29 @@ void functional_thermal_test_robin_condition() 1.0 // isotropic thermal conductivity }; - thermal_solver.setMaterial(mat); + Domain whole_domain = EntireDomain(pmesh); + Domain whole_boundary = EntireBoundary(pmesh); - // prescribe zero temperature at one end of the beam - std::set support = {1}; - auto zero = [](const mfem::Vector&, double) -> double { return 0.0; }; - thermal_solver.setTemperatureBCs(support, zero); + thermal_solver.setMaterial(mat, whole_domain); + + // set heat source + thermal_solver.setSource([](auto, auto, auto, auto) { return 2.0; }, whole_domain); // clang-format off thermal_solver.addCustomBoundaryIntegral(DependsOn<>{}, - [](double /* t */, auto /*position*/, auto temperature, auto /*temperature_rate*/) { - auto [T, dT_dxi] = temperature; - auto q = 5.0*(T-25.0); - return q; // define a convective (temperature-proportional) heat flux - }); + [](double /* t */, auto /*position*/, auto temperature, auto /*temperature_rate*/) { + auto [T, dT_dxi] = temperature; + auto q = 5.0*(T-25.0); + return q; // define a convective (temperature-proportional) heat flux + }, + whole_boundary + ); // clang-format on - // set heat source - thermal_solver.setSource([](auto, auto, auto, auto) { return 2.0; }); + // prescribe zero temperature at one end of the beam + std::set support = {1}; + auto zero = [](const mfem::Vector&, double) -> double { return 0.0; }; + thermal_solver.setTemperatureBCs(support, zero); // Finalize the data structures thermal_solver.completeSetup(); diff --git a/src/serac/physics/tests/thermal_shape.cpp b/src/serac/physics/tests/thermal_shape.cpp index bb25ac9e4c..afd19b0bb4 100644 --- a/src/serac/physics/tests/thermal_shape.cpp +++ b/src/serac/physics/tests/thermal_shape.cpp @@ -94,9 +94,9 @@ TEST(HeatTransfer, MoveShape) shape_displacement.project(shape_coef); thermal_solver.setShapeDisplacement(shape_displacement); - thermal_solver.setMaterial(mat); - - thermal_solver.setSource(source, EntireDomain(StateManager::mesh(mesh_tag))); + Domain whole_domain = EntireDomain(pmesh); + thermal_solver.setMaterial(mat, whole_domain); + thermal_solver.setSource(source, whole_domain); // Finalize the data structures thermal_solver.completeSetup(); @@ -138,9 +138,9 @@ TEST(HeatTransfer, MoveShape) thermal_solver_no_shape.setTemperatureBCs(ess_bdr, zero); thermal_solver_no_shape.setTemperature(zero); - thermal_solver_no_shape.setMaterial(mat); - - thermal_solver_no_shape.setSource(source, EntireDomain(StateManager::mesh(pure_mesh_tag))); + Domain whole_domain = EntireDomain(new_pmesh); + thermal_solver_no_shape.setMaterial(mat, whole_domain); + thermal_solver_no_shape.setSource(source, whole_domain); // Finalize the data structures thermal_solver_no_shape.completeSetup(); From 9f828b7ffb14a0050608b2db0052868350db1ef7 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Tue, 19 Nov 2024 08:00:56 -0800 Subject: [PATCH 35/92] update addCustomDomainIntegral in solid mechanics module --- src/serac/physics/solid_mechanics.hpp | 4 ++-- src/serac/physics/tests/solid_robin_condition.cpp | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/serac/physics/solid_mechanics.hpp b/src/serac/physics/solid_mechanics.hpp index e081d02152..6eb508f132 100644 --- a/src/serac/physics/solid_mechanics.hpp +++ b/src/serac/physics/solid_mechanics.hpp @@ -837,11 +837,11 @@ class SolidMechanics, std::integer_se * @note This method must be called prior to completeSetup() */ template - void addCustomDomainIntegral(DependsOn, callable qfunction, + void addCustomDomainIntegral(DependsOn, callable qfunction, Domain & domain, qdata_type qdata = NoQData) { residual_->AddDomainIntegral(Dimension{}, DependsOn<0, 1, active_parameters + NUM_STATE_VARS...>{}, qfunction, - mesh_, qdata); + domain, qdata); } /** diff --git a/src/serac/physics/tests/solid_robin_condition.cpp b/src/serac/physics/tests/solid_robin_condition.cpp index 7b90bc2529..cb97c7a874 100644 --- a/src/serac/physics/tests/solid_robin_condition.cpp +++ b/src/serac/physics/tests/solid_robin_condition.cpp @@ -42,7 +42,7 @@ void functional_solid_test_robin_condition() std::string mesh_tag{"mesh"}; auto mesh = mesh::refineAndDistribute(buildMeshFromFile(filename), serial_refinement, parallel_refinement); - serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // _solver_params_start serac::NonlinearSolverOptions nonlinear_options{.nonlin_solver = NonlinearSolver::Newton, @@ -61,7 +61,8 @@ void functional_solid_test_robin_condition() 1.0 // shear modulus }; - solid_solver.setMaterial(mat); + Domain whole_domain = EntireDomain(pmesh); + solid_solver.setMaterial(mat, whole_domain); // prescribe zero displacement in the y- and z-directions // at the supported end of the beam, From e3a87d1c476501ba275a94719b0921690c200bc0 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Tue, 19 Nov 2024 10:51:05 -0800 Subject: [PATCH 36/92] fix const issue --- src/serac/numerics/functional/detail/hexahedron_H1.inl | 8 ++++---- src/serac/numerics/functional/detail/hexahedron_Hcurl.inl | 8 ++++---- src/serac/numerics/functional/detail/hexahedron_L2.inl | 8 ++++---- src/serac/numerics/functional/detail/quadrilateral_H1.inl | 8 ++++---- .../numerics/functional/detail/quadrilateral_Hcurl.inl | 8 ++++---- src/serac/numerics/functional/detail/segment_H1.inl | 8 ++++---- src/serac/numerics/functional/detail/segment_L2.inl | 8 ++++---- src/serac/numerics/functional/detail/tetrahedron_H1.inl | 8 ++++---- src/serac/numerics/functional/detail/tetrahedron_L2.inl | 8 ++++---- src/serac/numerics/functional/detail/triangle_H1.inl | 8 ++++---- src/serac/numerics/functional/detail/triangle_L2.inl | 8 ++++---- 11 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/serac/numerics/functional/detail/hexahedron_H1.inl b/src/serac/numerics/functional/detail/hexahedron_H1.inl index c28b15fa0e..062fd7ad53 100644 --- a/src/serac/numerics/functional/detail/hexahedron_H1.inl +++ b/src/serac/numerics/functional/detail/hexahedron_H1.inl @@ -153,10 +153,10 @@ struct finite_element > { B(qx, jx) * B(qy, jy) * G(qz, jz)}; int Q = (qz * q + qy) * q + qx; - auto& d00 = get<0>(get<0>(input(Q))); - auto& d01 = get<1>(get<0>(input(Q))); - auto& d10 = get<0>(get<1>(input(Q))); - auto& d11 = get<1>(get<1>(input(Q))); + const auto& d00 = get<0>(get<0>(input(Q))); + const auto& d01 = get<1>(get<0>(input(Q))); + const auto& d10 = get<0>(get<1>(input(Q))); + const auto& d11 = get<1>(get<1>(input(Q))); output[Q] = {d00 * phi_j + dot(d01, dphi_j_dxi), d10 * phi_j + dot(d11, dphi_j_dxi)}; } diff --git a/src/serac/numerics/functional/detail/hexahedron_Hcurl.inl b/src/serac/numerics/functional/detail/hexahedron_Hcurl.inl index 36fdfd3d2f..30cfc2cbe6 100644 --- a/src/serac/numerics/functional/detail/hexahedron_Hcurl.inl +++ b/src/serac/numerics/functional/detail/hexahedron_Hcurl.inl @@ -319,10 +319,10 @@ struct finite_element> { } int Q = (qz * q + qy) * q + qx; - auto& d00 = get<0>(get<0>(input(Q))); - auto& d01 = get<1>(get<0>(input(Q))); - auto& d10 = get<0>(get<1>(input(Q))); - auto& d11 = get<1>(get<1>(input(Q))); + const auto& d00 = get<0>(get<0>(input(Q))); + const auto& d01 = get<1>(get<0>(input(Q))); + const auto& d10 = get<0>(get<1>(input(Q))); + const auto& d11 = get<1>(get<1>(input(Q))); output[Q] = {dot(d00, phi_j) + dot(d01, curl_phi_j), dot(d10, phi_j) + dot(d11, curl_phi_j)}; } diff --git a/src/serac/numerics/functional/detail/hexahedron_L2.inl b/src/serac/numerics/functional/detail/hexahedron_L2.inl index 4a75d77b32..9498191817 100644 --- a/src/serac/numerics/functional/detail/hexahedron_L2.inl +++ b/src/serac/numerics/functional/detail/hexahedron_L2.inl @@ -157,10 +157,10 @@ struct finite_element > { B(qx, jx) * B(qy, jy) * G(qz, jz)}; int Q = (qz * q + qy) * q + qx; - auto& d00 = get<0>(get<0>(input(Q))); - auto& d01 = get<1>(get<0>(input(Q))); - auto& d10 = get<0>(get<1>(input(Q))); - auto& d11 = get<1>(get<1>(input(Q))); + const auto& d00 = get<0>(get<0>(input(Q))); + const auto& d01 = get<1>(get<0>(input(Q))); + const auto& d10 = get<0>(get<1>(input(Q))); + const auto& d11 = get<1>(get<1>(input(Q))); output[Q] = {d00 * phi_j + dot(d01, dphi_j_dxi), d10 * phi_j + dot(d11, dphi_j_dxi)}; } diff --git a/src/serac/numerics/functional/detail/quadrilateral_H1.inl b/src/serac/numerics/functional/detail/quadrilateral_H1.inl index 9e58f18215..30d92eb08c 100644 --- a/src/serac/numerics/functional/detail/quadrilateral_H1.inl +++ b/src/serac/numerics/functional/detail/quadrilateral_H1.inl @@ -176,10 +176,10 @@ struct finite_element > { tensor dphi_j_dxi = {G(qx, jx) * B(qy, jy), B(qx, jx) * G(qy, jy)}; int Q = qy * q + qx; - auto& d00 = get<0>(get<0>(input(Q))); - auto& d01 = get<1>(get<0>(input(Q))); - auto& d10 = get<0>(get<1>(input(Q))); - auto& d11 = get<1>(get<1>(input(Q))); + const auto& d00 = get<0>(get<0>(input(Q))); + const auto& d01 = get<1>(get<0>(input(Q))); + const auto& d10 = get<0>(get<1>(input(Q))); + const auto& d11 = get<1>(get<1>(input(Q))); output[Q] = {d00 * phi_j + dot(d01, dphi_j_dxi), d10 * phi_j + dot(d11, dphi_j_dxi)}; } diff --git a/src/serac/numerics/functional/detail/quadrilateral_Hcurl.inl b/src/serac/numerics/functional/detail/quadrilateral_Hcurl.inl index fb28c55784..054aa82efb 100644 --- a/src/serac/numerics/functional/detail/quadrilateral_Hcurl.inl +++ b/src/serac/numerics/functional/detail/quadrilateral_Hcurl.inl @@ -273,10 +273,10 @@ struct finite_element > { double curl_phi_j = (dir == 0) * -B1(qx, jx) * G2(qy, jy) + (dir == 1) * B1(qy, jy) * G2(qx, jx); int Q = qy * q + qx; - auto& d00 = get<0>(get<0>(input(Q))); - auto& d01 = get<1>(get<0>(input(Q))); - auto& d10 = get<0>(get<1>(input(Q))); - auto& d11 = get<1>(get<1>(input(Q))); + const auto& d00 = get<0>(get<0>(input(Q))); + const auto& d01 = get<1>(get<0>(input(Q))); + const auto& d10 = get<0>(get<1>(input(Q))); + const auto& d11 = get<1>(get<1>(input(Q))); output[Q] = {dot(d00, phi_j) + d01 * curl_phi_j, dot(d10, phi_j) + d11 * curl_phi_j}; } diff --git a/src/serac/numerics/functional/detail/segment_H1.inl b/src/serac/numerics/functional/detail/segment_H1.inl index e9a41a28da..c21d6ad56b 100644 --- a/src/serac/numerics/functional/detail/segment_H1.inl +++ b/src/serac/numerics/functional/detail/segment_H1.inl @@ -110,10 +110,10 @@ struct finite_element > { double phi_j = B(qx, jx); double dphi_j_dxi = G(qx, jx); - auto& d00 = get<0>(get<0>(input(qx))); - auto& d01 = get<1>(get<0>(input(qx))); - auto& d10 = get<0>(get<1>(input(qx))); - auto& d11 = get<1>(get<1>(input(qx))); + const auto& d00 = get<0>(get<0>(input(qx))); + const auto& d01 = get<1>(get<0>(input(qx))); + const auto& d10 = get<0>(get<1>(input(qx))); + const auto& d11 = get<1>(get<1>(input(qx))); output[qx] = {d00 * phi_j + d01 * dphi_j_dxi, d10 * phi_j + d11 * dphi_j_dxi}; } diff --git a/src/serac/numerics/functional/detail/segment_L2.inl b/src/serac/numerics/functional/detail/segment_L2.inl index e15451ae2e..0703318c62 100644 --- a/src/serac/numerics/functional/detail/segment_L2.inl +++ b/src/serac/numerics/functional/detail/segment_L2.inl @@ -139,10 +139,10 @@ struct finite_element > { double phi0_j = B(qx, j) * (s == 0); double phi1_j = B(qx, j) * (s == 1); - auto& d00 = get<0>(get<0>(input(qx))); - auto& d01 = get<1>(get<0>(input(qx))); - auto& d10 = get<0>(get<1>(input(qx))); - auto& d11 = get<1>(get<1>(input(qx))); + const auto& d00 = get<0>(get<0>(input(qx))); + const auto& d01 = get<1>(get<0>(input(qx))); + const auto& d10 = get<0>(get<1>(input(qx))); + const auto& d11 = get<1>(get<1>(input(qx))); output[qx] = {d00 * phi0_j + d01 * phi1_j, d10 * phi0_j + d11 * phi1_j}; } diff --git a/src/serac/numerics/functional/detail/tetrahedron_H1.inl b/src/serac/numerics/functional/detail/tetrahedron_H1.inl index 2a7613cee9..74c97b3d6c 100644 --- a/src/serac/numerics/functional/detail/tetrahedron_H1.inl +++ b/src/serac/numerics/functional/detail/tetrahedron_H1.inl @@ -349,10 +349,10 @@ struct finite_element > { double phi_j = shape_function(xi[i], j); tensor dphi_j_dxi = shape_function_gradient(xi[i], j); - auto& d00 = get<0>(get<0>(input(i))); - auto& d01 = get<1>(get<0>(input(i))); - auto& d10 = get<0>(get<1>(input(i))); - auto& d11 = get<1>(get<1>(input(i))); + const auto& d00 = get<0>(get<0>(input(i))); + const auto& d01 = get<1>(get<0>(input(i))); + const auto& d10 = get<0>(get<1>(input(i))); + const auto& d11 = get<1>(get<1>(input(i))); output[i] = {d00 * phi_j + dot(d01, dphi_j_dxi), d10 * phi_j + dot(d11, dphi_j_dxi)}; } diff --git a/src/serac/numerics/functional/detail/tetrahedron_L2.inl b/src/serac/numerics/functional/detail/tetrahedron_L2.inl index 25bc8ffd36..1434e77d6f 100644 --- a/src/serac/numerics/functional/detail/tetrahedron_L2.inl +++ b/src/serac/numerics/functional/detail/tetrahedron_L2.inl @@ -354,10 +354,10 @@ struct finite_element > { double phi_j = shape_function(xi[i], j); tensor dphi_j_dxi = shape_function_gradient(xi[i], j); - auto& d00 = get<0>(get<0>(input(i))); - auto& d01 = get<1>(get<0>(input(i))); - auto& d10 = get<0>(get<1>(input(i))); - auto& d11 = get<1>(get<1>(input(i))); + const auto& d00 = get<0>(get<0>(input(i))); + const auto& d01 = get<1>(get<0>(input(i))); + const auto& d10 = get<0>(get<1>(input(i))); + const auto& d11 = get<1>(get<1>(input(i))); output[i] = {d00 * phi_j + dot(d01, dphi_j_dxi), d10 * phi_j + dot(d11, dphi_j_dxi)}; } diff --git a/src/serac/numerics/functional/detail/triangle_H1.inl b/src/serac/numerics/functional/detail/triangle_H1.inl index 0e63afc533..37990ffcfe 100644 --- a/src/serac/numerics/functional/detail/triangle_H1.inl +++ b/src/serac/numerics/functional/detail/triangle_H1.inl @@ -255,10 +255,10 @@ struct finite_element > { double phi_j = shape_function(xi[i], j); tensor dphi_j_dxi = shape_function_gradient(xi[i], j); - auto& d00 = get<0>(get<0>(input(i))); - auto& d01 = get<1>(get<0>(input(i))); - auto& d10 = get<0>(get<1>(input(i))); - auto& d11 = get<1>(get<1>(input(i))); + const auto& d00 = get<0>(get<0>(input(i))); + const auto& d01 = get<1>(get<0>(input(i))); + const auto& d10 = get<0>(get<1>(input(i))); + const auto& d11 = get<1>(get<1>(input(i))); output[i] = {d00 * phi_j + dot(d01, dphi_j_dxi), d10 * phi_j + dot(d11, dphi_j_dxi)}; } diff --git a/src/serac/numerics/functional/detail/triangle_L2.inl b/src/serac/numerics/functional/detail/triangle_L2.inl index ee0c549f10..2215bd2db2 100644 --- a/src/serac/numerics/functional/detail/triangle_L2.inl +++ b/src/serac/numerics/functional/detail/triangle_L2.inl @@ -264,10 +264,10 @@ struct finite_element > { double phi_j = shape_function(xi[i], j); tensor dphi_j_dxi = shape_function_gradient(xi[i], j); - auto& d00 = get<0>(get<0>(input(i))); - auto& d01 = get<1>(get<0>(input(i))); - auto& d10 = get<0>(get<1>(input(i))); - auto& d11 = get<1>(get<1>(input(i))); + const auto& d00 = get<0>(get<0>(input(i))); + const auto& d01 = get<1>(get<0>(input(i))); + const auto& d10 = get<0>(get<1>(input(i))); + const auto& d11 = get<1>(get<1>(input(i))); output[i] = {d00 * phi_j + dot(d01, dphi_j_dxi), d10 * phi_j + dot(d11, dphi_j_dxi)}; } From 9e43f72aa116de577fb3a2d6433539c43446640f Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Tue, 19 Nov 2024 11:35:32 -0800 Subject: [PATCH 37/92] more guarding against accesses to unnecessary restriction operators --- src/serac/numerics/functional/functional.hpp | 79 ++++++++++--------- .../functional/tests/check_gradient.hpp | 18 +++-- .../functional/tests/functional_basic_dg.cpp | 54 +++++++++++++ 3 files changed, 108 insertions(+), 43 deletions(-) diff --git a/src/serac/numerics/functional/functional.hpp b/src/serac/numerics/functional/functional.hpp index bb3340d838..0c0bad3e61 100644 --- a/src/serac/numerics/functional/functional.hpp +++ b/src/serac/numerics/functional/functional.hpp @@ -568,36 +568,38 @@ class Functional { std::set< row_col > nonzero_entries; for (auto& integral : form_.integrals_) { - Domain & dom = integral.domain_; - const auto& G_test = dom.get_restriction(form_.test_function_space_); - const auto& G_trial = dom.get_restriction(form_.trial_function_spaces_[which_argument]); - for (const auto& [geom, test_restriction] : G_test.restrictions) { - const auto& trial_restriction = G_trial.restrictions.at(geom); - - // the degrees of freedom associated with the rows/columns of the e^th element stiffness matrix - std::vector test_vdofs(test_restriction.nodes_per_elem * test_restriction.components); - std::vector trial_vdofs(trial_restriction.nodes_per_elem * trial_restriction.components); - - auto num_elements = static_cast(test_restriction.num_elements); - for (uint32_t e = 0; e < num_elements; e++) { - - for (uint32_t i = 0; i < test_restriction.nodes_per_elem; i++) { - auto test_dof = test_restriction.dof_info(e, i); - for (uint32_t j = 0; j < test_restriction.components; j++) { - test_vdofs[i * test_restriction.components + j] = int(test_restriction.GetVDof(test_dof, j).index()); + if (integral.DependsOn(which_argument)) { + Domain & dom = integral.domain_; + const auto& G_test = dom.get_restriction(form_.test_function_space_); + const auto& G_trial = dom.get_restriction(form_.trial_function_spaces_[which_argument]); + for (const auto& [geom, test_restriction] : G_test.restrictions) { + const auto& trial_restriction = G_trial.restrictions.at(geom); + + // the degrees of freedom associated with the rows/columns of the e^th element stiffness matrix + std::vector test_vdofs(test_restriction.nodes_per_elem * test_restriction.components); + std::vector trial_vdofs(trial_restriction.nodes_per_elem * trial_restriction.components); + + auto num_elements = static_cast(test_restriction.num_elements); + for (uint32_t e = 0; e < num_elements; e++) { + + for (uint32_t i = 0; i < test_restriction.nodes_per_elem; i++) { + auto test_dof = test_restriction.dof_info(e, i); + for (uint32_t j = 0; j < test_restriction.components; j++) { + test_vdofs[i * test_restriction.components + j] = int(test_restriction.GetVDof(test_dof, j).index()); + } } - } - for (uint32_t i = 0; i < trial_restriction.nodes_per_elem; i++) { - auto trial_dof = trial_restriction.dof_info(e, i); - for (uint32_t j = 0; j < trial_restriction.components; j++) { - trial_vdofs[i * trial_restriction.components + j] = int(trial_restriction.GetVDof(trial_dof, j).index()); + for (uint32_t i = 0; i < trial_restriction.nodes_per_elem; i++) { + auto trial_dof = trial_restriction.dof_info(e, i); + for (uint32_t j = 0; j < trial_restriction.components; j++) { + trial_vdofs[i * trial_restriction.components + j] = int(trial_restriction.GetVDof(trial_dof, j).index()); + } } - } - for (int row : test_vdofs) { - for (int col : trial_vdofs) { - nonzero_entries.insert({row, col}); + for (int row : test_vdofs) { + for (int col : trial_vdofs) { + nonzero_entries.insert({row, col}); + } } } } @@ -624,16 +626,18 @@ class Functional { uint64_t max_buffer_size() { uint64_t max_entries = 0; for (auto & integral : form_.integrals_) { - Domain & dom = integral.domain_; - const auto& G_test = dom.get_restriction(form_.test_function_space_); - const auto& G_trial = dom.get_restriction(form_.trial_function_spaces_[which_argument]); - for (const auto& [geom, test_restriction] : G_test.restrictions) { - const auto& trial_restriction = G_trial.restrictions.at(geom); - uint64_t nrows_per_element = test_restriction.nodes_per_elem * test_restriction.components; - uint64_t ncols_per_element = trial_restriction.nodes_per_elem * trial_restriction.components; - uint64_t entries_per_element = nrows_per_element * ncols_per_element; - uint64_t entries_needed = test_restriction.num_elements * entries_per_element; - max_entries = std::max(entries_needed, max_entries); + if (integral.DependsOn(which_argument)) { + Domain & dom = integral.domain_; + const auto& G_test = dom.get_restriction(form_.test_function_space_); + const auto& G_trial = dom.get_restriction(form_.trial_function_spaces_[which_argument]); + for (const auto& [geom, test_restriction] : G_test.restrictions) { + const auto& trial_restriction = G_trial.restrictions.at(geom); + uint64_t nrows_per_element = test_restriction.nodes_per_elem * test_restriction.components; + uint64_t ncols_per_element = trial_restriction.nodes_per_elem * trial_restriction.components; + uint64_t entries_per_element = nrows_per_element * ncols_per_element; + uint64_t entries_needed = test_restriction.num_elements * entries_per_element; + max_entries = std::max(entries_needed, max_entries); + } } } return max_entries; @@ -672,10 +676,9 @@ class Functional { for (auto & integral : form_.integrals_) { - Domain & dom = integral.domain_; - // if this integral's derivative isn't identically zero if (integral.functional_to_integral_index_.count(which_argument) > 0) { + Domain & dom = integral.domain_; uint32_t id = integral.functional_to_integral_index_.at(which_argument); const auto& G_test = dom.get_restriction(form_.test_function_space_); diff --git a/src/serac/numerics/functional/tests/check_gradient.hpp b/src/serac/numerics/functional/tests/check_gradient.hpp index ba720729fd..919db0363b 100644 --- a/src/serac/numerics/functional/tests/check_gradient.hpp +++ b/src/serac/numerics/functional/tests/check_gradient.hpp @@ -136,7 +136,7 @@ void check_gradient(serac::Functional& f, double t, const mfem::Vector& U, co mfem::Vector dU(U.Size()); dU.Randomize(seed); - mfem::Vector ddU_dt(U.Size()); + mfem::Vector ddU_dt(dU_dt.Size()); ddU_dt.Randomize(seed + 1); { @@ -149,9 +149,12 @@ void check_gradient(serac::Functional& f, double t, const mfem::Vector& U, co mfem::Vector df_jvp2(df_jvp1.Size()); dfdU_matrix->Mult(dU, df_jvp2); // sparse matvec - if (df_jvp1.Norml2() != 0) { + if (df_jvp1.Norml2() < 1.0e-13) { + double absolute_error = df_jvp1.DistanceTo(df_jvp2.GetData()); + EXPECT_NEAR(0., absolute_error, 5.e-14); + } else { double relative_error = df_jvp1.DistanceTo(df_jvp2.GetData()) / df_jvp1.Norml2(); - EXPECT_NEAR(0., relative_error, 5.e-6); + EXPECT_NEAR(0., relative_error, 5.e-14); } // {f(x - 2 * h), f(x - h), f(x), f(x + h), f(x + 2 * h)} @@ -209,8 +212,13 @@ void check_gradient(serac::Functional& f, double t, const mfem::Vector& U, co mfem::Vector df_jvp2(df_jvp1.Size()); df_ddU_dt_matrix->Mult(ddU_dt, df_jvp2); // sparse matvec - double relative_error = df_jvp1.DistanceTo(df_jvp2.GetData()) / df_jvp1.Norml2(); - EXPECT_NEAR(0., relative_error, 5.e-14); + if (df_jvp1.Norml2() < 1.0e-13) { + double absolute_error = df_jvp1.DistanceTo(df_jvp2.GetData()); + EXPECT_NEAR(0., absolute_error, 5.e-14); + } else { + double relative_error = df_jvp1.DistanceTo(df_jvp2.GetData()) / df_jvp1.Norml2(); + EXPECT_NEAR(0., relative_error, 5.e-14); + } // {f(x - 2 * h), f(x - h), f(x), f(x + h), f(x + 2 * h)} mfem::Vector f_values[5]; diff --git a/src/serac/numerics/functional/tests/functional_basic_dg.cpp b/src/serac/numerics/functional/tests/functional_basic_dg.cpp index 76942dad99..39c60f1b1d 100644 --- a/src/serac/numerics/functional/tests/functional_basic_dg.cpp +++ b/src/serac/numerics/functional/tests/functional_basic_dg.cpp @@ -132,6 +132,60 @@ TEST(basic, L2_qoi_test_tets_quadratic) { L2_qoi_test<3, 2>(SERAC_REPO_DIR "/dat TEST(basic, L2_qoi_test_hexes_linear) { L2_qoi_test<3, 1>(SERAC_REPO_DIR "/data/meshes/patch3D_hexes.mesh"); } TEST(basic, L2_qoi_test_hexes_quadratic) { L2_qoi_test<3, 2>(SERAC_REPO_DIR "/data/meshes/patch3D_hexes.mesh"); } +template +void L2_scalar_valued_test(std::string meshfile) +{ + using test_space = L2

; + using trial_space_0 = L2

; + using trial_space_1 = H1; + + auto mesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 0); + + auto L2fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); + mfem::ParFiniteElementSpace fespace_0(mesh.get(), &L2fec, 1, serac::ordering); + + auto H1fec = mfem::H1_FECollection(p, dim); + mfem::ParFiniteElementSpace fespace_1(mesh.get(), &H1fec, dim, serac::ordering); + + mfem::Vector U0(fespace_0.TrueVSize()); + U0.Randomize(); + + mfem::Vector U1(fespace_1.TrueVSize()); + U1.Randomize(); + + // Construct the new functional object using the specified test and trial spaces + Functional residual(&fespace_0, {&fespace_0, &fespace_1}); + + constexpr int DERIVATIVE = 1; + + Domain interior_faces = InteriorFaces(*mesh); + + residual.AddInteriorFaceIntegral( + Dimension{}, DependsOn<0>{}, + [=](double /*t*/, auto X, auto velocity) { + // compute the surface normal + auto dX_dxi = get(X); + [[maybe_unused]] auto n = normalize(cross(dX_dxi)); + + // extract the velocity values from each side of the interface + // note: the orientation convention is such that the normal + // computed as above will point from from side 1->2 + auto [u_1, u_2] = velocity; + + auto a = u_2 - u_1; + + auto f_1 = u_1 * a; + auto f_2 = u_2 * a; + return serac::tuple{f_1, f_2}; + }, interior_faces); + + double t = 0.0; + check_gradient(residual, t, U0, U1); + +} + +TEST(basic, L2_scalar_test_tris_and_quads_linear) { L2_scalar_valued_test<2, 1>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); } + int main(int argc, char* argv[]) { int num_procs, myid; From b37b454471cfa671438fcdf365c4e51ff0d9728c Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Tue, 19 Nov 2024 11:38:31 -0800 Subject: [PATCH 38/92] fix interpolate routines returning degenerate tensors, rather than `double`, add some related tests --- .../functional/detail/quadrilateral_L2.inl | 18 +++++++++++++----- .../numerics/functional/detail/segment_L2.inl | 14 +++++++++++--- .../numerics/functional/detail/triangle_L2.inl | 11 ++++++++--- .../functional/tests/functional_basic_dg.cpp | 4 +++- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/serac/numerics/functional/detail/quadrilateral_L2.inl b/src/serac/numerics/functional/detail/quadrilateral_L2.inl index 824a431b78..a6c35a942d 100644 --- a/src/serac/numerics/functional/detail/quadrilateral_L2.inl +++ b/src/serac/numerics/functional/detail/quadrilateral_L2.inl @@ -284,7 +284,7 @@ struct finite_element > { static constexpr bool apply_weights = false; static constexpr auto B = calculate_B(); - tensor< tuple< tensor, tensor >, q * q> output; + tensor< tuple< value_type, value_type >, q * q> output; tensor value{}; @@ -296,8 +296,12 @@ struct finite_element > { for (int qy = 0; qy < q; qy++) { for (int qx = 0; qx < q; qx++) { - for (int i = 0; i < c; i++) { - get<0>(output[qy * q + qx])[i] = value(i, qy, qx); + if constexpr (c == 1) { + get<0>(output[qy * q + qx]) = value(0, qy, qx); + } else { + for (int i = 0; i < c; i++) { + get<0>(output[qy * q + qx])[i] = value(i, qy, qx); + } } } } @@ -310,8 +314,12 @@ struct finite_element > { for (int qy = 0; qy < q; qy++) { for (int qx = 0; qx < q; qx++) { - for (int i = 0; i < c; i++) { - get<1>(output[qy * q + qx])[i] = value(i, qy, qx); + if constexpr (c == 1) { + get<1>(output[qy * q + qx]) = value(0, qy, qx); + } else { + for (int i = 0; i < c; i++) { + get<1>(output[qy * q + qx])[i] = value(i, qy, qx); + } } } } diff --git a/src/serac/numerics/functional/detail/segment_L2.inl b/src/serac/numerics/functional/detail/segment_L2.inl index 0703318c62..9fd801710c 100644 --- a/src/serac/numerics/functional/detail/segment_L2.inl +++ b/src/serac/numerics/functional/detail/segment_L2.inl @@ -158,18 +158,26 @@ struct finite_element > { tensor values{}; - tensor< tuple< tensor, tensor >, q> output; + tensor< tuple< value_type, value_type >, q > output{}; // apply the shape functions for (int i = 0; i < c; i++) { values = dot(X[i][0], BT); for (int qx = 0; qx < q; qx++) { - get<0>(output[qx])[i] = values[qx]; + if constexpr (c == 1) { + get<0>(output[qx]) = values[qx]; + } else { + get<0>(output[qx])[i] = values[qx]; + } } values = dot(X[i][1], BT); for (int qx = 0; qx < q; qx++) { - get<1>(output[qx])[i] = values[qx]; + if constexpr (c == 1) { + get<1>(output[qx]) = values[qx]; + } else { + get<1>(output[qx])[i] = values[qx]; + } } } diff --git a/src/serac/numerics/functional/detail/triangle_L2.inl b/src/serac/numerics/functional/detail/triangle_L2.inl index 2215bd2db2..bd9f806efb 100644 --- a/src/serac/numerics/functional/detail/triangle_L2.inl +++ b/src/serac/numerics/functional/detail/triangle_L2.inl @@ -335,14 +335,19 @@ struct finite_element > { constexpr auto xi = GaussLegendreNodes(); static constexpr int num_quadrature_points = q * (q + 1) / 2; - tensor< tuple< tensor, tensor >, num_quadrature_points > output{}; + tensor< tuple< value_type, value_type >, num_quadrature_points > output{}; // apply the shape functions for (int i = 0; i < c; i++) { for (int j = 0; j < num_quadrature_points; j++) { for (int k = 0; k < ndof; k++) { - get<0>(output[j])[i] += X[i][0][k] * shape_function(xi[j], k); - get<1>(output[j])[i] += X[i][1][k] * shape_function(xi[j], k); + if constexpr (c == 1) { + get<0>(output[j]) += X[i][0][k] * shape_function(xi[j], k); + get<1>(output[j]) += X[i][1][k] * shape_function(xi[j], k); + } else { + get<0>(output[j])[i] += X[i][0][k] * shape_function(xi[j], k); + get<1>(output[j])[i] += X[i][1][k] * shape_function(xi[j], k); + } } } } diff --git a/src/serac/numerics/functional/tests/functional_basic_dg.cpp b/src/serac/numerics/functional/tests/functional_basic_dg.cpp index 39c60f1b1d..1708b61e19 100644 --- a/src/serac/numerics/functional/tests/functional_basic_dg.cpp +++ b/src/serac/numerics/functional/tests/functional_basic_dg.cpp @@ -184,7 +184,9 @@ void L2_scalar_valued_test(std::string meshfile) } -TEST(basic, L2_scalar_test_tris_and_quads_linear) { L2_scalar_valued_test<2, 1>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); } +TEST(basic, L2_mixed_scalar_test_tris_and_quads_linear) { L2_scalar_valued_test<2, 1>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); } + +TEST(basic, L2_mixed_scalar_test_tets_and_hexes_linear) { L2_scalar_valued_test<3, 1>(SERAC_REPO_DIR "/data/meshes/patch3D_tets_and_hexes.mesh"); } int main(int argc, char* argv[]) { From a6360548fb27a2b63981c948b5ff8824b72798db Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Tue, 19 Nov 2024 12:27:50 -0800 Subject: [PATCH 39/92] fix mistakenly calling a function that wasn't defined --- .../interior_face_integral_kernels.hpp | 9 ++++-- .../functional/tests/functional_basic_dg.cpp | 30 +++++++++++-------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/serac/numerics/functional/interior_face_integral_kernels.hpp b/src/serac/numerics/functional/interior_face_integral_kernels.hpp index 0233082099..3a4875dfa5 100644 --- a/src/serac/numerics/functional/interior_face_integral_kernels.hpp +++ b/src/serac/numerics/functional/interior_face_integral_kernels.hpp @@ -322,8 +322,13 @@ void element_gradient_kernel([[maybe_unused]] ExecArrayView residual(&fespace_0, {&fespace_0, &fespace_1}); + constexpr int VALUE = 0; constexpr int DERIVATIVE = 1; Domain interior_faces = InteriorFaces(*mesh); residual.AddInteriorFaceIntegral( - Dimension{}, DependsOn<0>{}, - [=](double /*t*/, auto X, auto velocity) { - // compute the surface normal - auto dX_dxi = get(X); - [[maybe_unused]] auto n = normalize(cross(dX_dxi)); + Dimension{}, DependsOn<0, 1>{}, + [=](double /*t*/, auto X, auto rho, auto u) { - // extract the velocity values from each side of the interface - // note: the orientation convention is such that the normal - // computed as above will point from from side 1->2 - auto [u_1, u_2] = velocity; + // area in reference configuration + auto dA = norm(cross(get(X))); - auto a = u_2 - u_1; + // area-weighted surface normal in current configuration + // n = \hat{n} * da + auto n = cross(get(X) + get(u)); + + auto [rho0, rho1] = rho; + auto uTn = dot(get(u), n); + auto s = uTn > 0; + + return serac::tuple{ + uTn * (( s) * rho0 + (1.0 - s) * rho1), + uTn * ((1.0 - s) * rho0 + ( s) * rho1) + } / dA; - auto f_1 = u_1 * a; - auto f_2 = u_2 * a; - return serac::tuple{f_1, f_2}; }, interior_faces); double t = 0.0; From a20e7b9cf3339dec32c75b589fc4e255691b7391 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Tue, 19 Nov 2024 12:38:54 -0800 Subject: [PATCH 40/92] simplify qfunction in dg test --- .../numerics/functional/tests/functional_basic_dg.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/serac/numerics/functional/tests/functional_basic_dg.cpp b/src/serac/numerics/functional/tests/functional_basic_dg.cpp index 73cb0dd3f7..80ae66a763 100644 --- a/src/serac/numerics/functional/tests/functional_basic_dg.cpp +++ b/src/serac/numerics/functional/tests/functional_basic_dg.cpp @@ -165,12 +165,7 @@ void L2_scalar_valued_test(std::string meshfile) Dimension{}, DependsOn<0, 1>{}, [=](double /*t*/, auto X, auto rho, auto u) { - // area in reference configuration - auto dA = norm(cross(get(X))); - - // area-weighted surface normal in current configuration - // n = \hat{n} * da - auto n = cross(get(X) + get(u)); + auto n = normalize(cross(get(X))); auto [rho0, rho1] = rho; auto uTn = dot(get(u), n); @@ -179,7 +174,7 @@ void L2_scalar_valued_test(std::string meshfile) return serac::tuple{ uTn * (( s) * rho0 + (1.0 - s) * rho1), uTn * ((1.0 - s) * rho0 + ( s) * rho1) - } / dA; + }; }, interior_faces); From 87fa6a813144a3eaad8c00d5bd42f8476f5f5267 Mon Sep 17 00:00:00 2001 From: Brandon Talamini Date: Tue, 19 Nov 2024 13:03:34 -0800 Subject: [PATCH 41/92] Fix implementations of Enitre{Domain,Boundary} by removing repeated code Fixes a bug where not all members of the class were being constructed, leaving the object in an invalid state. In particular, the mfem id vectors (like mfem_edge_ids_) were not populated, making it impossible to get lists of dofs on the domain. --- src/serac/numerics/functional/domain.cpp | 79 ++++++------------------ 1 file changed, 20 insertions(+), 59 deletions(-) diff --git a/src/serac/numerics/functional/domain.cpp b/src/serac/numerics/functional/domain.cpp index 3d39e2d0a6..01e648405b 100644 --- a/src/serac/numerics/functional/domain.cpp +++ b/src/serac/numerics/functional/domain.cpp @@ -460,71 +460,32 @@ mfem::Array Domain::dof_list(mfem::FiniteElementSpace* fes) const Domain EntireDomain(const mfem::Mesh& mesh) { - Domain output{mesh, mesh.SpaceDimension() /* elems can be 2 or 3 dimensional */}; - - int tri_id = 0; - int quad_id = 0; - int tet_id = 0; - int hex_id = 0; - - // faces that satisfy the predicate are added to the domain - int num_elems = mesh.GetNE(); - for (int i = 0; i < num_elems; i++) { - auto geom = mesh.GetElementGeometry(i); - - switch (geom) { - case mfem::Geometry::TRIANGLE: - output.tri_ids_.push_back(tri_id++); - break; - case mfem::Geometry::SQUARE: - output.quad_ids_.push_back(quad_id++); - break; - case mfem::Geometry::TETRAHEDRON: - output.tet_ids_.push_back(tet_id++); - break; - case mfem::Geometry::CUBE: - output.hex_ids_.push_back(hex_id++); - break; - default: - SLIC_ERROR("unsupported element type"); - break; - } + switch (mesh.SpaceDimension()) { + case 2: + return Domain::ofElements(mesh, [](std::vector, int) { return true; }); + break; + case 3: + return Domain::ofElements(mesh, [](std::vector, int) { return true; }); + break; + default: + SLIC_ERROR("In valid spatial dimension. Domains may only be created on 2D or 3D meshes."); + exit(-1); } - - return output; } Domain EntireBoundary(const mfem::Mesh& mesh) { - Domain output{mesh, mesh.SpaceDimension() - 1, Domain::Type::BoundaryElements}; - - int edge_id = 0; - int tri_id = 0; - int quad_id = 0; - - for (int f = 0; f < mesh.GetNumFaces(); f++) { - // discard faces with the wrong type - if (mesh.GetFaceInformation(f).IsInterior()) continue; - - auto geom = mesh.GetFaceGeometry(f); - - switch (geom) { - case mfem::Geometry::SEGMENT: - output.edge_ids_.push_back(edge_id++); - break; - case mfem::Geometry::TRIANGLE: - output.tri_ids_.push_back(tri_id++); - break; - case mfem::Geometry::SQUARE: - output.quad_ids_.push_back(quad_id++); - break; - default: - SLIC_ERROR("unsupported element type"); - break; - } + switch (mesh.SpaceDimension()) { + case 2: + return Domain::ofBoundaryElements(mesh, [](std::vector, int) { return true; }); + break; + case 3: + return Domain::ofBoundaryElements(mesh, [](std::vector, int) { return true; }); + break; + default: + SLIC_ERROR("In valid spatial dimension. Domains may only be created on 2D or 3D meshes."); + exit(-1); } - - return output; } /// @cond From af09e08bcf0b195ce2268cb4411858f8bec88afc Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Tue, 19 Nov 2024 14:50:27 -0800 Subject: [PATCH 42/92] update more solid mechanics tests --- .../physics/tests/dynamic_thermal_adjoint.cpp | 37 ++++++++++++------- src/serac/physics/tests/solid.cpp | 26 +++++++------ .../physics/tests/solid_dynamics_patch.cpp | 23 ++++++++---- 3 files changed, 53 insertions(+), 33 deletions(-) diff --git a/src/serac/physics/tests/dynamic_thermal_adjoint.cpp b/src/serac/physics/tests/dynamic_thermal_adjoint.cpp index 8b5f833ffe..e23c85591f 100644 --- a/src/serac/physics/tests/dynamic_thermal_adjoint.cpp +++ b/src/serac/physics/tests/dynamic_thermal_adjoint.cpp @@ -52,16 +52,18 @@ static int iter = 0; std::unique_ptr> createNonlinearHeatTransfer( axom::sidre::DataStore& /*data_store*/, const NonlinearSolverOptions& nonlinear_opts, const TimesteppingOptions& dyn_opts, - const heat_transfer::IsotropicConductorWithLinearConductivityVsTemperature& mat) + const heat_transfer::IsotropicConductorWithLinearConductivityVsTemperature& mat, + Domain & whole_domain) { // Note that we are testing the non-default checkpoint to disk capability here auto thermal = std::make_unique>(nonlinear_opts, heat_transfer::direct_linear_options, dyn_opts, thermal_prefix + std::to_string(iter++), mesh_tag, std::vector{}, 0, 0.0); - thermal->setMaterial(mat); + thermal->setMaterial(mat, whole_domain); + thermal->setSource([](auto /* X */, auto /* time */, auto /* u */, auto /* du_dx */) { return 1.0; }, whole_domain); + thermal->setTemperature([](const mfem::Vector&, double) { return 0.0; }); thermal->setTemperatureBCs({1}, [](const mfem::Vector&, double) { return 0.0; }); - thermal->setSource([](auto /* X */, auto /* time */, auto /* u */, auto /* du_dx */) { return 1.0; }); thermal->completeSetup(); return thermal; } @@ -70,7 +72,7 @@ using ParametrizedHeatTransferT = HeatTransfer>, std::i std::unique_ptr createParameterizedHeatTransfer( axom::sidre::DataStore& /*data_store*/, const NonlinearSolverOptions& nonlinear_opts, - const TimesteppingOptions& dyn_opts, const heat_transfer::ParameterizedLinearIsotropicConductor& mat) + const TimesteppingOptions& dyn_opts, const heat_transfer::ParameterizedLinearIsotropicConductor& mat, Domain & whole_domain) { std::vector names{"conductivity"}; @@ -81,11 +83,11 @@ std::unique_ptr createParameterizedHeatTransfer( FiniteElementState user_defined_conductivity(StateManager::mesh(mesh_tag), H1

{}, "user_defined_conductivity"); user_defined_conductivity = 1.1; thermal->setParameter(0, user_defined_conductivity); - thermal->setMaterial(DependsOn<0>{}, mat); + thermal->setMaterial(DependsOn<0>{}, mat, whole_domain); + thermal->setSource([](auto /* X */, auto /* time */, auto /* u */, auto /* du_dx */) { return 1.0; }, + whole_domain); thermal->setTemperature([](const mfem::Vector&, double) { return 0.0; }); thermal->setTemperatureBCs({1}, [](const mfem::Vector&, double) { return 0.0; }); - thermal->setSource([](auto /* X */, auto /* time */, auto /* u */, auto /* du_dx */) { return 1.0; }, - EntireDomain(StateManager::mesh(mesh_tag))); thermal->completeSetup(); return thermal; } @@ -93,7 +95,8 @@ std::unique_ptr createParameterizedHeatTransfer( std::unique_ptr createParameterizedNonlinearHeatTransfer( axom::sidre::DataStore& /*data_store*/, const NonlinearSolverOptions& nonlinear_opts, const TimesteppingOptions& dyn_opts, - const heat_transfer::ParameterizedIsotropicConductorWithLinearConductivityVsTemperature& mat) + const heat_transfer::ParameterizedIsotropicConductorWithLinearConductivityVsTemperature& mat, + Domain & whole_domain) { std::vector names{"conductivity"}; @@ -104,10 +107,12 @@ std::unique_ptr createParameterizedNonlinearHeatTrans FiniteElementState user_defined_conductivity(StateManager::mesh(mesh_tag), H1

{}, "user_defined_conductivity"); user_defined_conductivity = 1.1; thermal->setParameter(0, user_defined_conductivity); - thermal->setMaterial(DependsOn<0>{}, mat); + + thermal->setMaterial(DependsOn<0>{}, mat, whole_domain); + thermal->setSource([](auto /* X */, auto /* time */, auto /* u */, auto /* du_dx */) { return 1.0; }, whole_domain); + thermal->setTemperature([](const mfem::Vector&, double) { return 0.0; }); thermal->setTemperatureBCs({1}, [](const mfem::Vector&, double) { return 0.0; }); - thermal->setSource([](auto /* X */, auto /* time */, auto /* u */, auto /* du_dx */) { return 1.0; }); thermal->completeSetup(); return thermal; } @@ -261,7 +266,8 @@ struct HeatTransferSensitivityFixture : public ::testing::Test { TEST_F(HeatTransferSensitivityFixture, InitialTemperatureSensitivities) { - auto thermal_solver = createNonlinearHeatTransfer(data_store, nonlinear_opts, dyn_opts, nonlinearMat); + Domain whole_domain = EntireDomain(*mesh); + auto thermal_solver = createNonlinearHeatTransfer(data_store, nonlinear_opts, dyn_opts, nonlinearMat, whole_domain); auto [qoi_base, temperature_sensitivity, _] = computeThermalQoiAndInitialTemperatureAndShapeSensitivity(*thermal_solver, tsInfo); @@ -279,7 +285,8 @@ TEST_F(HeatTransferSensitivityFixture, InitialTemperatureSensitivities) TEST_F(HeatTransferSensitivityFixture, ShapeSensitivities) { - auto thermal_solver = createNonlinearHeatTransfer(data_store, nonlinear_opts, dyn_opts, nonlinearMat); + Domain whole_domain = EntireDomain(*mesh); + auto thermal_solver = createNonlinearHeatTransfer(data_store, nonlinear_opts, dyn_opts, nonlinearMat, whole_domain); auto [qoi_base, _, shape_sensitivity] = computeThermalQoiAndInitialTemperatureAndShapeSensitivity(*thermal_solver, tsInfo); @@ -296,7 +303,8 @@ TEST_F(HeatTransferSensitivityFixture, ShapeSensitivities) TEST_F(HeatTransferSensitivityFixture, ConductivityParameterSensitivities) { - auto thermal_solver = createParameterizedHeatTransfer(data_store, nonlinear_opts, dyn_opts, parameterizedMat); + Domain whole_domain = EntireDomain(*mesh); + auto thermal_solver = createParameterizedHeatTransfer(data_store, nonlinear_opts, dyn_opts, parameterizedMat, whole_domain); auto [qoi_base, conductivity_sensitivity] = computeThermalConductivitySensitivity(*thermal_solver, tsInfo); thermal_solver->resetStates(); @@ -311,8 +319,9 @@ TEST_F(HeatTransferSensitivityFixture, ConductivityParameterSensitivities) TEST_F(HeatTransferSensitivityFixture, NonlinearConductivityParameterSensitivities) { + Domain whole_domain = EntireDomain(*mesh); auto thermal_solver = - createParameterizedNonlinearHeatTransfer(data_store, nonlinear_opts, dyn_opts, parameterizedNonlinearMat); + createParameterizedNonlinearHeatTransfer(data_store, nonlinear_opts, dyn_opts, parameterizedNonlinearMat, whole_domain); auto [qoi_base, conductivity_sensitivity] = computeThermalConductivitySensitivity(*thermal_solver, tsInfo); thermal_solver->resetStates(); diff --git a/src/serac/physics/tests/solid.cpp b/src/serac/physics/tests/solid.cpp index c9d7304cfe..754cca35c9 100644 --- a/src/serac/physics/tests/solid.cpp +++ b/src/serac/physics/tests/solid.cpp @@ -45,7 +45,7 @@ void functional_solid_test_static_J2() std::string mesh_tag{"mesh"}; - serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // _solver_params_start serac::LinearSolverOptions linear_options{.linear_solver = LinearSolver::SuperLU}; @@ -76,7 +76,8 @@ void functional_solid_test_static_J2() auto qdata = solid_solver.createQuadratureDataBuffer(initial_state); - solid_solver.setMaterial(mat, qdata); + Domain whole_domain = EntireDomain(pmesh); + solid_solver.setMaterial(mat, whole_domain, qdata); // prescribe zero displacement at the supported end of the beam, std::set support = {1}; @@ -135,7 +136,7 @@ void functional_solid_spatial_essential_bc() std::string mesh_tag{"mesh"}; - serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // Construct a functional-based solid mechanics solver SolidMechanics solid_solver(solid_mechanics::default_nonlinear_options, @@ -143,7 +144,8 @@ void functional_solid_spatial_essential_bc() solid_mechanics::default_quasistatic_options, "solid_mechanics", mesh_tag); solid_mechanics::LinearIsotropic mat{1.0, 1.0, 1.0}; - solid_solver.setMaterial(mat); + Domain whole_domain = EntireDomain(pmesh); + solid_solver.setMaterial(mat, whole_domain); // Set up auto zero_vector = [](const mfem::Vector&, mfem::Vector& u) { u = 0.0; }; @@ -301,8 +303,11 @@ void functional_parameterized_solid_test(double expected_disp_norm) solid_solver.setParameter(0, user_defined_bulk_modulus); solid_solver.setParameter(1, user_defined_shear_modulus); + Domain whole_domain = EntireDomain(pmesh); + Domain whole_boundary = EntireBoundary(pmesh); + solid_mechanics::ParameterizedLinearIsotropicSolid mat{1.0, 0.0, 0.0}; - solid_solver.setMaterial(DependsOn<0, 1>{}, mat); + solid_solver.setMaterial(DependsOn<0, 1>{}, mat, whole_domain); // Define the function for the initial displacement and boundary condition auto bc = [](const mfem::Vector&, mfem::Vector& bc_vec) -> void { bc_vec = 0.0; }; @@ -331,16 +336,13 @@ void functional_parameterized_solid_test(double expected_disp_norm) } solid_mechanics::ConstantBodyForce force{constant_force}; - solid_solver.addBodyForce(force, EntireDomain(pmesh)); + solid_solver.addBodyForce(force, whole_domain); // add some nonexistent body forces / tractions to check that // these parameterized versions compile and run without error - solid_solver.addBodyForce( - DependsOn<0>{}, [](const auto& x, double /*t*/, auto /* bulk */) { return x * 0.0; }, EntireDomain(pmesh)); - solid_solver.addBodyForce(DependsOn<1>{}, ParameterizedBodyForce{[](const auto& x) { return 0.0 * x; }}, - EntireDomain(pmesh)); - solid_solver.setTraction( - DependsOn<1>{}, [](const auto& x, auto...) { return 0 * x; }, EntireBoundary(pmesh)); + solid_solver.addBodyForce(DependsOn<0>{}, [](const auto& x, double /*t*/, auto /* bulk */) { return x * 0.0; }, whole_domain); + solid_solver.addBodyForce(DependsOn<1>{}, ParameterizedBodyForce{[](const auto& x) { return 0.0 * x; }}, whole_domain); + solid_solver.setTraction(DependsOn<1>{}, [](const auto& x, auto...) { return 0 * x; }, whole_boundary); // Finalize the data structures solid_solver.completeSetup(); diff --git a/src/serac/physics/tests/solid_dynamics_patch.cpp b/src/serac/physics/tests/solid_dynamics_patch.cpp index a460394893..39d98d6a11 100644 --- a/src/serac/physics/tests/solid_dynamics_patch.cpp +++ b/src/serac/physics/tests/solid_dynamics_patch.cpp @@ -131,7 +131,7 @@ class AffineSolution { * @param essential_boundaries Boundary attributes on which essential boundary conditions are desired */ template - void applyLoads(const Material& material, SolidMechanics& solid, std::set essential_boundaries) const + void applyLoads(const Material& material, SolidMechanics& solid, std::set essential_boundaries, Domain & bdr_domain) const { // essential BCs auto ebc_func = [*this](const auto& X, double t, auto& u) { this->operator()(X, t, u); }; @@ -162,7 +162,7 @@ class AffineSolution { auto T = dot(P, n0); return T; }; - solid.setTraction(traction, EntireBoundary(solid.mesh())); + solid.setTraction(traction, bdr_domain); } private: @@ -224,7 +224,7 @@ class ConstantAccelerationSolution { * @param essential_boundaries Boundary attributes on which essential boundary conditions are desired */ template - void applyLoads(const Material& material, SolidMechanics& solid, std::set essential_boundaries) const + void applyLoads(const Material& material, SolidMechanics& solid, std::set essential_boundaries, Domain & domain) const { // essential BCs auto ebc_func = [*this](const auto& X, double t, auto& u) { this->operator()(X, t, u); }; @@ -234,7 +234,7 @@ class ConstantAccelerationSolution { // body force auto a = make_tensor([*this](int i) { return this->acceleration(i); }); - solid.addBodyForce([&material, a](auto /* X */, auto /* t */) { return material.density * a; }); + solid.addBodyForce([&material, a](auto /* X */, auto /* t */) { return material.density * a; }, domain); } private: @@ -310,16 +310,25 @@ double solution_error(solution_type exact_solution, PatchBoundaryCondition bc) "solid_dynamics", mesh_tag); solid_mechanics::NeoHookean mat{.density = 1.0, .K = 1.0, .G = 1.0}; - Domain material_block = EntireDomain(pmesh); - solid.setMaterial(mat, material_block); + Domain whole_domain = EntireDomain(pmesh); + Domain whole_boundary = EntireBoundary(pmesh); + solid.setMaterial(mat, whole_domain); // initial conditions solid.setVelocity([exact_solution](const mfem::Vector& x, mfem::Vector& v) { exact_solution.velocity(x, 0.0, v); }); solid.setDisplacement([exact_solution](const mfem::Vector& x, mfem::Vector& u) { exact_solution(x, 0.0, u); }); + + // forcing terms - exact_solution.applyLoads(mat, solid, essentialBoundaryAttributes(bc)); + if constexpr (std::is_same< solution_type, ConstantAccelerationSolution >::value) { + exact_solution.applyLoads(mat, solid, essentialBoundaryAttributes(bc), whole_domain); + } + + if constexpr (std::is_same< solution_type, AffineSolution >::value) { + exact_solution.applyLoads(mat, solid, essentialBoundaryAttributes(bc), whole_boundary); + } // Finalize the data structures solid.completeSetup(); From db99e0e5d0bf453bbc3c16ffa4efaebab36d166e Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Tue, 19 Nov 2024 15:27:49 -0800 Subject: [PATCH 43/92] add more tests --- .../simple_conduction/without_input_file.cpp | 6 ++++-- .../physics/tests/dynamic_solid_adjoint.cpp | 21 ++++++++++++------- src/serac/physics/tests/fit_test.cpp | 6 ++++-- .../physics/tests/lce_Bertoldi_lattice.cpp | 5 +++-- .../physics/tests/parameterized_thermal.cpp | 9 +++++--- .../physics/tests/solid_statics_patch.cpp | 13 ++++++------ 6 files changed, 37 insertions(+), 23 deletions(-) diff --git a/examples/simple_conduction/without_input_file.cpp b/examples/simple_conduction/without_input_file.cpp index c9dfa01c04..3e6ab5fe52 100644 --- a/examples/simple_conduction/without_input_file.cpp +++ b/examples/simple_conduction/without_input_file.cpp @@ -38,7 +38,7 @@ int main(int argc, char* argv[]) std::string mesh_tag{"mesh"}; - serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // _create_mesh_end // _create_module_start @@ -54,7 +54,9 @@ int main(int argc, char* argv[]) // _conductivity_start constexpr double kappa = 0.5; serac::heat_transfer::LinearIsotropicConductor mat(1.0, 1.0, kappa); - heat_transfer.setMaterial(mat); + + serac::Domain whole_domain = serac::EntireDomain(pmesh); + heat_transfer.setMaterial(mat, whole_domain); // _conductivity_end // _bc_start diff --git a/src/serac/physics/tests/dynamic_solid_adjoint.cpp b/src/serac/physics/tests/dynamic_solid_adjoint.cpp index 567ba2806e..07c9803cc6 100644 --- a/src/serac/physics/tests/dynamic_solid_adjoint.cpp +++ b/src/serac/physics/tests/dynamic_solid_adjoint.cpp @@ -87,7 +87,7 @@ void applyInitialAndBoundaryConditions(SolidMechanics& solid_solver) } std::unique_ptr> createNonlinearSolidMechanicsSolver( - const NonlinearSolverOptions& nonlinear_opts, const TimesteppingOptions& dyn_opts, const SolidMaterial& mat) + const NonlinearSolverOptions& nonlinear_opts, const TimesteppingOptions& dyn_opts, const SolidMaterial& mat, Domain & whole_domain) { static int iter = 0; const LinearSolverOptions linear_options = {.linear_solver = LinearSolver::CG, @@ -101,7 +101,7 @@ std::unique_ptr> createNonlinearSolidMechanicsSolver( auto solid = std::make_unique>(nonlinear_opts, linear_options, dyn_opts, physics_prefix + std::to_string(iter++), mesh_tag, std::vector{}, 0, 0.0, checkpoint_to_disk, false); - solid->setMaterial(mat); + solid->setMaterial(mat, whole_domain); solid->setDisplacementBCs( {1}, [](const mfem::Vector&, double t, mfem::Vector& disp) { disp = (1.0 + 10 * t) * boundary_disp; }); @@ -110,7 +110,7 @@ std::unique_ptr> createNonlinearSolidMechanicsSolver( Y[0] = 0.1 + 0.1 * X[0] + 0.3 * X[1] - 0.2 * t; Y[1] = -0.05 - 0.08 * X[0] + 0.15 * X[1] + 0.3 * t; return 0.4 * X + Y; - }); + }, whole_domain); solid->completeSetup(); applyInitialAndBoundaryConditions(*solid); @@ -273,7 +273,8 @@ struct SolidMechanicsSensitivityFixture : public ::testing::Test { TEST_F(SolidMechanicsSensitivityFixture, InitialDisplacementSensitivities) { - auto solid_solver = createNonlinearSolidMechanicsSolver(nonlinear_opts, dyn_opts, mat); + Domain whole_domain = EntireDomain(*mesh); + auto solid_solver = createNonlinearSolidMechanicsSolver(nonlinear_opts, dyn_opts, mat, whole_domain); auto [qoi_base, init_disp_sensitivity, _, __] = computeSolidMechanicsQoiSensitivities(*solid_solver, tsInfo); solid_solver->resetStates(); @@ -290,7 +291,8 @@ TEST_F(SolidMechanicsSensitivityFixture, InitialDisplacementSensitivities) TEST_F(SolidMechanicsSensitivityFixture, InitialVelocitySensitivities) { - auto solid_solver = createNonlinearSolidMechanicsSolver(nonlinear_opts, dyn_opts, mat); + Domain whole_domain = EntireDomain(*mesh); + auto solid_solver = createNonlinearSolidMechanicsSolver(nonlinear_opts, dyn_opts, mat, whole_domain); auto [qoi_base, _, init_velo_sensitivity, __] = computeSolidMechanicsQoiSensitivities(*solid_solver, tsInfo); solid_solver->resetStates(); @@ -306,7 +308,8 @@ TEST_F(SolidMechanicsSensitivityFixture, InitialVelocitySensitivities) TEST_F(SolidMechanicsSensitivityFixture, ShapeSensitivities) { - auto solid_solver = createNonlinearSolidMechanicsSolver(nonlinear_opts, dyn_opts, mat); + Domain whole_domain = EntireDomain(*mesh); + auto solid_solver = createNonlinearSolidMechanicsSolver(nonlinear_opts, dyn_opts, mat, whole_domain); auto [qoi_base, _, __, shape_sensitivity] = computeSolidMechanicsQoiSensitivities(*solid_solver, tsInfo); solid_solver->resetStates(); @@ -322,8 +325,9 @@ TEST_F(SolidMechanicsSensitivityFixture, ShapeSensitivities) TEST_F(SolidMechanicsSensitivityFixture, QuasiStaticShapeSensitivities) { + Domain whole_domain = EntireDomain(*mesh); dyn_opts.timestepper = TimestepMethod::QuasiStatic; - auto solid_solver = createNonlinearSolidMechanicsSolver(nonlinear_opts, dyn_opts, mat); + auto solid_solver = createNonlinearSolidMechanicsSolver(nonlinear_opts, dyn_opts, mat, whole_domain); auto [qoi_base, _, __, shape_sensitivity] = computeSolidMechanicsQoiSensitivities(*solid_solver, tsInfo); solid_solver->resetStates(); @@ -339,7 +343,8 @@ TEST_F(SolidMechanicsSensitivityFixture, QuasiStaticShapeSensitivities) TEST_F(SolidMechanicsSensitivityFixture, WhenShapeSensitivitiesCalledTwice_GetSameObjectiveAndGradient) { - auto solid_solver = createNonlinearSolidMechanicsSolver(nonlinear_opts, dyn_opts, mat); + Domain whole_domain = EntireDomain(*mesh); + auto solid_solver = createNonlinearSolidMechanicsSolver(nonlinear_opts, dyn_opts, mat, whole_domain); auto [qoi1, _, __, shape_sensitivity1] = computeSolidMechanicsQoiSensitivities(*solid_solver, tsInfo); solid_solver->resetStates(); diff --git a/src/serac/physics/tests/fit_test.cpp b/src/serac/physics/tests/fit_test.cpp index 3af17185f8..4171bc466c 100644 --- a/src/serac/physics/tests/fit_test.cpp +++ b/src/serac/physics/tests/fit_test.cpp @@ -67,7 +67,9 @@ void stress_extrapolation_test() 50.0 // shear modulus }; - solid_solver.setMaterial(mat); + Domain whole_domain = EntireDomain(pmesh); + + solid_solver.setMaterial(mat, whole_domain); // prescribe small displacement at each hole, pulling the plate apart std::set top_hole = {2}; @@ -99,7 +101,7 @@ void stress_extrapolation_test() auto stress = mat(internal_variables, du_dx); return tuple{I2(dev(stress)), zero{}}; }, - pmesh, u); + whole_domain, u); solid_solver.setParameter(0, sigma_J2); diff --git a/src/serac/physics/tests/lce_Bertoldi_lattice.cpp b/src/serac/physics/tests/lce_Bertoldi_lattice.cpp index cf1df68acc..0a6d812bee 100644 --- a/src/serac/physics/tests/lce_Bertoldi_lattice.cpp +++ b/src/serac/physics/tests/lce_Bertoldi_lattice.cpp @@ -56,7 +56,7 @@ TEST(LiquidCrystalElastomer, Bertoldi) std::string mesh_tag{"mesh"}; - serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // Construct a functional-based solid mechanics solver LinearSolverOptions linear_options = {.linear_solver = LinearSolver::SuperLU}; @@ -116,7 +116,8 @@ TEST(LiquidCrystalElastomer, Bertoldi) // Set material LiquidCrystalElastomerBertoldi lceMat(density, young_modulus, possion_ratio, max_order_param, beta_param); - solid_solver.setMaterial(DependsOn{}, lceMat); + Domain whole_domain = EntireDomain(pmesh); + solid_solver.setMaterial(DependsOn{}, lceMat, whole_domain); // Boundary conditions: // Prescribe zero displacement at the supported end of the beam diff --git a/src/serac/physics/tests/parameterized_thermal.cpp b/src/serac/physics/tests/parameterized_thermal.cpp index d89174ac3e..3049826a7b 100644 --- a/src/serac/physics/tests/parameterized_thermal.cpp +++ b/src/serac/physics/tests/parameterized_thermal.cpp @@ -66,9 +66,12 @@ TEST(Thermal, ParameterizedMaterial) thermal_solver.setParameter(0, user_defined_conductivity); + Domain whole_domain = EntireDomain(pmesh); + Domain whole_boundary = EntireBoundary(pmesh); + // Construct a potentially user-defined parameterized material and send it to the thermal module heat_transfer::ParameterizedLinearIsotropicConductor mat; - thermal_solver.setMaterial(DependsOn<0>{}, mat); + thermal_solver.setMaterial(DependsOn<0>{}, mat, whole_domain); // Define the function for the initial temperature and boundary condition auto bdr_temp = [](const mfem::Vector& x, double) -> double { @@ -84,11 +87,11 @@ TEST(Thermal, ParameterizedMaterial) // Define a constant source term heat_transfer::ConstantSource source{-1.0}; - thermal_solver.setSource(source, EntireDomain(pmesh)); + thermal_solver.setSource(source, whole_domain); // Set the flux term to zero for testing code paths heat_transfer::ConstantFlux flux_bc{0.0}; - thermal_solver.setFluxBCs(flux_bc); + thermal_solver.setFluxBCs(flux_bc, whole_boundary); // Finalize the data structures thermal_solver.completeSetup(); diff --git a/src/serac/physics/tests/solid_statics_patch.cpp b/src/serac/physics/tests/solid_statics_patch.cpp index f98f2266c4..1ec794afe4 100644 --- a/src/serac/physics/tests/solid_statics_patch.cpp +++ b/src/serac/physics/tests/solid_statics_patch.cpp @@ -177,7 +177,7 @@ class ManufacturedSolution { * @param essential_boundaries Boundary attributes on which essential boundary conditions are desired */ template - void applyLoads(const material_type & material, SolidMechanics& sf, std::set essential_boundaries) const + void applyLoads(const material_type & material, SolidMechanics& sf, std::set essential_boundaries, Domain & domain, Domain & boundary) const { // essential BCs auto ebc_func = [*this](const auto& X, auto& u){ this->operator()(X, u); }; @@ -191,7 +191,7 @@ class ManufacturedSolution { return dot(P, n0); }; - sf.setTraction(traction, EntireBoundary(sf.mesh())); + sf.setTraction(traction, boundary); auto bf = [=](auto X, auto) { auto X_val = get_value(X); @@ -206,7 +206,7 @@ class ManufacturedSolution { return divP; }; - sf.addBodyForce(DependsOn<>{}, bf, EntireDomain(sf.mesh())); + sf.addBodyForce(DependsOn<>{}, bf, domain); } @@ -324,10 +324,11 @@ double solution_error(PatchBoundaryCondition bc) SolidMechanics solid(std::move(equation_solver), solid_mechanics::default_quasistatic_options, "solid", mesh_tag); solid_mechanics::NeoHookean mat{.density=1.0, .K=1.0, .G=1.0}; - Domain material_block = EntireDomain(pmesh); - solid.setMaterial(mat, material_block); + Domain domain = EntireDomain(pmesh); + Domain boundary = EntireBoundary(pmesh); + solid.setMaterial(mat, domain); - exact_displacement.applyLoads(mat, solid, essentialBoundaryAttributes(bc)); + exact_displacement.applyLoads(mat, solid, essentialBoundaryAttributes(bc), domain, boundary); // Finalize the data structures solid.completeSetup(); From 080e0d089b0d55a29374d3654edc507c43e89fe9 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Tue, 19 Nov 2024 16:04:53 -0800 Subject: [PATCH 44/92] fix indexing bug in element stiffness calculation on interior faces, update remaining tests --- src/drivers/CMakeLists.txt | 60 +++++++++---------- .../interior_face_integral_kernels.hpp | 10 ++-- src/serac/physics/fit.hpp | 13 ++-- src/serac/physics/solid_mechanics.hpp | 2 + src/serac/physics/tests/fit_test.cpp | 2 +- .../tests/quasistatic_solid_adjoint.cpp | 6 +- .../physics/tests/solid_multi_material.cpp | 10 ---- src/serac/physics/tests/thermal_mechanics.cpp | 12 ++-- .../physics/tests/thermal_statics_patch.cpp | 12 ++-- src/serac/physics/thermomechanics.hpp | 11 ++-- 10 files changed, 70 insertions(+), 68 deletions(-) diff --git a/src/drivers/CMakeLists.txt b/src/drivers/CMakeLists.txt index 5c213cb306..121ad55566 100644 --- a/src/drivers/CMakeLists.txt +++ b/src/drivers/CMakeLists.txt @@ -4,33 +4,33 @@ # # SPDX-License-Identifier: (BSD-3-Clause) -blt_add_executable( NAME serac_driver - SOURCES serac.cpp - DEPENDS_ON serac_physics serac_mesh - OUTPUT_NAME serac - ) - -if (SERAC_ENABLE_TESTS) - set(input_files_dir ${CMAKE_CURRENT_SOURCE_DIR}/../../data/input_files/tests) - - # Run basic test for the Serac driver - blt_add_test(NAME serac_driver_solid - COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/serac -o driver_solid -i ${input_files_dir}/solid/dyn_solve.lua - NUM_MPI_TASKS 1 ) - - blt_add_test(NAME serac_driver_heat_transfer - COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/serac -o driver_heat_transfer -i ${input_files_dir}/heat_transfer/static_solve.lua - NUM_MPI_TASKS 1 ) - - blt_add_test(NAME serac_driver_help - COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/serac --help - NUM_MPI_TASKS 1 ) - - blt_add_test(NAME serac_driver_docs - COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/serac -o docs -d -i ${input_files_dir}/solid/qs_linear.lua - NUM_MPI_TASKS 1 ) -endif() - -install( TARGETS serac_driver - RUNTIME DESTINATION bin - ) +#blt_add_executable( NAME serac_driver +# SOURCES serac.cpp +# DEPENDS_ON serac_physics serac_mesh +# OUTPUT_NAME serac +# ) +# +#if (SERAC_ENABLE_TESTS) +# set(input_files_dir ${CMAKE_CURRENT_SOURCE_DIR}/../../data/input_files/tests) +# +# # Run basic test for the Serac driver +# blt_add_test(NAME serac_driver_solid +# COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/serac -o driver_solid -i ${input_files_dir}/solid/dyn_solve.lua +# NUM_MPI_TASKS 1 ) +# +# blt_add_test(NAME serac_driver_heat_transfer +# COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/serac -o driver_heat_transfer -i ${input_files_dir}/heat_transfer/static_solve.lua +# NUM_MPI_TASKS 1 ) +# +# blt_add_test(NAME serac_driver_help +# COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/serac --help +# NUM_MPI_TASKS 1 ) +# +# blt_add_test(NAME serac_driver_docs +# COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/serac -o docs -d -i ${input_files_dir}/solid/qs_linear.lua +# NUM_MPI_TASKS 1 ) +#endif() +# +#install( TARGETS serac_driver +# RUNTIME DESTINATION bin +# ) diff --git a/src/serac/numerics/functional/interior_face_integral_kernels.hpp b/src/serac/numerics/functional/interior_face_integral_kernels.hpp index 3a4875dfa5..d3567ce87a 100644 --- a/src/serac/numerics/functional/interior_face_integral_kernels.hpp +++ b/src/serac/numerics/functional/interior_face_integral_kernels.hpp @@ -321,13 +321,15 @@ void element_gradient_kernel([[maybe_unused]] ExecArrayView -FiniteElementState fit(std::integer_sequence, func f, Domain & domain, const T&... solution_fields) +FiniteElementState fit(std::integer_sequence, func f, mfem::ParMesh & pmesh, const T&... solution_fields) { // signature looks like return_type(arg0_type, arg1_type); // so this unpacks the return type using output_space = typename FunctionSignature::return_type; - FiniteElementState fitted_field(domain.mesh_, output_space{}); + FiniteElementState fitted_field(pmesh, output_space{}); fitted_field = 0.0; // mass term + Domain whole_domain = EntireDomain(pmesh); serac::Functional phi_phi(&fitted_field.space(), {&fitted_field.space()}); phi_phi.AddDomainIntegral( Dimension{}, DependsOn<0>{}, [](double /*t*/, auto /*x*/, auto u) { return tuple{get<0>(u), zero{}}; }, - domain); + whole_domain); auto M = get<1>(phi_phi(DifferentiateWRT<0>{}, 0.0 /* t */, fitted_field)); // rhs std::array trial_spaces = {&solution_fields.space()...}; serac::Functional phi_f(&fitted_field.space(), trial_spaces); - phi_f.AddDomainIntegral(Dimension{}, DependsOn{}, f, domain); + phi_f.AddDomainIntegral(Dimension{}, DependsOn{}, f, whole_domain); mfem::Vector b = phi_f(0.0, solution_fields...); mfem::CGSolver cg(MPI_COMM_WORLD); @@ -63,10 +64,10 @@ FiniteElementState fit(std::integer_sequence, func f, Domain & domain * @note: mesh is passed by non-const ref because mfem mutates the mesh when creating ParGridFunctions */ template -FiniteElementState fit(func f, Domain & domain, const T&... solution_fields) +FiniteElementState fit(func f, mfem::ParMesh & pmesh, const T&... solution_fields) { auto iseq = std::make_integer_sequence{}; - return detail::fit(iseq, f, domain, solution_fields...); + return detail::fit(iseq, f, pmesh, solution_fields...); } } // namespace serac diff --git a/src/serac/physics/solid_mechanics.hpp b/src/serac/physics/solid_mechanics.hpp index 6eb508f132..94ef817502 100644 --- a/src/serac/physics/solid_mechanics.hpp +++ b/src/serac/physics/solid_mechanics.hpp @@ -263,6 +263,7 @@ class SolidMechanics, std::integer_se initializeSolidMechanicsStates(); } +#if 0 /** * @brief Construct a new Nonlinear SolidMechanics Solver object * @@ -352,6 +353,7 @@ class SolidMechanics, std::integer_se } } } +#endif /// @brief Destroy the SolidMechanics Functional object virtual ~SolidMechanics() {} diff --git a/src/serac/physics/tests/fit_test.cpp b/src/serac/physics/tests/fit_test.cpp index 4171bc466c..adcc7aa67f 100644 --- a/src/serac/physics/tests/fit_test.cpp +++ b/src/serac/physics/tests/fit_test.cpp @@ -101,7 +101,7 @@ void stress_extrapolation_test() auto stress = mat(internal_variables, du_dx); return tuple{I2(dev(stress)), zero{}}; }, - whole_domain, u); + pmesh, u); solid_solver.setParameter(0, sigma_J2); diff --git a/src/serac/physics/tests/quasistatic_solid_adjoint.cpp b/src/serac/physics/tests/quasistatic_solid_adjoint.cpp index f09c276a16..5bee184c48 100644 --- a/src/serac/physics/tests/quasistatic_solid_adjoint.cpp +++ b/src/serac/physics/tests/quasistatic_solid_adjoint.cpp @@ -157,7 +157,9 @@ TEST(quasistatic, finiteDifference) using materialType = ParameterizedNeoHookeanSolid; materialType material; - seracSolid->setMaterial(::serac::DependsOn<0, 1>{}, material); + + Domain whole_domain = EntireDomain(*meshPtr); + seracSolid->setMaterial(::serac::DependsOn<0, 1>{}, material, whole_domain); seracSolid->setDisplacementBCs( {3}, [](const mfem::Vector&) { return 0.0; }, 0); @@ -195,7 +197,7 @@ TEST(quasistatic, finiteDifference) auto stress = material(state, du_dx, E, v); return stress[2][2] * time; }, - *meshPtr); + whole_domain); int nTimeSteps = 3; double timeStep = 0.8; diff --git a/src/serac/physics/tests/solid_multi_material.cpp b/src/serac/physics/tests/solid_multi_material.cpp index 9929a280f8..b0ab17f416 100644 --- a/src/serac/physics/tests/solid_multi_material.cpp +++ b/src/serac/physics/tests/solid_multi_material.cpp @@ -16,16 +16,6 @@ namespace serac { -template -tensor average(std::vector>& positions) -{ - tensor total{}; - for (auto x : positions) { - total += x; - } - return total / double(positions.size()); -} - TEST(Solid, MultiMaterial) { /* diff --git a/src/serac/physics/tests/thermal_mechanics.cpp b/src/serac/physics/tests/thermal_mechanics.cpp index b73c7f7501..f2024ec3a2 100644 --- a/src/serac/physics/tests/thermal_mechanics.cpp +++ b/src/serac/physics/tests/thermal_mechanics.cpp @@ -40,7 +40,7 @@ void functional_test_static_3D(double expected_norm) std::string mesh_tag{"mesh"}; - serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // Define a boundary attribute set std::set ess_bdr = {1}; @@ -73,7 +73,9 @@ void functional_test_static_3D(double expected_norm) GreenSaintVenantThermoelasticMaterial material{rho, E, nu, c, alpha, theta_ref, k}; GreenSaintVenantThermoelasticMaterial::State initial_state{}; auto qdata = thermal_solid_solver.createQuadratureDataBuffer(initial_state); - thermal_solid_solver.setMaterial(material, qdata); + + Domain whole_domain = EntireDomain(pmesh); + thermal_solid_solver.setMaterial(material, whole_domain, qdata); // Define the function for the initial temperature and boundary condition auto one = [](const mfem::Vector&, double) -> double { return 1.0; }; @@ -122,7 +124,7 @@ void functional_test_shrinking_3D(double expected_norm) std::string mesh_tag{"mesh"}; - serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // Define a boundary attribute set std::set constraint_bdr = {1}; @@ -155,7 +157,9 @@ void functional_test_shrinking_3D(double expected_norm) GreenSaintVenantThermoelasticMaterial material{rho, E, nu, c, alpha, theta_ref, k}; GreenSaintVenantThermoelasticMaterial::State initial_state{}; auto qdata = thermal_solid_solver.createQuadratureDataBuffer(initial_state); - thermal_solid_solver.setMaterial(material, qdata); + + Domain whole_domain = EntireDomain(pmesh); + thermal_solid_solver.setMaterial(material, whole_domain, qdata); // Define the function for the initial temperature double theta_0 = 1.0; diff --git a/src/serac/physics/tests/thermal_statics_patch.cpp b/src/serac/physics/tests/thermal_statics_patch.cpp index 0b0357e999..b802690f2e 100644 --- a/src/serac/physics/tests/thermal_statics_patch.cpp +++ b/src/serac/physics/tests/thermal_statics_patch.cpp @@ -68,7 +68,7 @@ class AffineSolution { * @param essential_boundaries Boundary attributes on which essential boundary conditions are desired */ template - void applyLoads(const Material& material, HeatTransfer& physics, std::set essential_boundaries) const + void applyLoads(const Material& material, HeatTransfer& physics, std::set essential_boundaries, Domain & boundary) const { // essential BCs auto ebc_func = [*this](const auto& X, auto){ return this->operator()(X); }; @@ -80,7 +80,7 @@ class AffineSolution { auto flux = serac::get<1>(material(dummy_x, 1.0, temp_grad)); auto surface_flux = [flux](auto, auto n0, auto, auto) { return dot(flux, n0); }; - physics.setFluxBCs(surface_flux); + physics.setFluxBCs(surface_flux, boundary); } private: @@ -167,7 +167,7 @@ double solution_error(const ExactSolution& exact_temperature, PatchBoundaryCondi std::string mesh_tag{"mesh"}; - serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // Construct a heat transfer mechanics solver auto nonlinear_opts = heat_transfer::default_nonlinear_options; @@ -176,9 +176,11 @@ double solution_error(const ExactSolution& exact_temperature, PatchBoundaryCondi HeatTransfer thermal(nonlinear_opts, heat_transfer::direct_linear_options, heat_transfer::default_static_options, "thermal", mesh_tag); heat_transfer::LinearIsotropicConductor mat(1.0,1.0,1.0); - thermal.setMaterial(mat); + Domain whole_domain = EntireDomain(pmesh); + Domain whole_boundary = EntireBoundary(pmesh); + thermal.setMaterial(mat, whole_domain); - exact_temperature.applyLoads(mat, thermal, essentialBoundaryAttributes(bc)); + exact_temperature.applyLoads(mat, thermal, essentialBoundaryAttributes(bc), whole_boundary); // Finalize the data structures thermal.completeSetup(); diff --git a/src/serac/physics/thermomechanics.hpp b/src/serac/physics/thermomechanics.hpp index f0a210c53e..83d2826c54 100644 --- a/src/serac/physics/thermomechanics.hpp +++ b/src/serac/physics/thermomechanics.hpp @@ -371,22 +371,21 @@ class Thermomechanics : public BasePhysics { * and thermal flux when operator() is called with the arguments listed above. */ template - void setMaterial(DependsOn, const MaterialType& material, + void setMaterial(DependsOn, const MaterialType& material, Domain & domain, std::shared_ptr> qdata) { // note: these parameter indices are offset by 1 since, internally, this module uses the first parameter // to communicate the temperature and displacement field information to the other physics module // - thermal_.setMaterial(DependsOn<0, active_parameters + 1 ...>{}, ThermalMaterialInterface{material}); - solid_.setMaterial(DependsOn<0, active_parameters + 1 ...>{}, MechanicalMaterialInterface{material}, - qdata); + thermal_.setMaterial(DependsOn<0, active_parameters + 1 ...>{}, ThermalMaterialInterface{material}, domain); + solid_.setMaterial(DependsOn<0, active_parameters + 1 ...>{}, MechanicalMaterialInterface{material}, domain, qdata); } /// @overload template - void setMaterial(const MaterialType& material, std::shared_ptr> qdata = EmptyQData) + void setMaterial(const MaterialType& material, Domain& domain, std::shared_ptr> qdata = EmptyQData) { - setMaterial(DependsOn<>{}, material, qdata); + setMaterial(DependsOn<>{}, material, domain, qdata); } /** From 5a0f61112b62f80738eaf3b46d431891dd5a5e3d Mon Sep 17 00:00:00 2001 From: Brandon Talamini Date: Wed, 20 Nov 2024 14:51:59 -0800 Subject: [PATCH 45/92] Put in test that catches bug in set operations, add tests for Entire* functions --- .../functional/tests/domain_tests.cpp | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/serac/numerics/functional/tests/domain_tests.cpp b/src/serac/numerics/functional/tests/domain_tests.cpp index dfd1b05720..f7d3f87ffe 100644 --- a/src/serac/numerics/functional/tests/domain_tests.cpp +++ b/src/serac/numerics/functional/tests/domain_tests.cpp @@ -336,6 +336,75 @@ TEST(domain, of_elements) } } +TEST(domain, entireDomain2d) +{ + constexpr int dim = 2; + constexpr int p = 1; + auto mesh = import_mesh("patch2D_tris_and_quads.mesh"); + + Domain d0 = EntireDomain(mesh); + + EXPECT_EQ(d0.dim_, 2); + EXPECT_EQ(d0.tri_ids_.size(), 2); + EXPECT_EQ(d0.quad_ids_.size(), 4); + + auto fec = mfem::H1_FECollection(p, dim); + auto fes = mfem::FiniteElementSpace(&mesh, &fec); + + mfem::Array dof_indices = d0.dof_list(&fes); + EXPECT_EQ(dof_indices.Size(), 8); +} + +TEST(domain, entireDomain3d) +{ + constexpr int dim = 3; + constexpr int p = 1; + auto mesh = import_mesh("patch3D_tets_and_hexes.mesh"); + + Domain d0 = EntireDomain(mesh); + + EXPECT_EQ(d0.dim_, 3); + EXPECT_EQ(d0.tet_ids_.size(), 12); + EXPECT_EQ(d0.hex_ids_.size(), 7); + + auto fec = mfem::H1_FECollection(p, dim); + auto fes = mfem::FiniteElementSpace(&mesh, &fec); + + mfem::Array dof_indices = d0.dof_list(&fes); + EXPECT_EQ(dof_indices.Size(), 25); +} + +TEST(domain, ofElements2dFindsDofs) +{ + constexpr int dim = 2; + constexpr int p = 2; + auto mesh = import_mesh("patch2D_tris_and_quads.mesh"); + + auto find_element_0 = [](std::vector vertices, int /* attr */) { + auto centroid = average(vertices); + return (centroid[0] < 0.5) && (centroid[1] < 0.25); + }; + + Domain d0 = Domain::ofElements(mesh, find_element_0); + + auto fec = mfem::H1_FECollection(p, dim); + auto fes = mfem::FiniteElementSpace(&mesh, &fec); + + mfem::Array dof_indices = d0.dof_list(&fes); + EXPECT_EQ(dof_indices.Size(), 9); + + auto find_element_1 = [](std::vector vertices, int /* attr */) { + auto centroid = average(vertices); + return (centroid[0] < 0.5) && (centroid[1] < 0.25); + }; + Domain d1 = Domain::ofElements(mesh, find_element_1); + + Domain elements_0_and_1 = d0 | d1; + + dof_indices = elements_0_and_1.dof_list(&fes); + EXPECT_EQ(dof_indices.Size(), 15); +} + int main(int argc, char* argv[]) { int num_procs, myid; From 2bf54fb3b831605bacf76051851dabff0fdd1a2a Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Thu, 21 Nov 2024 07:01:59 -0800 Subject: [PATCH 46/92] add a lot of doxygen comments, trying to figure out how to add support for FaceNbrData --- src/serac/numerics/functional/domain.cpp | 44 ++-- src/serac/numerics/functional/domain.hpp | 48 ++-- .../functional/element_restriction.cpp | 72 +++++- .../functional/element_restriction.hpp | 13 +- .../numerics/functional/finite_element.hpp | 20 +- src/serac/numerics/functional/functional.hpp | 31 +++ .../numerics/functional/functional_qoi.inl | 2 +- .../numerics/functional/geometric_factors.cpp | 8 +- src/serac/numerics/functional/integral.hpp | 9 +- .../interior_face_integral_kernels.hpp | 8 + .../numerics/functional/tests/CMakeLists.txt | 1 + .../functional/tests/bug_residual.cpp | 105 +++++++++ .../functional/tests/check_gradient.hpp | 1 + .../numerics/functional/tests/cuda_sandbox.cu | 212 ++++++++++++++++++ .../tests/dg_restriction_operators.cpp | 12 +- .../functional/tests/functional_basic_dg.cpp | 84 ++++++- src/serac/numerics/functional/typedefs.hpp | 11 + src/serac/physics/fit.hpp | 2 +- src/serac/physics/heat_transfer.hpp | 7 +- src/serac/physics/solid_mechanics.hpp | 22 +- .../physics/state/finite_element_vector.hpp | 3 +- src/serac/physics/tests/solid_finite_diff.cpp | 10 +- .../physics/tests/solid_statics_patch.cpp | 3 +- src/serac/physics/thermomechanics.hpp | 3 + 24 files changed, 636 insertions(+), 95 deletions(-) create mode 100644 src/serac/numerics/functional/tests/bug_residual.cpp create mode 100644 src/serac/numerics/functional/tests/cuda_sandbox.cu create mode 100644 src/serac/numerics/functional/typedefs.hpp diff --git a/src/serac/numerics/functional/domain.cpp b/src/serac/numerics/functional/domain.cpp index 9a424dd9f6..4eb2ceaca3 100644 --- a/src/serac/numerics/functional/domain.cpp +++ b/src/serac/numerics/functional/domain.cpp @@ -8,7 +8,7 @@ * @file domain.hpp * * @brief many of the functions in this file amount to extracting - * element indices from an mfem::Mesh like + * element indices from an mesh_t like * * | mfem::Geometry | mfem element id | tri id | quad id | * | -------------- | --------------- | ------ | ------- | @@ -29,6 +29,8 @@ namespace serac { +using mesh_t = mfem::Mesh; + /** * @brief gather vertex coordinates for a list of vertices * @@ -49,7 +51,7 @@ std::vector> gather(const mfem::Vector& coordinates, mfem::Arr } template -static Domain domain_of_vertices(const mfem::Mesh& mesh, std::function)> predicate) +static Domain domain_of_vertices(const mesh_t& mesh, std::function)> predicate) { assert(mesh.SpaceDimension() == d); @@ -76,12 +78,12 @@ static Domain domain_of_vertices(const mfem::Mesh& mesh, std::function func) +Domain Domain::ofVertices(const mesh_t& mesh, std::function func) { return domain_of_vertices(mesh, func); } -Domain Domain::ofVertices(const mfem::Mesh& mesh, std::function func) +Domain Domain::ofVertices(const mesh_t& mesh, std::function func) { return domain_of_vertices(mesh, func); } @@ -90,7 +92,7 @@ Domain Domain::ofVertices(const mfem::Mesh& mesh, std::function func /////////////////////////////////////////////////////////////////////////////////////// template -static Domain domain_of_edges(const mfem::Mesh& mesh, std::function predicate) +static Domain domain_of_edges(const mesh_t& mesh, std::function predicate) { assert(mesh.SpaceDimension() == d); @@ -131,12 +133,12 @@ static Domain domain_of_edges(const mfem::Mesh& mesh, std::function predicate return output; } -Domain Domain::ofEdges(const mfem::Mesh& mesh, std::function, int)> func) +Domain Domain::ofEdges(const mesh_t& mesh, std::function, int)> func) { return domain_of_edges<2>(mesh, func); } -Domain Domain::ofEdges(const mfem::Mesh& mesh, std::function)> func) +Domain Domain::ofEdges(const mesh_t& mesh, std::function)> func) { return domain_of_edges<3>(mesh, func); } @@ -145,7 +147,7 @@ Domain Domain::ofEdges(const mfem::Mesh& mesh, std::function -static Domain domain_of_faces(const mfem::Mesh& mesh, +static Domain domain_of_faces(const mesh_t& mesh, std::function>, int)> predicate) { assert(mesh.SpaceDimension() == d); @@ -214,12 +216,12 @@ static Domain domain_of_faces(const mfem::Mesh& return output; } -Domain Domain::ofFaces(const mfem::Mesh& mesh, std::function, int)> func) +Domain Domain::ofFaces(const mesh_t& mesh, std::function, int)> func) { return domain_of_faces(mesh, func); } -Domain Domain::ofFaces(const mfem::Mesh& mesh, std::function, int)> func) +Domain Domain::ofFaces(const mesh_t& mesh, std::function, int)> func) { return domain_of_faces(mesh, func); } @@ -228,7 +230,7 @@ Domain Domain::ofFaces(const mfem::Mesh& mesh, std::function -static Domain domain_of_elems(const mfem::Mesh& mesh, +static Domain domain_of_elems(const mesh_t& mesh, std::function>, int)> predicate) { assert(mesh.SpaceDimension() == d); @@ -295,12 +297,12 @@ static Domain domain_of_elems(const mfem::Mesh& return output; } -Domain Domain::ofElements(const mfem::Mesh& mesh, std::function, int)> func) +Domain Domain::ofElements(const mesh_t& mesh, std::function, int)> func) { return domain_of_elems<2>(mesh, func); } -Domain Domain::ofElements(const mfem::Mesh& mesh, std::function, int)> func) +Domain Domain::ofElements(const mesh_t& mesh, std::function, int)> func) { return domain_of_elems<3>(mesh, func); } @@ -309,7 +311,7 @@ Domain Domain::ofElements(const mfem::Mesh& mesh, std::function -static Domain domain_of_boundary_elems(const mfem::Mesh& mesh, +static Domain domain_of_boundary_elems(const mesh_t& mesh, std::function>, int)> predicate) { assert(mesh.SpaceDimension() == d); @@ -375,17 +377,17 @@ static Domain domain_of_boundary_elems(const mfem::Mesh& return output; } -Domain Domain::ofBoundaryElements(const mfem::Mesh& mesh, std::function, int)> func) +Domain Domain::ofBoundaryElements(const mesh_t& mesh, std::function, int)> func) { return domain_of_boundary_elems<2>(mesh, func); } -Domain Domain::ofBoundaryElements(const mfem::Mesh& mesh, std::function, int)> func) +Domain Domain::ofBoundaryElements(const mesh_t& mesh, std::function, int)> func) { return domain_of_boundary_elems<3>(mesh, func); } -mfem::Array Domain::dof_list(mfem::FiniteElementSpace* fes) const +mfem::Array Domain::dof_list(const serac::fes_t * fes) const { std::set dof_ids; mfem::Array elem_dofs; @@ -453,7 +455,7 @@ mfem::Array Domain::dof_list(mfem::FiniteElementSpace* fes) const return uniq_dof_ids; } -void Domain::insert_restriction(const mfem::FiniteElementSpace * fes, FunctionSpace space) { +void Domain::insert_restriction(const serac::fes_t * fes, FunctionSpace space) { // if we don't already have a BlockElementRestriction for this FunctionSpace, make one if (restriction_operators.count(space) == 0) { @@ -472,7 +474,7 @@ const BlockElementRestriction & Domain::get_restriction(FunctionSpace space) { /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// -Domain EntireDomain(const mfem::Mesh& mesh) +Domain EntireDomain(const mesh_t& mesh) { Domain output{mesh, mesh.SpaceDimension() /* elems can be 2 or 3 dimensional */}; @@ -512,7 +514,7 @@ Domain EntireDomain(const mfem::Mesh& mesh) return output; } -Domain EntireBoundary(const mfem::Mesh& mesh) +Domain EntireBoundary(const mesh_t& mesh) { Domain output{mesh, mesh.SpaceDimension() - 1, Domain::Type::BoundaryElements}; @@ -549,7 +551,7 @@ Domain EntireBoundary(const mfem::Mesh& mesh) } /// @brief constructs a domain from all the interior face elements in a mesh -Domain InteriorFaces(const mfem::Mesh& mesh) { +Domain InteriorFaces(const mesh_t & mesh) { Domain output{mesh, mesh.SpaceDimension() - 1, Domain::Type::InteriorFaces}; diff --git a/src/serac/numerics/functional/domain.hpp b/src/serac/numerics/functional/domain.hpp index c2e590d5d3..129601117e 100644 --- a/src/serac/numerics/functional/domain.hpp +++ b/src/serac/numerics/functional/domain.hpp @@ -13,6 +13,7 @@ #include "serac/numerics/functional/tensor.hpp" #include "serac/numerics/functional/finite_element.hpp" #include "serac/numerics/functional/element_restriction.hpp" +#include "serac/numerics/functional/typedefs.hpp" namespace serac { @@ -36,7 +37,7 @@ struct Domain { static constexpr int num_types = 3; ///< the number of entries in the Type enum /// @brief the underyling mesh for this domain - const mfem::Mesh& mesh_; + const mesh_t& mesh_; /// @brief the geometric dimension of the domain int dim_; @@ -67,7 +68,10 @@ struct Domain { std::map< FunctionSpace, BlockElementRestriction > restriction_operators; - Domain(const mfem::Mesh& m, int d, Type type = Domain::Type::Elements) : mesh_(m), dim_(d), type_(type) {} + /** + * @brief empty Domain constructor, with connectivity info to be populated later + */ + Domain(const mesh_t& m, int d, Type type = Domain::Type::Elements) : mesh_(m), dim_(d), type_(type) {} /** * @brief create a domain from some subset of the vertices in an mfem::Mesh @@ -75,10 +79,10 @@ struct Domain { * @param func predicate function for determining which vertices will be * included in this domain. The function's argument is the spatial position of the vertex. */ - static Domain ofVertices(const mfem::Mesh& mesh, std::function func); + static Domain ofVertices(const mesh_t& mesh, std::function func); /// @overload - static Domain ofVertices(const mfem::Mesh& mesh, std::function func); + static Domain ofVertices(const mesh_t& mesh, std::function func); /** * @brief create a domain from some subset of the edges in an mfem::Mesh @@ -87,10 +91,10 @@ struct Domain { * included in this domain. The function's arguments are the list of vertex coordinates and * an attribute index (if appropriate). */ - static Domain ofEdges(const mfem::Mesh& mesh, std::function, int)> func); + static Domain ofEdges(const mesh_t& mesh, std::function, int)> func); /// @overload - static Domain ofEdges(const mfem::Mesh& mesh, std::function)> func); + static Domain ofEdges(const mesh_t& mesh, std::function)> func); /** * @brief create a domain from some subset of the faces in an mfem::Mesh @@ -99,10 +103,10 @@ struct Domain { * included in this domain. The function's arguments are the list of vertex coordinates and * an attribute index (if appropriate). */ - static Domain ofFaces(const mfem::Mesh& mesh, std::function, int)> func); + static Domain ofFaces(const mesh_t& mesh, std::function, int)> func); /// @overload - static Domain ofFaces(const mfem::Mesh& mesh, std::function, int)> func); + static Domain ofFaces(const mesh_t& mesh, std::function, int)> func); /** * @brief create a domain from some subset of the elements (spatial dim == geometry dim) in an mfem::Mesh @@ -111,20 +115,20 @@ struct Domain { * included in this domain. The function's arguments are the list of vertex coordinates and * an attribute index (if appropriate). */ - static Domain ofElements(const mfem::Mesh& mesh, std::function, int)> func); + static Domain ofElements(const mesh_t& mesh, std::function, int)> func); /// @overload - static Domain ofElements(const mfem::Mesh& mesh, std::function, int)> func); + static Domain ofElements(const mesh_t& mesh, std::function, int)> func); /** * @brief create a domain from some subset of the boundary elements (spatial dim == geometry dim + 1) in an mfem::Mesh * @param mesh the entire mesh * @param func predicate function for determining which boundary elements will be included in this domain */ - static Domain ofBoundaryElements(const mfem::Mesh& mesh, std::function, int)> func); + static Domain ofBoundaryElements(const mesh_t& mesh, std::function, int)> func); /// @overload - static Domain ofBoundaryElements(const mfem::Mesh& mesh, std::function, int)> func); + static Domain ofBoundaryElements(const mesh_t& mesh, std::function, int)> func); /// @brief get elements by geometry type const std::vector& get(mfem::Geometry::Type geom) const @@ -151,10 +155,17 @@ struct Domain { exit(1); } + /** + * @brief returns how many elements of any type belong to this domain + */ int total_elements() const { return int(vertex_ids_.size() + edge_ids_.size() + tri_ids_.size() + quad_ids_.size() + tet_ids_.size() + hex_ids_.size()); } + /** + * @brief returns an array of the prefix sum of element counts belonging to this domain. + * Primarily intended to be used in mfem::BlockVector::Update(double * data, mfem::Array bOffsets); + */ mfem::Array bOffsets() const { mfem::Array offsets(mfem::Geometry::NUM_GEOMETRIES + 1); @@ -173,10 +184,10 @@ struct Domain { } /// @brief get mfem degree of freedom list for a given FiniteElementSpace - mfem::Array dof_list(mfem::FiniteElementSpace* fes) const; + mfem::Array dof_list(const fes_t* fes) const; /// @brief TODO - void insert_restriction(const mfem::FiniteElementSpace * fes, FunctionSpace space); + void insert_restriction(const fes_t * fes, FunctionSpace space); /// @brief TODO const BlockElementRestriction & get_restriction(FunctionSpace space); @@ -184,13 +195,13 @@ struct Domain { }; /// @brief constructs a domain from all the elements in a mesh -Domain EntireDomain(const mfem::Mesh& mesh); +Domain EntireDomain(const mesh_t& mesh); /// @brief constructs a domain from all the boundary elements in a mesh -Domain EntireBoundary(const mfem::Mesh& mesh); +Domain EntireBoundary(const mesh_t& mesh); /// @brief constructs a domain from all the interior face elements in a mesh -Domain InteriorFaces(const mfem::Mesh& mesh); +Domain InteriorFaces(const mesh_t& mesh); /// @brief create a new domain that is the union of `a` and `b` Domain operator|(const Domain& a, const Domain& b); @@ -225,6 +236,9 @@ inline std::array geometry_counts(cons return counts; } +/** + * @brief convenience function for computing the arithmetic mean of some list of vectors + */ template inline tensor average(std::vector >& positions) { diff --git a/src/serac/numerics/functional/element_restriction.cpp b/src/serac/numerics/functional/element_restriction.cpp index 73e1c8e47e..ac61957fde 100644 --- a/src/serac/numerics/functional/element_restriction.cpp +++ b/src/serac/numerics/functional/element_restriction.cpp @@ -4,6 +4,9 @@ #include "serac/numerics/functional/geometry.hpp" +// TODO REMOVE AFTER DEBUGGING +#include "serac/infrastructure/mpi_fstream.hpp" + std::vector > lexicographic_permutations(int p) { // p == 0 is admissible for L2 spaces, but lexicographic permutations @@ -213,7 +216,7 @@ std::vector > geom_local_face_dofs(int p) return output; } -axom::Array GetElementRestriction(const mfem::FiniteElementSpace* fes, +axom::Array GetElementRestriction(const serac::fes_t* fes, mfem::Geometry::Type geom) { std::vector elem_dofs{}; @@ -276,7 +279,7 @@ axom::Array GetElementRestriction(const mfem::F } } -axom::Array GetElementDofs(const mfem::FiniteElementSpace* fes, +axom::Array GetElementDofs(const serac::fes_t* fes, mfem::Geometry::Type geom, const std::vector< int > & mfem_elem_ids) @@ -344,7 +347,7 @@ axom::Array GetElementDofs(const mfem::FiniteEl } } -axom::Array GetFaceDofs(const mfem::FiniteElementSpace* fes, +axom::Array GetFaceDofs(const serac::fes_t* fes, mfem::Geometry::Type face_geom, FaceType type) { std::vector face_dofs; @@ -459,7 +462,7 @@ axom::Array GetFaceDofs(const mfem::FiniteEleme } } -axom::Array GetFaceDofs(const mfem::FiniteElementSpace* fes, +axom::Array GetFaceDofs(const serac::fes_t* fes, mfem::Geometry::Type face_geom, const std::vector & mfem_face_ids) { @@ -473,6 +476,11 @@ axom::Array GetFaceDofs(const mfem::FiniteEleme std::vector > local_face_dofs = geom_local_face_dofs(p); std::vector > lex_perm = lexicographic_permutations(p); + //int components_per_node = fes->GetVDim(); + //bool by_vdim = fes->GetOrdering() == mfem::Ordering::byVDIM; + //fes->ExchangeFaceNbrData(); + //int LSize = fes->GetProlongationMatrix()->Height(); + uint64_t n = 0; for (int f : mfem_face_ids) { @@ -481,13 +489,34 @@ axom::Array GetFaceDofs(const mfem::FiniteEleme SLIC_ERROR("encountered incorrect face geometry type"); } - // mfem doesn't provide this connectivity info for DG spaces directly, + //mfem::Mesh::FaceInformation info = mesh->GetFaceInformation(f); + + // mfem doesn't provide this connectivity info for DG spaces directly (?), // so we have to get at it indirectly in several steps: if (isDG(*fes)) { + // 1. find the element(s) that this face belongs to mfem::Array elem_ids; face_to_elem->GetRow(f, elem_ids); +#if 0 + mpi::out << f << " elem ids: "; + for (auto elem : elem_ids) { + mpi::out << elem << " "; + } + mpi::out << std::endl; + + mfem::Array test_dofs; + fes->GetFaceDofs(f, test_dofs); + + mpi::out << " dofs (sz = " << test_dofs.Size() << "): "; + for (int z = 0; z < test_dofs.Size(); z++) { + mpi::out << test_dofs[z] << " "; + } + mpi::out << std::endl; + mpi::out << std::endl; +#endif + for (auto elem : elem_ids) { // 2a. get the list of faces (and their orientations) that belong to that element ... mfem::Array elem_side_ids, orientations; @@ -532,6 +561,35 @@ axom::Array GetFaceDofs(const mfem::FiniteEleme face_dofs.push_back(uint64_t(elem_dof_ids[local_face_dofs[uint32_t(elem_geom)](i, k)])); } +#if 0 + // (5. optional) add remaining dofs that were omitted on shared faces + if (info.IsShared()) { + + mfem::Array shared_elem_vdof_ids; + + // on my processor + // VVVVVVV + // dofs for the face [0 1 3 4 | 5 8 12 13] + // ^^^^^^^^^ + // on the other processor + int other_element_id = info.element[1].index; + fes->GetFaceNbrElementVDofs(other_element_id, shared_elem_vdof_ids); // indices into vector from FaceNbrData + + mpi::out << " this face is also shared so it has additional dofs: "; + int dofs_per_face = shared_elem_vdof_ids.Size() / components_per_node; + int stride = (by_vdim) ? components_per_node : 1; + + // sam: is this right? + // byVDIM == x y z x y z x y z x y z + // byNODES == x x x x y y y y z z z z + for (int k = 0; k < dofs_per_face; k++) { + face_dofs.push_back(uint64_t(fes->VDofToDof(shared_elem_vdof_ids[k * stride]) + LSize)); + mpi::out << face_dofs.back().index() << " "; + } + mpi::out << std::endl; + } +#endif + } // H1 and Hcurl spaces are more straight-forward, since @@ -573,7 +631,7 @@ axom::Array GetFaceDofs(const mfem::FiniteEleme namespace serac { -ElementRestriction::ElementRestriction(const mfem::FiniteElementSpace* fes, mfem::Geometry::Type elem_geom, const std::vector< int > & elem_ids) { +ElementRestriction::ElementRestriction(const fes_t* fes, mfem::Geometry::Type elem_geom, const std::vector< int > & elem_ids) { int sdim = fes->GetMesh()->Dimension(); int gdim = dimension_of(elem_geom); @@ -649,7 +707,7 @@ void ElementRestriction::ScatterAdd(const mfem::Vector& E_vector, mfem::Vector& //////////////////////////////////////////////////////////////////////// /// create a BlockElementRestriction for the elements in a given domain -BlockElementRestriction::BlockElementRestriction(const mfem::FiniteElementSpace* fes, const Domain & domain) { +BlockElementRestriction::BlockElementRestriction(const fes_t* fes, const Domain & domain) { // TODO: changing the mfem_XXX_ids arrays to mfem_ids[XXX] would simplify this if (domain.mfem_edge_ids_.size() > 0) { diff --git a/src/serac/numerics/functional/element_restriction.hpp b/src/serac/numerics/functional/element_restriction.hpp index ff96e7cd9d..1ebb891b7c 100644 --- a/src/serac/numerics/functional/element_restriction.hpp +++ b/src/serac/numerics/functional/element_restriction.hpp @@ -7,6 +7,8 @@ #include "geometry.hpp" #include "domain.hpp" +#include "serac/numerics/functional/typedefs.hpp" + inline bool isH1(const mfem::FiniteElementSpace& fes) { return (fes.FEColl()->GetContType() == mfem::FiniteElementCollection::CONTINUOUS); @@ -150,7 +152,8 @@ struct ElementRestriction { /// default ctor leaves this object uninitialized ElementRestriction() {} - ElementRestriction(const mfem::FiniteElementSpace* fes, mfem::Geometry::Type elem_geom, const std::vector & domain_elements); + /// ctor from a list of elements (e.g. from a serac::Domain) + ElementRestriction(const fes_t* fes, mfem::Geometry::Type elem_geom, const std::vector & domain_elements); /// the size of the "E-vector" associated with this restriction operator uint64_t ESize() const; @@ -213,7 +216,7 @@ struct BlockElementRestriction { BlockElementRestriction() {} /// create a BlockElementRestriction for the elements in a given domain - BlockElementRestriction(const mfem::FiniteElementSpace* fes, const Domain & domain); + BlockElementRestriction(const fes_t* fes, const Domain & domain); /// the size of the "E-vector" associated with this restriction operator uint64_t ESize() const; @@ -242,13 +245,13 @@ struct BlockElementRestriction { * @param fes the finite element space containing the dof information * @param geom the kind of element geometry */ -axom::Array GetElementDofs(const mfem::FiniteElementSpace* fes, mfem::Geometry::Type geom); +axom::Array GetElementDofs(const serac::fes_t* fes, mfem::Geometry::Type geom); /** - * @brief Get the list of dofs for each face element (of the specified geometry) from the mfem::FiniteElementSpace + * @brief Get the list of dofs for each face element (of the specified geometry) from the fes_t * * @param fes the finite element space containing the dof information * @param geom the kind of element geometry * @param type whether the face is of interior or boundary type */ -axom::Array GetFaceDofs(const mfem::FiniteElementSpace* fes, mfem::Geometry::Type face_geom, FaceType type); +axom::Array GetFaceDofs(const serac::fes_t* fes, mfem::Geometry::Type face_geom, FaceType type); diff --git a/src/serac/numerics/functional/finite_element.hpp b/src/serac/numerics/functional/finite_element.hpp index 5eded85753..3d4c6f4e27 100644 --- a/src/serac/numerics/functional/finite_element.hpp +++ b/src/serac/numerics/functional/finite_element.hpp @@ -232,15 +232,27 @@ struct QOI { static constexpr Family family = Family::QOI; ///< the family of the basis functions }; -struct FunctionSpace { - Family family; - int order; - int components; +/** + * @brief a small POD class for tracking function space metadata + */ +struct FunctionSpace { + Family family; ///< either H1, Hcurl, L2 + int order; ///< polynomial order + int components; ///< how many values are stored at each node + + /** + * @brief return the data contained in this struct as a tuple + * @note the main point of this conversion is to take advantage of std::tuple's automatic + * lexicographic-ordering comparison operators + */ std::tuple as_tuple() const { return std::tuple(int(family), order, components); } + /** + * @brief defines an ordering over FunctionSpaces, to enable use in containers like std::map + */ bool operator<(FunctionSpace other) const { return this->as_tuple() < other.as_tuple(); } diff --git a/src/serac/numerics/functional/functional.hpp b/src/serac/numerics/functional/functional.hpp index 0c0bad3e61..85def6b9ce 100644 --- a/src/serac/numerics/functional/functional.hpp +++ b/src/serac/numerics/functional/functional.hpp @@ -444,7 +444,36 @@ class Functional { // get the values for each local processor for (uint32_t i = 0; i < num_trial_spaces; i++) { + #if 0 + if (trial_function_spaces_[i].family == Family::L2) { + + // make a PGF on the fly + // TODO: don't allocate/deallocate this data every invocation + mfem::ParGridFunction X; + X.MakeRef(trial_space_[i], input_L_[i].GetData()); + + // call exchange face nbr + X.ExchangeFaceNbrData(); + + // copy input_L[i] and facenbrdata [i] into a common array like: + // first part second part + // [ --- L --- | --- FND --- ] + mfem::Vector first_part; + first_part.MakeRef(input_L_[i], 0); + P_trial_[i]->Mult(*input_T[i], first_part); + + mfem::Vector second_part; + second_part.MakeRef(input_L_[i], first_part.Size()); + second_part = X.FaceNbrData(); + + } else { + + P_trial_[i]->Mult(*input_T[i], input_L_[i]); + + } + #else P_trial_[i]->Mult(*input_T[i], input_L_[i]); + #endif } output_L_ = 0.0; @@ -453,10 +482,12 @@ class Functional { Domain & dom = integral.domain_; const serac::BlockElementRestriction & G_test = dom.get_restriction(test_function_space_); + mpi::out << "G_test.ESize() " << G_test.ESize() << std::endl; for (auto i : integral.active_trial_spaces_) { const serac::BlockElementRestriction & G_trial = dom.get_restriction(trial_function_spaces_[i]); input_E_buffer_[i].SetSize(int(G_trial.ESize())); + mpi::out << "G_trial.ESize() " << G_trial.ESize() << std::endl; input_E_[i].Update(input_E_buffer_[i], G_trial.bOffsets()); G_trial.Gather(input_L_[i], input_E_[i]); } diff --git a/src/serac/numerics/functional/functional_qoi.inl b/src/serac/numerics/functional/functional_qoi.inl index 4a0476711f..b291816622 100644 --- a/src/serac/numerics/functional/functional_qoi.inl +++ b/src/serac/numerics/functional/functional_qoi.inl @@ -159,7 +159,7 @@ public: * @tparam dim The dimension of the boundary element (1 for line, 2 for quad, etc) * @tparam lambda the type of the integrand functor: must implement operator() with an appropriate function signature * @param[in] integrand The user-provided quadrature function, see @p Integral - * @param[in] mesh The domain on which to evaluate the integral + * @param[in] domain which elements make up the domain of integration * * @brief Adds a boundary integral term to the Functional object * diff --git a/src/serac/numerics/functional/geometric_factors.cpp b/src/serac/numerics/functional/geometric_factors.cpp index 152af877b0..c00f1645e9 100644 --- a/src/serac/numerics/functional/geometric_factors.cpp +++ b/src/serac/numerics/functional/geometric_factors.cpp @@ -62,17 +62,17 @@ void compute_geometric_factors(mfem::Vector& positions_q, mfem::Vector& jacobian GeometricFactors::GeometricFactors(const Domain& domain, int q, mfem::Geometry::Type geom) { - auto* nodes = domain.mesh_.GetNodes(); - auto* fes = nodes->FESpace(); + const mfem::ParGridFunction* nodes = static_cast< const mfem::ParGridFunction * >(domain.mesh_.GetNodes()); + mfem::ParFiniteElementSpace * pfes = nodes->ParFESpace(); const std::vector & element_ids = domain.get_mfem_ids(geom); - auto restriction = serac::ElementRestriction(fes, geom, element_ids); + auto restriction = serac::ElementRestriction(pfes, geom, element_ids); mfem::Vector X_e(int(restriction.ESize())); restriction.Gather(*nodes, X_e); // assumes all elements are the same order - int p = fes->GetElementOrder(0); + int p = pfes->GetElementOrder(0); int spatial_dim = domain.mesh_.SpaceDimension(); int geometry_dim = dimension_of(geom); diff --git a/src/serac/numerics/functional/integral.hpp b/src/serac/numerics/functional/integral.hpp index 7827ab5773..f5759cd6c2 100644 --- a/src/serac/numerics/functional/integral.hpp +++ b/src/serac/numerics/functional/integral.hpp @@ -19,6 +19,9 @@ #include "serac/numerics/functional/interior_face_integral_kernels.hpp" #include "serac/numerics/functional/differentiate_wrt.hpp" +// TODO REMOVE AFTER DEBUGGING +#include "serac/infrastructure/mpi_fstream.hpp" + namespace serac { /// @brief a class for representing a Integral calculations and their derivatives @@ -74,8 +77,10 @@ struct Integral { (with_AD) ? evaluation_with_AD_[functional_to_integral_index_.at(differentiation_index)] : evaluation_; for (auto& [geometry, func] : kernels) { std::vector inputs(active_trial_spaces_.size()); + mpi::out << geometry << std::endl; for (std::size_t i = 0; i < active_trial_spaces_.size(); i++) { inputs[i] = input_E[uint32_t(active_trial_spaces_[i])].GetBlock(geometry).Read(); + mpi::out << "input_E[" << i << "].Size(): " << input_E[uint32_t(active_trial_spaces_[i])].GetBlock(geometry).Size() << std::endl; } func(t, inputs, output_E.GetBlock(geometry).ReadWrite(), update_state); } @@ -84,9 +89,9 @@ struct Integral { /** * @brief evaluate the jacobian(with respect to some trial space)-vector product of this integral * - * @param input_E a block vector (block index corresponds to the element geometry) of a specific trial space element + * @param dinput_E a block vector (block index corresponds to the element geometry) of a specific trial space element * values - * @param output_E a block vector (block index corresponds to the element geometry) of the output values for each + * @param doutput_E a block vector (block index corresponds to the element geometry) of the output values for each * element. * @param differentiation_index a non-negative value indicates directional derivative with respect to the trial space * with that index. diff --git a/src/serac/numerics/functional/interior_face_integral_kernels.hpp b/src/serac/numerics/functional/interior_face_integral_kernels.hpp index d3567ce87a..4b5667276f 100644 --- a/src/serac/numerics/functional/interior_face_integral_kernels.hpp +++ b/src/serac/numerics/functional/interior_face_integral_kernels.hpp @@ -11,6 +11,9 @@ #include "serac/numerics/functional/quadrature_data.hpp" #include "serac/numerics/functional/differentiate_wrt.hpp" +// TODO REMOVE AFTER DEBUGGING +#include "serac/infrastructure/mpi_fstream.hpp" + namespace serac { namespace interior_face_integral { @@ -164,8 +167,13 @@ void evaluation_kernel_impl(trial_element_type trial_elements, test_element, dou [[maybe_unused]] tuple u = { reinterpret_cast(trial_elements))::dof_type_if*>(inputs[indices])...}; + ((mpi::out << "sizeof(dof_type_if) / sizeof(double): " << sizeof(decltype(get(u)[0])) / sizeof(double) << std::endl), ...); + // for each element in the domain for (uint32_t e = 0; e < num_elements; e++) { + + mpi::out << "e: " << e << " / " << num_elements << std::endl; + // load the jacobians and positions for each quadrature point in this element auto J_e = J[e]; auto x_e = x[e]; diff --git a/src/serac/numerics/functional/tests/CMakeLists.txt b/src/serac/numerics/functional/tests/CMakeLists.txt index bd2bc19cb3..ff8ce856d1 100644 --- a/src/serac/numerics/functional/tests/CMakeLists.txt +++ b/src/serac/numerics/functional/tests/CMakeLists.txt @@ -21,6 +21,7 @@ set(functional_serial_test_sources element_restriction_tests.cpp dg_restriction_operators.cpp bug_boundary_qoi.cpp + bug_residual.cpp domain_tests.cpp geometric_factors_tests.cpp hcurl_unit_tests.cpp diff --git a/src/serac/numerics/functional/tests/bug_residual.cpp b/src/serac/numerics/functional/tests/bug_residual.cpp new file mode 100644 index 0000000000..9d48398e98 --- /dev/null +++ b/src/serac/numerics/functional/tests/bug_residual.cpp @@ -0,0 +1,105 @@ +// Copyright (c) 2019-2023, Lawrence Livermore National Security, LLC and +// other Serac Project Developers. See the top-level LICENSE file for +// details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include +#include + +#include "axom/slic/core/SimpleLogger.hpp" +#include +#include "mfem.hpp" + +#include "serac/serac_config.hpp" +#include "serac/mesh/mesh_utils_base.hpp" +#include "serac/numerics/functional/functional.hpp" +#include "serac/numerics/functional/shape_aware_functional.hpp" +#include "serac/numerics/functional/tensor.hpp" +#include "serac/infrastructure/profiling.hpp" + +#include "serac/numerics/functional/tests/check_gradient.hpp" + +using namespace serac; +using namespace serac::profiling; + +double t = 0.0; + +int num_procs, myid; + +#define L2_SCALAR_SPACE + +// this is an attempt to reproduce an error message described by Mike +void test() +{ + constexpr int p = 1; + constexpr int dim = 2; + +#ifdef L2_SCALAR_SPACE + using scalar_space = serac::L2; +#else + using scalar_space = serac::H1; +#endif + using vector_space = serac::H1; + + std::string meshfile = SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"; + auto pmesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 1); + + auto L2fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); + mfem::ParFiniteElementSpace L2_fespace(pmesh.get(), &L2fec, 1, serac::ordering); + +#ifdef L2_SCALAR_SPACE + auto scalar_fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); +#else + auto scalar_fec = mfem::H1_FECollection(p, dim); +#endif + mfem::ParFiniteElementSpace scalar_fespace(pmesh.get(), &scalar_fec, 1, serac::ordering); + + auto vector_fec = mfem::H1_FECollection(p, dim); + mfem::ParFiniteElementSpace vector_fespace(pmesh.get(), &vector_fec, dim, serac::ordering); + + Domain whole_domain = EntireDomain(*pmesh); + + Functional< scalar_space(scalar_space, scalar_space, vector_space, vector_space) > residual( + &scalar_fespace, + {&scalar_fespace, &scalar_fespace, &vector_fespace, &vector_fespace} + ); + + residual.AddDomainIntegral(serac::Dimension{}, serac::DependsOn<0,1,2,3>{}, + [=](double time, auto /*X*/, auto Rho, auto Rho_dot, auto U0, auto UF) { + auto U = UF * time + U0 * (1.0 - time); + auto dx_dX = get(U) + Identity(); + auto dX_dx = inv(dx_dX); + + auto v = get(UF) - get(U0); + auto v_X = get(UF) - get(U0); + auto v_dx = dot(v_X, dX_dx); + auto div_v = serac::tr(v_dx); + + auto rho_dot = get(Rho_dot); + auto rho = get(Rho); + + auto J = det(dx_dX); + auto JrhoV = J * dot(v, transpose(dX_dx)); + //std::string y = rho; + //std::string z = rho*JrhoV; + return serac::tuple{J*(rho_dot + rho*div_v), rho * JrhoV}; + }, + whole_domain + ); + +} + +int main(int argc, char* argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + MPI_Init(&argc, &argv); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + MPI_Comm_rank(MPI_COMM_WORLD, &myid); + + axom::slic::SimpleLogger logger; + + int result = RUN_ALL_TESTS(); + MPI_Finalize(); + return result; +} diff --git a/src/serac/numerics/functional/tests/check_gradient.hpp b/src/serac/numerics/functional/tests/check_gradient.hpp index 919db0363b..53e8f531b9 100644 --- a/src/serac/numerics/functional/tests/check_gradient.hpp +++ b/src/serac/numerics/functional/tests/check_gradient.hpp @@ -125,6 +125,7 @@ void check_gradient(serac::Functional& f, double t, mfem::Vector& U, do double e3 = df1_cd[0].DistanceTo(df_jvp1.GetData()) / denominator; double e4 = df1_cd[1].DistanceTo(df_jvp1.GetData()) / denominator; EXPECT_TRUE((fabs(e3 / e4 - 4.0) < 0.1) || fmin(e3, e4) < 1.0e-9); + } template diff --git a/src/serac/numerics/functional/tests/cuda_sandbox.cu b/src/serac/numerics/functional/tests/cuda_sandbox.cu new file mode 100644 index 0000000000..aae7098ad8 --- /dev/null +++ b/src/serac/numerics/functional/tests/cuda_sandbox.cu @@ -0,0 +1,212 @@ +#include + +#include "serac/numerics/functional/tensor.hpp" + +namespace serac { + +enum class Family { + H1, + Hcurl, + Hdiv, + DG +}; + +template < mfem::Geometry::Type g, Family f > +struct FiniteElement; + +template <> +struct FiniteElement< mfem::Geometry::TRIANGLE, Family::H1 > { + + using source_type = double; + using flux_type = vec2; + + static constexpr int dim = 2; + + uint32_t num_nodes() const { return ((p + 1) * (p + 2)) / 2; } + + constexpr double shape_function(vec2 xi, uint32_t i) const { + if (p == 1) { + if (i == 0) return 1.0 - xi[0] - xi[1]; + if (i == 1) return xi[0]; + if (i == 2) return xi[1]; + } + if (p == 2) { + if (i == 0) return (-1+xi[0]+xi[1])*(-1+2*xi[0]+2*xi[1]); + if (i == 1) return -4*xi[0]*(-1+xi[0]+xi[1]); + if (i == 2) return xi[0]*(-1+2*xi[0]); + if (i == 3) return -4*xi[1]*(-1+xi[0]+xi[1]); + if (i == 4) return 4*xi[0]*xi[1]; + if (i == 5) return xi[1]*(-1+2*xi[1]); + } + if (p == 3) { + double sqrt5 = 2.23606797749978981; + if (i == 0) return -((-1+xi[0]+xi[1])*(1+5*xi[0]*xi[0]+5*(-1+xi[1])*xi[1]+xi[0]*(-5+11*xi[1]))); + if (i == 1) return (5*xi[0]*(-1+xi[0]+xi[1])*(-1-sqrt5+2*sqrt5*xi[0]+(3+sqrt5)*xi[1]))/2.0; + if (i == 2) return (-5*xi[0]*(-1+xi[0]+xi[1])*(1-sqrt5+2*sqrt5*xi[0]+(-3+sqrt5)*xi[1]))/2.0; + if (i == 3) return xi[0]*(1+5*xi[0]*xi[0]+xi[1]-xi[1]*xi[1]-xi[0]*(5+xi[1])); + if (i == 4) return (5*xi[1]*(-1+xi[0]+xi[1])*(-1-sqrt5+(3+sqrt5)*xi[0]+2*sqrt5*xi[1]))/2.0; + if (i == 5) return -27*xi[0]*xi[1]*(-1+xi[0]+xi[1]); + if (i == 6) return (5*xi[0]*xi[1]*(-2+(3+sqrt5)*xi[0]-(-3+sqrt5)*xi[1]))/2.; + if (i == 7) return (5*xi[1]*(-1+xi[0]+xi[1])*(5-3*sqrt5+2*(-5+2*sqrt5)*xi[0]+5*(-1+sqrt5)*xi[1]))/(-5+sqrt5); + if (i == 8) return (-5*xi[0]*xi[1]*(2+(-3+sqrt5)*xi[0]-(3+sqrt5)*xi[1]))/2.; + if (i == 9) return xi[1]*(1+xi[0]-xi[0]*xi[0]-xi[0]*xi[1]+5*(-1+xi[1])*xi[1]); + } + + return -1.0; + } + + vec2 shape_function_gradient(vec2 xi, uint32_t i) const { + // expressions generated symbolically by mathematica + if (p == 1) { + if (i == 0) return {-1.0, -1.0}; + if (i == 1) return { 1.0, 0.0}; + if (i == 2) return { 0.0, 1.0}; + } + if (p == 2) { + if (i == 0) return {-3+4*xi[0]+4*xi[1], -3+4*xi[0]+4*xi[1]}; + if (i == 1) return {-4*(-1+2*xi[0]+xi[1]), -4*xi[0]}; + if (i == 2) return {-1+4*xi[0], 0}; + if (i == 3) return {-4*xi[1], -4*(-1+xi[0]+2*xi[1])}; + if (i == 4) return {4*xi[1], 4*xi[0]}; + if (i == 5) return {0, -1+4*xi[1]}; + } + if (p == 3) { + double sqrt5 = 2.23606797749978981; + if (i == 0) return {-6-15*xi[0]*xi[0]+4*xi[0]*(5-8*xi[1])+(21-16*xi[1])*xi[1], -6-16*xi[0]*xi[0]+xi[0]*(21-32*xi[1])+5*(4-3*xi[1])*xi[1]}; + if (i == 1) return {(5*(6*sqrt5*xi[0]*xi[0]+xi[0]*(-2-6*sqrt5+6*(1+sqrt5)*xi[1])+(-1+xi[1])*(-1-sqrt5+(3+sqrt5)*xi[1])))/2., (5*xi[0]*(-2*(2+sqrt5)+3*(1+sqrt5)*xi[0]+2*(3+sqrt5)*xi[1]))/2.}; + if (i == 2) return {(-5*(6*sqrt5*xi[0]*xi[0]+(-1+xi[1])*(1-sqrt5+(-3+sqrt5)*xi[1])+xi[0]*(2-6*sqrt5+6*(-1+sqrt5)*xi[1])))/2., (-5*xi[0]*(4-2*sqrt5+3*(-1+sqrt5)*xi[0]+2*(-3+sqrt5)*xi[1]))/2.}; + if (i == 3) return {1+15*xi[0]*xi[0]+xi[1]-xi[1]*xi[1]-2*xi[0]*(5+xi[1]), -(xi[0]*(-1+xi[0]+2*xi[1]))}; + if (i == 4) return {(5*xi[1]*(-2*(2+sqrt5)+2*(3+sqrt5)*xi[0]+3*(1+sqrt5)*xi[1]))/2., (5*(1+sqrt5-2*(2+sqrt5)*xi[0]+(3+sqrt5)*xi[0]*xi[0]+6*(1+sqrt5)*xi[0]*xi[1]+2*xi[1]*(-1-3*sqrt5+3*sqrt5*xi[1])))/2.}; + if (i == 5) return {-27*xi[1]*(-1+2*xi[0]+xi[1]), -27*xi[0]*(-1+xi[0]+2*xi[1])}; + if (i == 6) return {(-5*xi[1]*(2-2*(3+sqrt5)*xi[0]+(-3+sqrt5)*xi[1]))/2., (5*xi[0]*(-2+(3+sqrt5)*xi[0]-2*(-3+sqrt5)*xi[1]))/2.}; + if (i == 7) return {(-5*xi[1]*(4-2*sqrt5+2*(-3+sqrt5)*xi[0]+3*(-1+sqrt5)*xi[1]))/2., (-5*(-1+sqrt5+(-3+sqrt5)*xi[0]*xi[0]+2*xi[1]*(1-3*sqrt5+3*sqrt5*xi[1])+xi[0]*(4-2*sqrt5+6*(-1+sqrt5)*xi[1])))/2.}; + if (i == 8) return {(5*xi[1]*(-2-2*(-3+sqrt5)*xi[0]+(3+sqrt5)*xi[1]))/2., (-5*xi[0]*(2+(-3+sqrt5)*xi[0]-2*(3+sqrt5)*xi[1]))/2.}; + if (i == 9) return {-(xi[1]*(-1+2*xi[0]+xi[1])), 1+xi[0]-xi[0]*xi[0]-2*(5+xi[0])*xi[1]+15*xi[1]*xi[1]}; + } + + return {}; + } + +#if 0 + nd::array< double, 2 > evaluate_shape_functions(nd::view xi) const { + uint32_t q = xi.shape[0]; + nd::array shape_fns({q, num_nodes()}); + for (int i = 0; i < q; i++) { + GaussLobattoInterpolationTriangle(&xi(i, 0), p, &shape_fns(i, 0)); + } + return shape_fns; + } + + nd::array< double, 3 > evaluate_shape_function_gradients(nd::view xi) const { + uint32_t q = xi.shape[0]; + nd::array shape_fn_grads({q, num_nodes(), dim}); + for (int i = 0; i < q; i++) { + GaussLobattoInterpolationDerivativeTriangle(&xi(i, 0), p, &shape_fn_grads(i, 0, 0)); + } + return shape_fn_grads; + } + + void interpolate(nd::view values_q, nd::view values_e, nd::view shape_fns) const { + int nnodes = num_nodes(); + int nqpts = values_q.shape[0]; + + for (int q = 0; q < nqpts; q++) { + double sum = 0.0; + for (int i = 0; i < nnodes; i++) { + sum += shape_fns(q, i) * values_e(i); + } + values_q(q) = sum; + } + } + + void gradient(nd::view gradients_q, nd::view values_e, nd::view shape_fn_grads) const { + int nnodes = num_nodes(); + int nqpts = gradients_q.shape[0]; + for (int j = 0; j < nqpts; j++) { + double sum[2]{}; + for (int i = 0; i < nnodes; i++) { + sum[0] += values_e(i) * shape_fn_grads(j, i, 0); + sum[1] += values_e(i) * shape_fn_grads(j, i, 1); + } + gradients_q(j, 0) = sum[0]; + gradients_q(j, 1) = sum[1]; + + } + } + + nd::array< double, 2 > evaluate_weighted_shape_functions(nd::view xi, + nd::view weights) const { + uint32_t nnodes = num_nodes(); + uint32_t q = xi.shape[0]; + nd::array shape_fns({q, nnodes}); + for (uint32_t i = 0; i < q; i++) { + GaussLobattoInterpolationTriangle(&xi(i, 0), p, &shape_fns(i, 0)); + for (uint32_t j = 0; j < nnodes; j++) { + shape_fns(i, j) = shape_fns(i, j) * weights(i); + } + } + return shape_fns; + } + + void integrate_source(nd::view residual_e, nd::view source_q, nd::view shape_fn) const { + int nnodes = num_nodes(); + int nqpts = source_q.shape[0]; + + for (int i = 0; i < nnodes; i++) { + double sum = 0.0; + for (int q = 0; q < nqpts; q++) { + sum += shape_fn(q, i) * source_q(q); + } + residual_e(i) = sum; + } + } + + nd::array< double, 3 > evaluate_weighted_shape_function_gradients(nd::view xi, + nd::view weights) const { + uint32_t nnodes = num_nodes(); + uint32_t q = xi.shape[0]; + nd::array shape_fn_grads({q, nnodes, dim}); + for (uint32_t i = 0; i < q; i++) { + GaussLobattoInterpolationDerivativeTriangle(&xi(i, 0), p, &shape_fn_grads(i, 0, 0)); + for (uint32_t j = 0; j < nnodes; j++) { + shape_fn_grads(i, j, 0) = shape_fn_grads(i, j, 0) * weights(i); + shape_fn_grads(i, j, 1) = shape_fn_grads(i, j, 1) * weights(i); + } + } + + return shape_fn_grads; + } + + void integrate_flux(nd::view residual_e, nd::view flux_q, nd::view shape_fn_grads) const { + int nnodes = num_nodes(); + int nqpts = flux_q.shape[0]; + + for (int i = 0; i < nnodes; i++) { + double sum = 0.0; + for (int q = 0; q < nqpts; q++) { + for (int d = 0; d < dim; d++) { + sum += shape_fn_grads(q, i, d) * flux_q(q)[d]; + } + } + residual_e(i) = sum; + } + } +#endif + + int p; + +}; + +} + +//#include "elements/h1_edge.hpp" +//#include "elements/h1_triangle.hpp" +//#include "elements/h1_quadrilateral.hpp" +//#include "elements/h1_tetrahedron.hpp" +//#include "elements/h1_hexahedron.hpp" + +__global__ void kernel() {} + +int main() { + kernel<<<1,1>>>(); +} diff --git a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp index dde6e1e80d..46d208868c 100644 --- a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp +++ b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp @@ -24,13 +24,13 @@ int possible_permutations(mfem::Geometry::Type geom) { return -1; } -template < int n > +template < uint32_t n > std::array< int, n > apply_permutation(const int (&arr)[n], const int (&p)[n]) { - std::array permuted_arr{}; - for (int i = 0; i < n; i++) { - permuted_arr[i] = arr[p[i]]; - } - return permuted_arr; + std::array permuted_arr{}; + for (uint32_t i = 0; i < n; i++) { + permuted_arr[i] = arr[p[i]]; + } + return permuted_arr; } mfem::Mesh generate_permuted_mesh(mfem::Geometry::Type geom, int i) { diff --git a/src/serac/numerics/functional/tests/functional_basic_dg.cpp b/src/serac/numerics/functional/tests/functional_basic_dg.cpp index 80ae66a763..632bb7528b 100644 --- a/src/serac/numerics/functional/tests/functional_basic_dg.cpp +++ b/src/serac/numerics/functional/tests/functional_basic_dg.cpp @@ -24,12 +24,85 @@ using namespace serac; using namespace serac::profiling; +// +// patch2D_tris_and_quads.mesh +// +// o---------------------o +// | * 0 * | +// | * * | +// | o ----- o | +// | 0 | 0 . ^ | | +// | | * 1 | 1 | +// | o ----- o | +// | * * | +// | * 1 * | +// o---------------------o +// + +// [ --- L --- | -- FND -- ] +// ^^ + +#if 0 +void ParNonlinearForm::Mult(const Vector &x, Vector &y) const +{ + NonlinearForm::Mult(x, y); // x --(P)--> aux1 --(A_local)--> aux2 + + if (fnfi.Size()) + { + MFEM_VERIFY(!NonlinearForm::ext, "Not implemented (extensions + faces"); + // Terms over shared interior faces in parallel. + ParFiniteElementSpace *pfes = ParFESpace(); + ParMesh *pmesh = pfes->GetParMesh(); + FaceElementTransformations *tr; + const FiniteElement *fe1, *fe2; + Array vdofs1, vdofs2; + Vector el_x, el_y; + + aux1.HostReadWrite(); + X.MakeRef(aux1, 0); // aux1 contains P.x + X.ExchangeFaceNbrData(); + const int n_shared_faces = pmesh->GetNSharedFaces(); + for (int i = 0; i < n_shared_faces; i++) + { + tr = pmesh->GetSharedFaceTransformations(i, true); + int Elem2NbrNo = tr->Elem2No - pmesh->GetNE(); + + fe1 = pfes->GetFE(tr->Elem1No); + fe2 = pfes->GetFaceNbrFE(Elem2NbrNo); + + pfes->GetElementVDofs(tr->Elem1No, vdofs1); + pfes->GetFaceNbrElementVDofs(Elem2NbrNo, vdofs2); + + el_x.SetSize(vdofs1.Size() + vdofs2.Size()); + X.GetSubVector(vdofs1, el_x.GetData()); + X.FaceNbrData().GetSubVector(vdofs2, el_x.GetData() + vdofs1.Size()); + + for (int k = 0; k < fnfi.Size(); k++) + { + fnfi[k]->AssembleFaceVector(*fe1, *fe2, *tr, el_x, el_y); + aux2.AddElementVector(vdofs1, el_y.GetData()); + } + } + } + + P->MultTranspose(aux2, y); + + const int N = ess_tdof_list.Size(); + const auto idx = ess_tdof_list.Read(); + auto Y_RW = y.ReadWrite(); + mfem::forall(N, [=] MFEM_HOST_DEVICE (int i) { Y_RW[idx[i]] = 0.0; }); +} +#endif + template void L2_test(std::string meshfile) { using test_space = L2; using trial_space = L2; + //int k = 0; + //while (k == 0); + auto mesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 0); auto fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); @@ -65,11 +138,15 @@ void L2_test(std::string meshfile) }, interior_faces); double t = 0.0; - check_gradient(residual, t, U); + + auto value = residual(t, U); + //check_gradient(residual, t, U); } TEST(basic, L2_test_tris_and_quads_linear) { L2_test<2, 1>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); } + +#if 0 TEST(basic, L2_test_tris_and_quads_quadratic) { L2_test<2, 2>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); } TEST(basic, L2_test_tets_linear) { L2_test<3, 1>(SERAC_REPO_DIR "/data/meshes/patch3D_tets.mesh"); } @@ -83,7 +160,7 @@ void L2_qoi_test(std::string meshfile) { using trial_space = L2; - auto mesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 0); + auto mesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 1); auto fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); mfem::ParFiniteElementSpace fespace(mesh.get(), &fec, dim, serac::ordering); @@ -139,7 +216,7 @@ void L2_scalar_valued_test(std::string meshfile) using trial_space_0 = L2

; using trial_space_1 = H1; - auto mesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 0); + auto mesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 1); auto L2fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); mfem::ParFiniteElementSpace fespace_0(mesh.get(), &L2fec, 1, serac::ordering); @@ -186,6 +263,7 @@ void L2_scalar_valued_test(std::string meshfile) TEST(basic, L2_mixed_scalar_test_tris_and_quads_linear) { L2_scalar_valued_test<2, 1>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); } TEST(basic, L2_mixed_scalar_test_tets_and_hexes_linear) { L2_scalar_valued_test<3, 1>(SERAC_REPO_DIR "/data/meshes/patch3D_tets_and_hexes.mesh"); } +#endif int main(int argc, char* argv[]) { diff --git a/src/serac/numerics/functional/typedefs.hpp b/src/serac/numerics/functional/typedefs.hpp new file mode 100644 index 0000000000..036b478bb1 --- /dev/null +++ b/src/serac/numerics/functional/typedefs.hpp @@ -0,0 +1,11 @@ +#pragma once + +namespace serac { + +// sam: this is a kludge-- it looks like the DG spaces need to use some interface defined ONLY on +// mfem::ParMesh / mfeme::ParFiniteElementSpace, but I'm not ready to pull the trigger on a big +// interface change like that, so these typedefs mark the parts that would need to eventually change +using mesh_t = mfem::Mesh; +using fes_t = mfem::FiniteElementSpace; + +} diff --git a/src/serac/physics/fit.hpp b/src/serac/physics/fit.hpp index 664175b59c..960411dcbd 100644 --- a/src/serac/physics/fit.hpp +++ b/src/serac/physics/fit.hpp @@ -58,7 +58,7 @@ FiniteElementState fit(std::integer_sequence, func f, mfem::ParMesh & /** * @brief determine field parameters to approximate the output of a user-provided q-function * @param[in] f the user-provided function to approximate - * @param[in] mesh the region over which to approximate the function f + * @param[in] pmesh the region over which to approximate the function f * @param[in] solution_fields [optional] any auxiliary field quantities needed to evaluate f * * @note: mesh is passed by non-const ref because mfem mutates the mesh when creating ParGridFunctions diff --git a/src/serac/physics/heat_transfer.hpp b/src/serac/physics/heat_transfer.hpp index 1207979763..30fe301efc 100644 --- a/src/serac/physics/heat_transfer.hpp +++ b/src/serac/physics/heat_transfer.hpp @@ -410,6 +410,7 @@ class HeatTransfer, std::integer_sequ * * @tparam MaterialType The thermal material type * @param material A material containing heat capacity and thermal flux evaluation information + * @param domain which elements in the mesh are described by the specified material * * @pre material must be a object that can be called with the following arguments: * 1. `tensor x` the spatial position of the material evaluation call @@ -466,8 +467,7 @@ class HeatTransfer, std::integer_sequ * * @tparam SourceType The type of the source function * @param source_function A source function for a prescribed thermal load - * @param optional_domain The domain over which the source is applied. If nothing is supplied the entire domain is - * used. + * @param domain The domain over which the source is applied. * * @pre source_function must be a object that can be called with the following arguments: * 1. `tensor x` the spatial coordinates for the quadrature point @@ -514,8 +514,7 @@ class HeatTransfer, std::integer_sequ * * @tparam FluxType The type of the thermal flux object * @param flux_function A function describing the flux applied to a boundary - * @param optional_domain The domain over which the flux is applied. If nothing is supplied the entire boundary is - * used. + * @param domain The domain over which the flux is applied * * @pre FluxType must be a object that can be called with the following arguments: * 1. `tensor x` the spatial coordinates for the quadrature point diff --git a/src/serac/physics/solid_mechanics.hpp b/src/serac/physics/solid_mechanics.hpp index 94ef817502..34d020fc45 100644 --- a/src/serac/physics/solid_mechanics.hpp +++ b/src/serac/physics/solid_mechanics.hpp @@ -815,6 +815,7 @@ class SolidMechanics, std::integer_se * @tparam active_parameters a list of indices, describing which parameters to pass to the q-function * @tparam StateType the type that contains the internal variables (if any) for q-function * @param qfunction a callable that returns a tuple of body-force and stress + * @param domain which elements should evaluate the provided qfunction * @param qdata the buffer of material internal variables at each quadrature point * * ~~~ {.cpp} @@ -1003,8 +1004,8 @@ class SolidMechanics, std::integer_se * * @tparam BodyForceType The type of the body force load * @param body_force A function describing the body force applied - * @param optional_domain The domain over which the body force is applied. If nothing is supplied the entire domain is - * used. + * @param domain which part of the mesh to apply the body force to + * * @pre body_force must be a object that can be called with the following arguments: * 1. `tensor x` the spatial coordinates for the quadrature point * 2. `double t` the time (note: time will be handled differently in the future) @@ -1037,8 +1038,8 @@ class SolidMechanics, std::integer_se * * @tparam TractionType The type of the traction load * @param traction_function A function describing the traction applied to a boundary - * @param optional_domain The domain over which the traction is applied. If nothing is supplied the entire boundary is - * used. + * @param domain The domain over which the traction is applied. + * * @pre TractionType must be a object that can be called with the following arguments: * 1. `tensor x` the spatial coordinates for the quadrature point * 2. `tensor n` the outward-facing unit normal for the quadrature point @@ -1081,8 +1082,8 @@ class SolidMechanics, std::integer_se * * @tparam PressureType The type of the pressure load * @param pressure_function A function describing the pressure applied to a boundary - * @param optional_domain The domain over which the pressure is applied. If nothing is supplied the entire boundary is - * used. + * @param domain The domain over which the pressure is applied. + * * @pre PressureType must be a object that can be called with the following arguments: * 1. `tensor x` the reference configuration spatial coordinates for the quadrature point * 2. `double t` the time (note: time will be handled differently in the future) @@ -1100,11 +1101,8 @@ class SolidMechanics, std::integer_se * @note This method must be called prior to completeSetup() */ template - void setPressure(DependsOn, PressureType pressure_function, - const std::optional& optional_domain = std::nullopt) + void setPressure(DependsOn, PressureType pressure_function, Domain &domain) { - Domain domain = (optional_domain) ? *optional_domain : EntireBoundary(mesh_); - residual_->AddBoundaryIntegral( Dimension{}, DependsOn<0, 1, active_parameters + NUM_STATE_VARS...>{}, [pressure_function](double t, auto X, auto displacement, auto /* acceleration */, auto... params) { @@ -1132,9 +1130,9 @@ class SolidMechanics, std::integer_se /// @overload template - void setPressure(PressureType pressure_function, const std::optional& optional_domain = std::nullopt) + void setPressure(PressureType pressure_function, Domain & domain) { - setPressure(DependsOn<>{}, pressure_function, optional_domain); + setPressure(DependsOn<>{}, pressure_function, domain); } /// @brief Build the quasi-static operator corresponding to the total Lagrangian formulation diff --git a/src/serac/physics/state/finite_element_vector.hpp b/src/serac/physics/state/finite_element_vector.hpp index a91cf7d41c..6de60c5151 100644 --- a/src/serac/physics/state/finite_element_vector.hpp +++ b/src/serac/physics/state/finite_element_vector.hpp @@ -61,10 +61,11 @@ class FiniteElementVector : public mfem::HypreParVector { * * @tparam FunctionSpace what kind of interpolating functions to use * @param mesh The mesh used to construct the finite element state + * @param f metadata describing the kind of function space to define * @param name The name of the new finite element state field */ template - FiniteElementVector(mfem::ParMesh& mesh, FunctionSpace, const std::string& name = "") : mesh_(mesh), name_(name) + FiniteElementVector(mfem::ParMesh& mesh, [[maybe_unused]] FunctionSpace f, const std::string& name = "") : mesh_(mesh), name_(name) { std::tie(space_, coll_) = serac::generateParFiniteElementSpace(&mesh); diff --git a/src/serac/physics/tests/solid_finite_diff.cpp b/src/serac/physics/tests/solid_finite_diff.cpp index 386bc23ffe..ceae8872f2 100644 --- a/src/serac/physics/tests/solid_finite_diff.cpp +++ b/src/serac/physics/tests/solid_finite_diff.cpp @@ -237,8 +237,8 @@ void finite_difference_shape_test(LoadingType load) solid_solver.setDisplacementBCs(ess_bdr, bc); solid_solver.setDisplacement(bc); - Domain top_face = Domain::ofBoundaryElements(pmesh, [](std::vector< vec2 > vertices, int attr){ - return average(vertices)[1] > 0.99; // select faces whose y-coordinates are at the top of the mesh + Domain top_face = Domain::ofBoundaryElements(pmesh, [](std::vector< vec2 > vertices, int /*attr*/){ + return average(vertices)[1] > 0.99; // select faces by y-coordinate }); if (load == LoadingType::BodyForce) { @@ -247,17 +247,15 @@ void finite_difference_shape_test(LoadingType load) solid_mechanics::ConstantBodyForce force{constant_force}; solid_solver.addBodyForce(force, whole_mesh); - - } else if (load == LoadingType::Pressure) { solid_solver.setPressure( - [](auto& X, double) { + [](auto /*X*/, double /*t*/) { return 0.1; }, top_face); } else if (load == LoadingType::Traction) { solid_solver.setTraction( - [](auto& X, auto, double) { + [](auto /*X*/, auto /*n*/, double /*t*/) { return vec2{0.01, 0.01}; }, top_face); diff --git a/src/serac/physics/tests/solid_statics_patch.cpp b/src/serac/physics/tests/solid_statics_patch.cpp index 1ec794afe4..1198c2a160 100644 --- a/src/serac/physics/tests/solid_statics_patch.cpp +++ b/src/serac/physics/tests/solid_statics_patch.cpp @@ -402,6 +402,7 @@ double pressure_error() solid_mechanics::NeoHookean mat{.density=1.0, .K=1.0, .G=1.0}; Domain material_block = EntireDomain(pmesh); + Domain boundary = EntireBoundary(pmesh); solid.setMaterial(mat, material_block); typename solid_mechanics::NeoHookean::State state; @@ -419,7 +420,7 @@ double pressure_error() // Set the pressure corresponding to 10% uniaxial strain solid.setPressure([pressure](auto&, double) { return pressure; - }); + }, boundary); // Define the essential boundary conditions corresponding to 10% uniaxial strain everywhere // except the pressure loaded surface diff --git a/src/serac/physics/thermomechanics.hpp b/src/serac/physics/thermomechanics.hpp index 83d2826c54..752c8be41c 100644 --- a/src/serac/physics/thermomechanics.hpp +++ b/src/serac/physics/thermomechanics.hpp @@ -352,6 +352,7 @@ class Thermomechanics : public BasePhysics { * @tparam MaterialType The thermomechanical material type * @tparam StateType The type that contains the internal variables for MaterialType * @param material A material that provides a function to evaluate stress, heat flux, density, and heat capacity + * @param domain The domain over which the source is applied. * @param qdata the buffer of material internal variables at each quadrature point * * @pre material must be a object that can be called with the following arguments: @@ -366,6 +367,8 @@ class Thermomechanics : public BasePhysics { * when doing direct evaluation. When differentiating with respect to one of the inputs, its stored * values will change to `dual` numbers rather than `double`. (e.g. `tensor` becomes * `tensor, 3>`) + * + * @param domain which elements in the mesh are described by the specified material * * @pre MaterialType must return a serac::tuple of Cauchy stress, volumetric heat capacity, internal heat source, * and thermal flux when operator() is called with the arguments listed above. From 7905f46abb16567375bd7eda0d03599cfc6015bb Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Thu, 21 Nov 2024 08:12:41 -0800 Subject: [PATCH 47/92] fix bug where face orientations were not be applied when they should have, update relevant tests --- .../functional/element_restriction.cpp | 6 +- .../functional/element_restriction.hpp | 3 + .../tests/dg_restriction_operators.cpp | 358 +++++++++++------- 3 files changed, 224 insertions(+), 143 deletions(-) diff --git a/src/serac/numerics/functional/element_restriction.cpp b/src/serac/numerics/functional/element_restriction.cpp index ac61957fde..0cf517bfe2 100644 --- a/src/serac/numerics/functional/element_restriction.cpp +++ b/src/serac/numerics/functional/element_restriction.cpp @@ -485,12 +485,12 @@ axom::Array GetFaceDofs(const serac::fes_t* fes for (int f : mfem_face_ids) { + mfem::Mesh::FaceInformation info = mesh->GetFaceInformation(f); + if (mesh->GetFaceGeometry(f) != face_geom) { SLIC_ERROR("encountered incorrect face geometry type"); } - //mfem::Mesh::FaceInformation info = mesh->GetFaceInformation(f); - // mfem doesn't provide this connectivity info for DG spaces directly (?), // so we have to get at it indirectly in several steps: if (isDG(*fes)) { @@ -554,7 +554,7 @@ axom::Array GetFaceDofs(const serac::fes_t* fes // to get a consistent winding. // // In 3D, mfem does use a consistently CCW winding for boundary faces (I think). - int orientation = (mesh->Dimension() == 2) ? 0 : orientations[i]; + int orientation = (mesh->Dimension() == 2 && info.IsBoundary()) ? 0 : orientations[i]; // 4. extract only the dofs that correspond to side `i` for (auto k : face_perm(orientation)) { diff --git a/src/serac/numerics/functional/element_restriction.hpp b/src/serac/numerics/functional/element_restriction.hpp index 1ebb891b7c..821571fd7a 100644 --- a/src/serac/numerics/functional/element_restriction.hpp +++ b/src/serac/numerics/functional/element_restriction.hpp @@ -255,3 +255,6 @@ axom::Array GetElementDofs(const serac::fes_t* * @param type whether the face is of interior or boundary type */ axom::Array GetFaceDofs(const serac::fes_t* fes, mfem::Geometry::Type face_geom, FaceType type); + +/// @overload +axom::Array GetFaceDofs(const serac::fes_t* fes, mfem::Geometry::Type face_geom, const std::vector & mfem_face_ids); diff --git a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp index 46d208868c..3df6a451a6 100644 --- a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp +++ b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp @@ -1,11 +1,12 @@ #include +#include "serac/mesh/mesh_utils_base.hpp" #include "serac/numerics/functional/domain.hpp" #include "serac/numerics/functional/element_restriction.hpp" +#include "serac/numerics/functional/functional.hpp" using namespace serac; - std::string mesh_dir = SERAC_REPO_DIR "/data/meshes/"; constexpr mfem::Geometry::Type face_type(mfem::Geometry::Type geom) { @@ -241,7 +242,7 @@ std::ostream & operator<<(std::ostream & out, axom::Array -double scalar_func(const mfem::Vector & x, double /*t*/) { +double scalar_func_mfem(const mfem::Vector & x, double /*t*/) { if constexpr (dim == 2) { return x[0] + 10 * x[1]; } else { @@ -250,7 +251,16 @@ double scalar_func(const mfem::Vector & x, double /*t*/) { } template < int dim > -void vector_func(const mfem::Vector & x, double /*t*/, mfem::Vector & output) { +double scalar_func(const tensor x) { + if constexpr (dim == 2) { + return x[0] + 10 * x[1]; + } else { + return x[0] + 10 * x[1] + 100 * x[2]; + } +} + +template < int dim > +void vector_func_mfem(const mfem::Vector & x, double /*t*/, mfem::Vector & output) { if constexpr (dim == 2) { output[0] = +x[1]; output[1] = -x[0]; @@ -261,8 +271,8 @@ void vector_func(const mfem::Vector & x, double /*t*/, mfem::Vector & output) { } } -template < mfem::Geometry::Type geom > -void parametrized_test(int polynomial_order, int permutation) { +template < mfem::Geometry::Type geom, int polynomial_order > +void parametrized_test(int permutation) { constexpr mfem::Geometry::Type face_geom = face_type(geom); constexpr int dim = dimension_of(geom); @@ -275,19 +285,24 @@ void parametrized_test(int polynomial_order, int permutation) { // and a single "face" that separates them EXPECT_EQ(mesh.GetNE(), 2); + std::vector face_ids; + if (face_geom == mfem::Geometry::SEGMENT) { EXPECT_EQ(interior_faces.edge_ids_.size(), 1); EXPECT_EQ(interior_faces.mfem_edge_ids_.size(), 1); + face_ids = interior_faces.mfem_edge_ids_; } if (face_geom == mfem::Geometry::TRIANGLE) { EXPECT_EQ(interior_faces.tri_ids_.size(), 1); EXPECT_EQ(interior_faces.mfem_tri_ids_.size(), 1); + face_ids = interior_faces.mfem_tri_ids_; } if (face_geom == mfem::Geometry::SQUARE) { EXPECT_EQ(interior_faces.quad_ids_.size(), 1); EXPECT_EQ(interior_faces.mfem_quad_ids_.size(), 1); + face_ids = interior_faces.mfem_quad_ids_; } auto H1_fec = std::make_unique(polynomial_order, dim); @@ -302,8 +317,8 @@ void parametrized_test(int polynomial_order, int permutation) { mfem::GridFunction Hcurl_gf(Hcurl_fes.get()); mfem::GridFunction L2_gf(L2_fes.get()); - mfem::FunctionCoefficient sfunc(scalar_func); - mfem::VectorFunctionCoefficient vfunc(dim, vector_func); + mfem::FunctionCoefficient sfunc(scalar_func_mfem); + mfem::VectorFunctionCoefficient vfunc(dim, vector_func_mfem); H1_gf.ProjectCoefficient(sfunc); Hcurl_gf.ProjectCoefficient(vfunc); @@ -311,16 +326,58 @@ void parametrized_test(int polynomial_order, int permutation) { auto H1_dofs = GetFaceDofs(H1_fes.get(), face_geom, FaceType::INTERIOR); auto Hcurl_dofs = GetFaceDofs(Hcurl_fes.get(), face_geom, FaceType::INTERIOR); + +#if 0 auto L2_dofs = GetFaceDofs(L2_fes.get(), face_geom, FaceType::INTERIOR); +#else + auto L2_dofs = GetFaceDofs(L2_fes.get(), face_geom, face_ids); +#endif // verify that the dofs for the L2 faces are aligned properly int dofs_per_side = L2_dofs.shape()[1] / 2; for (int i = 0; i < dofs_per_side; i++) { int id1 = int(L2_dofs(0, i).index()); int id2 = int(L2_dofs(0, i + dofs_per_side).index()); + //std::cout << id1 << " " << id2 << " " << L2_gf[id1] << " " << L2_gf[id2] << std::endl; EXPECT_NEAR(L2_gf[id1], L2_gf[id2], 5.0e-14); } +//////////////////////////////////////////////////////////////////////////////// + + using space = L2; + + auto pmesh = mesh::refineAndDistribute(std::move(mesh), 0); + Domain pinterior_faces = InteriorFaces(*pmesh); + + auto L2_pfes = mfem::ParFiniteElementSpace(pmesh.get(), L2_fec.get()); + + Functional< double(space) > r({&L2_pfes}); + + r.AddInteriorFaceIntegral( + Dimension{}, + DependsOn<0>{}, + [](double /*t*/, auto X, auto rho){ + //std::cout << get<0>(X) << std::endl; + double expected = scalar_func(get<0>(X)); + auto [rho1, rho2] = rho; + EXPECT_NEAR(expected, get_value(rho1), 5.0e-14); + EXPECT_NEAR(expected, get_value(rho2), 5.0e-14); + auto difference = rho1 - rho2; + return difference * difference; + }, + pinterior_faces + ); + + mfem::Vector U(L2_pfes.TrueVSize()); + mfem::ParGridFunction U_pgf(&L2_pfes); + U_pgf.ProjectCoefficient(sfunc); + U_pgf.GetTrueDofs(U); + + double t = 0.0; + double output = r(t, U); + + EXPECT_NEAR(output, 0.0, 5.0e-14); + // TODO: check that the actual values match their respective functions // evaluated directly at the nodes (for H1, Hcurl, and L2 gfs) @@ -330,153 +387,174 @@ void parametrized_test(int polynomial_order, int permutation) { //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -TEST(DomainInterior, TriMesh10) { parametrized_test(1, 0); } -TEST(DomainInterior, TriMesh11) { parametrized_test(1, 1); } -TEST(DomainInterior, TriMesh12) { parametrized_test(1, 2); } +TEST(DomainInterior, TriMesh10) { parametrized_test(0); } +#if 1 +TEST(DomainInterior, TriMesh11) { parametrized_test(1); } +TEST(DomainInterior, TriMesh12) { parametrized_test(2); } -TEST(DomainInterior, TriMesh20) { parametrized_test(2, 0); } -TEST(DomainInterior, TriMesh21) { parametrized_test(2, 1); } -TEST(DomainInterior, TriMesh22) { parametrized_test(2, 2); } +TEST(DomainInterior, TriMesh20) { parametrized_test(0); } +TEST(DomainInterior, TriMesh21) { parametrized_test(1); } +TEST(DomainInterior, TriMesh22) { parametrized_test(2); } -TEST(DomainInterior, TriMesh30) { parametrized_test(3, 0); } -TEST(DomainInterior, TriMesh31) { parametrized_test(3, 1); } -TEST(DomainInterior, TriMesh32) { parametrized_test(3, 2); } +TEST(DomainInterior, TriMesh30) { parametrized_test(0); } +TEST(DomainInterior, TriMesh31) { parametrized_test(1); } +TEST(DomainInterior, TriMesh32) { parametrized_test(2); } //////////////////////////////////////////////////////////////////////////////// -TEST(DomainInterior, QuadMesh10) { parametrized_test(1, 0); } -TEST(DomainInterior, QuadMesh11) { parametrized_test(1, 1); } -TEST(DomainInterior, QuadMesh12) { parametrized_test(1, 2); } -TEST(DomainInterior, QuadMesh13) { parametrized_test(1, 3); } +TEST(DomainInterior, QuadMesh10) { parametrized_test(0); } +TEST(DomainInterior, QuadMesh11) { parametrized_test(1); } +TEST(DomainInterior, QuadMesh12) { parametrized_test(2); } +TEST(DomainInterior, QuadMesh13) { parametrized_test(3); } -TEST(DomainInterior, QuadMesh20) { parametrized_test(2, 0); } -TEST(DomainInterior, QuadMesh21) { parametrized_test(2, 1); } -TEST(DomainInterior, QuadMesh22) { parametrized_test(2, 2); } -TEST(DomainInterior, QuadMesh23) { parametrized_test(2, 3); } +TEST(DomainInterior, QuadMesh20) { parametrized_test(0); } +TEST(DomainInterior, QuadMesh21) { parametrized_test(1); } +TEST(DomainInterior, QuadMesh22) { parametrized_test(2); } +TEST(DomainInterior, QuadMesh23) { parametrized_test(3); } -TEST(DomainInterior, QuadMesh30) { parametrized_test(3, 0); } -TEST(DomainInterior, QuadMesh31) { parametrized_test(3, 1); } -TEST(DomainInterior, QuadMesh32) { parametrized_test(3, 2); } -TEST(DomainInterior, QuadMesh33) { parametrized_test(3, 3); } +TEST(DomainInterior, QuadMesh30) { parametrized_test(0); } +TEST(DomainInterior, QuadMesh31) { parametrized_test(1); } +TEST(DomainInterior, QuadMesh32) { parametrized_test(2); } +TEST(DomainInterior, QuadMesh33) { parametrized_test(3); } //////////////////////////////////////////////////////////////////////////////// -TEST(DomainInterior, TetMesh100) { parametrized_test(1, 0); } -TEST(DomainInterior, TetMesh101) { parametrized_test(1, 1); } -TEST(DomainInterior, TetMesh102) { parametrized_test(1, 2); } -TEST(DomainInterior, TetMesh103) { parametrized_test(1, 3); } -TEST(DomainInterior, TetMesh104) { parametrized_test(1, 4); } -TEST(DomainInterior, TetMesh105) { parametrized_test(1, 5); } -TEST(DomainInterior, TetMesh106) { parametrized_test(1, 6); } -TEST(DomainInterior, TetMesh107) { parametrized_test(1, 7); } -TEST(DomainInterior, TetMesh108) { parametrized_test(1, 8); } -TEST(DomainInterior, TetMesh109) { parametrized_test(1, 9); } -TEST(DomainInterior, TetMesh110) { parametrized_test(1, 10); } -TEST(DomainInterior, TetMesh111) { parametrized_test(1, 11); } - -TEST(DomainInterior, TetMesh200) { parametrized_test(2, 0); } -TEST(DomainInterior, TetMesh201) { parametrized_test(2, 1); } -TEST(DomainInterior, TetMesh202) { parametrized_test(2, 2); } -TEST(DomainInterior, TetMesh203) { parametrized_test(2, 3); } -TEST(DomainInterior, TetMesh204) { parametrized_test(2, 4); } -TEST(DomainInterior, TetMesh205) { parametrized_test(2, 5); } -TEST(DomainInterior, TetMesh206) { parametrized_test(2, 6); } -TEST(DomainInterior, TetMesh207) { parametrized_test(2, 7); } -TEST(DomainInterior, TetMesh208) { parametrized_test(2, 8); } -TEST(DomainInterior, TetMesh209) { parametrized_test(2, 9); } -TEST(DomainInterior, TetMesh210) { parametrized_test(2, 10); } -TEST(DomainInterior, TetMesh211) { parametrized_test(2, 11); } - -TEST(DomainInterior, TetMesh300) { parametrized_test(3, 0); } -TEST(DomainInterior, TetMesh301) { parametrized_test(3, 1); } -TEST(DomainInterior, TetMesh302) { parametrized_test(3, 2); } -TEST(DomainInterior, TetMesh303) { parametrized_test(3, 3); } -TEST(DomainInterior, TetMesh304) { parametrized_test(3, 4); } -TEST(DomainInterior, TetMesh305) { parametrized_test(3, 5); } -TEST(DomainInterior, TetMesh306) { parametrized_test(3, 6); } -TEST(DomainInterior, TetMesh307) { parametrized_test(3, 7); } -TEST(DomainInterior, TetMesh308) { parametrized_test(3, 8); } -TEST(DomainInterior, TetMesh309) { parametrized_test(3, 9); } -TEST(DomainInterior, TetMesh310) { parametrized_test(3, 10); } -TEST(DomainInterior, TetMesh311) { parametrized_test(3, 11); } +TEST(DomainInterior, TetMesh100) { parametrized_test( 0); } +TEST(DomainInterior, TetMesh101) { parametrized_test( 1); } +TEST(DomainInterior, TetMesh102) { parametrized_test( 2); } +TEST(DomainInterior, TetMesh103) { parametrized_test( 3); } +TEST(DomainInterior, TetMesh104) { parametrized_test( 4); } +TEST(DomainInterior, TetMesh105) { parametrized_test( 5); } +TEST(DomainInterior, TetMesh106) { parametrized_test( 6); } +TEST(DomainInterior, TetMesh107) { parametrized_test( 7); } +TEST(DomainInterior, TetMesh108) { parametrized_test( 8); } +TEST(DomainInterior, TetMesh109) { parametrized_test( 9); } +TEST(DomainInterior, TetMesh110) { parametrized_test(10); } +TEST(DomainInterior, TetMesh111) { parametrized_test(11); } + +TEST(DomainInterior, TetMesh200) { parametrized_test( 0); } +TEST(DomainInterior, TetMesh201) { parametrized_test( 1); } +TEST(DomainInterior, TetMesh202) { parametrized_test( 2); } +TEST(DomainInterior, TetMesh203) { parametrized_test( 3); } +TEST(DomainInterior, TetMesh204) { parametrized_test( 4); } +TEST(DomainInterior, TetMesh205) { parametrized_test( 5); } +TEST(DomainInterior, TetMesh206) { parametrized_test( 6); } +TEST(DomainInterior, TetMesh207) { parametrized_test( 7); } +TEST(DomainInterior, TetMesh208) { parametrized_test( 8); } +TEST(DomainInterior, TetMesh209) { parametrized_test( 9); } +TEST(DomainInterior, TetMesh210) { parametrized_test(10); } +TEST(DomainInterior, TetMesh211) { parametrized_test(11); } + +TEST(DomainInterior, TetMesh300) { parametrized_test( 0); } +TEST(DomainInterior, TetMesh301) { parametrized_test( 1); } +TEST(DomainInterior, TetMesh302) { parametrized_test( 2); } +TEST(DomainInterior, TetMesh303) { parametrized_test( 3); } +TEST(DomainInterior, TetMesh304) { parametrized_test( 4); } +TEST(DomainInterior, TetMesh305) { parametrized_test( 5); } +TEST(DomainInterior, TetMesh306) { parametrized_test( 6); } +TEST(DomainInterior, TetMesh307) { parametrized_test( 7); } +TEST(DomainInterior, TetMesh308) { parametrized_test( 8); } +TEST(DomainInterior, TetMesh309) { parametrized_test( 9); } +TEST(DomainInterior, TetMesh310) { parametrized_test(10); } +TEST(DomainInterior, TetMesh311) { parametrized_test(11); } //////////////////////////////////////////////////////////////////////////////// -TEST(DomainInterior, HexMesh100) { parametrized_test(1, 0); } -TEST(DomainInterior, HexMesh101) { parametrized_test(1, 1); } -TEST(DomainInterior, HexMesh102) { parametrized_test(1, 2); } -TEST(DomainInterior, HexMesh103) { parametrized_test(1, 3); } -TEST(DomainInterior, HexMesh104) { parametrized_test(1, 4); } -TEST(DomainInterior, HexMesh105) { parametrized_test(1, 5); } -TEST(DomainInterior, HexMesh106) { parametrized_test(1, 6); } -TEST(DomainInterior, HexMesh107) { parametrized_test(1, 7); } -TEST(DomainInterior, HexMesh108) { parametrized_test(1, 8); } -TEST(DomainInterior, HexMesh109) { parametrized_test(1, 9); } -TEST(DomainInterior, HexMesh110) { parametrized_test(1, 10); } -TEST(DomainInterior, HexMesh111) { parametrized_test(1, 11); } -TEST(DomainInterior, HexMesh112) { parametrized_test(1, 12); } -TEST(DomainInterior, HexMesh113) { parametrized_test(1, 13); } -TEST(DomainInterior, HexMesh114) { parametrized_test(1, 14); } -TEST(DomainInterior, HexMesh115) { parametrized_test(1, 15); } -TEST(DomainInterior, HexMesh116) { parametrized_test(1, 16); } -TEST(DomainInterior, HexMesh117) { parametrized_test(1, 17); } -TEST(DomainInterior, HexMesh118) { parametrized_test(1, 18); } -TEST(DomainInterior, HexMesh119) { parametrized_test(1, 19); } -TEST(DomainInterior, HexMesh120) { parametrized_test(1, 20); } -TEST(DomainInterior, HexMesh121) { parametrized_test(1, 21); } -TEST(DomainInterior, HexMesh122) { parametrized_test(1, 22); } -TEST(DomainInterior, HexMesh123) { parametrized_test(1, 23); } - -TEST(DomainInterior, HexMesh200) { parametrized_test(2, 0); } -TEST(DomainInterior, HexMesh201) { parametrized_test(2, 1); } -TEST(DomainInterior, HexMesh202) { parametrized_test(2, 2); } -TEST(DomainInterior, HexMesh203) { parametrized_test(2, 3); } -TEST(DomainInterior, HexMesh204) { parametrized_test(2, 4); } -TEST(DomainInterior, HexMesh205) { parametrized_test(2, 5); } -TEST(DomainInterior, HexMesh206) { parametrized_test(2, 6); } -TEST(DomainInterior, HexMesh207) { parametrized_test(2, 7); } -TEST(DomainInterior, HexMesh208) { parametrized_test(2, 8); } -TEST(DomainInterior, HexMesh209) { parametrized_test(2, 9); } -TEST(DomainInterior, HexMesh210) { parametrized_test(2, 10); } -TEST(DomainInterior, HexMesh211) { parametrized_test(2, 11); } -TEST(DomainInterior, HexMesh212) { parametrized_test(2, 12); } -TEST(DomainInterior, HexMesh213) { parametrized_test(2, 13); } -TEST(DomainInterior, HexMesh214) { parametrized_test(2, 14); } -TEST(DomainInterior, HexMesh215) { parametrized_test(2, 15); } -TEST(DomainInterior, HexMesh216) { parametrized_test(2, 16); } -TEST(DomainInterior, HexMesh217) { parametrized_test(2, 17); } -TEST(DomainInterior, HexMesh218) { parametrized_test(2, 18); } -TEST(DomainInterior, HexMesh219) { parametrized_test(2, 19); } -TEST(DomainInterior, HexMesh220) { parametrized_test(2, 20); } -TEST(DomainInterior, HexMesh221) { parametrized_test(2, 21); } -TEST(DomainInterior, HexMesh222) { parametrized_test(2, 22); } -TEST(DomainInterior, HexMesh223) { parametrized_test(2, 23); } - -TEST(DomainInterior, HexMesh300) { parametrized_test(3, 0); } -TEST(DomainInterior, HexMesh301) { parametrized_test(3, 1); } -TEST(DomainInterior, HexMesh302) { parametrized_test(3, 2); } -TEST(DomainInterior, HexMesh303) { parametrized_test(3, 3); } -TEST(DomainInterior, HexMesh304) { parametrized_test(3, 4); } -TEST(DomainInterior, HexMesh305) { parametrized_test(3, 5); } -TEST(DomainInterior, HexMesh306) { parametrized_test(3, 6); } -TEST(DomainInterior, HexMesh307) { parametrized_test(3, 7); } -TEST(DomainInterior, HexMesh308) { parametrized_test(3, 8); } -TEST(DomainInterior, HexMesh309) { parametrized_test(3, 9); } -TEST(DomainInterior, HexMesh310) { parametrized_test(3, 10); } -TEST(DomainInterior, HexMesh311) { parametrized_test(3, 11); } -TEST(DomainInterior, HexMesh312) { parametrized_test(3, 12); } -TEST(DomainInterior, HexMesh313) { parametrized_test(3, 13); } -TEST(DomainInterior, HexMesh314) { parametrized_test(3, 14); } -TEST(DomainInterior, HexMesh315) { parametrized_test(3, 15); } -TEST(DomainInterior, HexMesh316) { parametrized_test(3, 16); } -TEST(DomainInterior, HexMesh317) { parametrized_test(3, 17); } -TEST(DomainInterior, HexMesh318) { parametrized_test(3, 18); } -TEST(DomainInterior, HexMesh319) { parametrized_test(3, 19); } -TEST(DomainInterior, HexMesh320) { parametrized_test(3, 20); } -TEST(DomainInterior, HexMesh321) { parametrized_test(3, 21); } -TEST(DomainInterior, HexMesh322) { parametrized_test(3, 22); } -TEST(DomainInterior, HexMesh323) { parametrized_test(3, 23); } +TEST(DomainInterior, HexMesh100) { parametrized_test( 0); } +TEST(DomainInterior, HexMesh101) { parametrized_test( 1); } +TEST(DomainInterior, HexMesh102) { parametrized_test( 2); } +TEST(DomainInterior, HexMesh103) { parametrized_test( 3); } +TEST(DomainInterior, HexMesh104) { parametrized_test( 4); } +TEST(DomainInterior, HexMesh105) { parametrized_test( 5); } +TEST(DomainInterior, HexMesh106) { parametrized_test( 6); } +TEST(DomainInterior, HexMesh107) { parametrized_test( 7); } +TEST(DomainInterior, HexMesh108) { parametrized_test( 8); } +TEST(DomainInterior, HexMesh109) { parametrized_test( 9); } +TEST(DomainInterior, HexMesh110) { parametrized_test(10); } +TEST(DomainInterior, HexMesh111) { parametrized_test(11); } +TEST(DomainInterior, HexMesh112) { parametrized_test(12); } +TEST(DomainInterior, HexMesh113) { parametrized_test(13); } +TEST(DomainInterior, HexMesh114) { parametrized_test(14); } +TEST(DomainInterior, HexMesh115) { parametrized_test(15); } +TEST(DomainInterior, HexMesh116) { parametrized_test(16); } +TEST(DomainInterior, HexMesh117) { parametrized_test(17); } +TEST(DomainInterior, HexMesh118) { parametrized_test(18); } +TEST(DomainInterior, HexMesh119) { parametrized_test(19); } +TEST(DomainInterior, HexMesh120) { parametrized_test(20); } +TEST(DomainInterior, HexMesh121) { parametrized_test(21); } +TEST(DomainInterior, HexMesh122) { parametrized_test(22); } +TEST(DomainInterior, HexMesh123) { parametrized_test(23); } + +TEST(DomainInterior, HexMesh200) { parametrized_test( 0); } +TEST(DomainInterior, HexMesh201) { parametrized_test( 1); } +TEST(DomainInterior, HexMesh202) { parametrized_test( 2); } +TEST(DomainInterior, HexMesh203) { parametrized_test( 3); } +TEST(DomainInterior, HexMesh204) { parametrized_test( 4); } +TEST(DomainInterior, HexMesh205) { parametrized_test( 5); } +TEST(DomainInterior, HexMesh206) { parametrized_test( 6); } +TEST(DomainInterior, HexMesh207) { parametrized_test( 7); } +TEST(DomainInterior, HexMesh208) { parametrized_test( 8); } +TEST(DomainInterior, HexMesh209) { parametrized_test( 9); } +TEST(DomainInterior, HexMesh210) { parametrized_test(10); } +TEST(DomainInterior, HexMesh211) { parametrized_test(11); } +TEST(DomainInterior, HexMesh212) { parametrized_test(12); } +TEST(DomainInterior, HexMesh213) { parametrized_test(13); } +TEST(DomainInterior, HexMesh214) { parametrized_test(14); } +TEST(DomainInterior, HexMesh215) { parametrized_test(15); } +TEST(DomainInterior, HexMesh216) { parametrized_test(16); } +TEST(DomainInterior, HexMesh217) { parametrized_test(17); } +TEST(DomainInterior, HexMesh218) { parametrized_test(18); } +TEST(DomainInterior, HexMesh219) { parametrized_test(19); } +TEST(DomainInterior, HexMesh220) { parametrized_test(20); } +TEST(DomainInterior, HexMesh221) { parametrized_test(21); } +TEST(DomainInterior, HexMesh222) { parametrized_test(22); } +TEST(DomainInterior, HexMesh223) { parametrized_test(23); } + +TEST(DomainInterior, HexMesh300) { parametrized_test( 0); } +TEST(DomainInterior, HexMesh301) { parametrized_test( 1); } +TEST(DomainInterior, HexMesh302) { parametrized_test( 2); } +TEST(DomainInterior, HexMesh303) { parametrized_test( 3); } +TEST(DomainInterior, HexMesh304) { parametrized_test( 4); } +TEST(DomainInterior, HexMesh305) { parametrized_test( 5); } +TEST(DomainInterior, HexMesh306) { parametrized_test( 6); } +TEST(DomainInterior, HexMesh307) { parametrized_test( 7); } +TEST(DomainInterior, HexMesh308) { parametrized_test( 8); } +TEST(DomainInterior, HexMesh309) { parametrized_test( 9); } +TEST(DomainInterior, HexMesh310) { parametrized_test(10); } +TEST(DomainInterior, HexMesh311) { parametrized_test(11); } +TEST(DomainInterior, HexMesh312) { parametrized_test(12); } +TEST(DomainInterior, HexMesh313) { parametrized_test(13); } +TEST(DomainInterior, HexMesh314) { parametrized_test(14); } +TEST(DomainInterior, HexMesh315) { parametrized_test(15); } +TEST(DomainInterior, HexMesh316) { parametrized_test(16); } +TEST(DomainInterior, HexMesh317) { parametrized_test(17); } +TEST(DomainInterior, HexMesh318) { parametrized_test(18); } +TEST(DomainInterior, HexMesh319) { parametrized_test(19); } +TEST(DomainInterior, HexMesh320) { parametrized_test(20); } +TEST(DomainInterior, HexMesh321) { parametrized_test(21); } +TEST(DomainInterior, HexMesh322) { parametrized_test(22); } +TEST(DomainInterior, HexMesh323) { parametrized_test(23); } +#endif //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// + +int main(int argc, char* argv[]) +{ + int num_procs, myid; + + ::testing::InitGoogleTest(&argc, argv); + + MPI_Init(&argc, &argv); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + MPI_Comm_rank(MPI_COMM_WORLD, &myid); + + axom::slic::SimpleLogger logger; + + int result = RUN_ALL_TESTS(); + + MPI_Finalize(); + + return result; +} From fff274417e174e8deb81196f65d01d94f87d352f Mon Sep 17 00:00:00 2001 From: Brandon Talamini Date: Thu, 21 Nov 2024 09:01:20 -0800 Subject: [PATCH 48/92] Add a setter method to populate element lists to make object state stay valid Makes it less likely that a user will populate some of the required lists but not others. --- src/serac/numerics/functional/domain.cpp | 34 ++++++++++++++++++------ src/serac/numerics/functional/domain.hpp | 2 ++ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/serac/numerics/functional/domain.cpp b/src/serac/numerics/functional/domain.cpp index 01e648405b..4ef90871f9 100644 --- a/src/serac/numerics/functional/domain.cpp +++ b/src/serac/numerics/functional/domain.cpp @@ -258,31 +258,27 @@ static Domain domain_of_elems(const mfem::Mesh& switch (x.size()) { case 3: if (add) { - output.tri_ids_.push_back(tri_id); - output.mfem_tri_ids_.push_back(i); + output.addElement(tri_id, i, mfem::Geometry::TRIANGLE); } tri_id++; break; case 4: if constexpr (d == 2) { if (add) { - output.quad_ids_.push_back(quad_id); - output.mfem_quad_ids_.push_back(i); + output.addElement(quad_id, i, mfem::Geometry::SQUARE); } quad_id++; } if constexpr (d == 3) { if (add) { - output.tet_ids_.push_back(tet_id); - output.mfem_tet_ids_.push_back(i); + output.addElement(tet_id, i, mfem::Geometry::TETRAHEDRON); } tet_id++; } break; case 8: if (add) { - output.hex_ids_.push_back(hex_id); - output.mfem_hex_ids_.push_back(i); + output.addElement(hex_id, i, mfem::Geometry::CUBE); } hex_id++; break; @@ -305,6 +301,25 @@ Domain Domain::ofElements(const mfem::Mesh& mesh, std::function(mesh, func); } +void Domain::addElement(int geom_id, int elem_id, mfem::Geometry::Type element_geometry) +{ + if (element_geometry == mfem::Geometry::TRIANGLE) { + tri_ids_.push_back(geom_id); + mfem_tri_ids_.push_back(elem_id); + } else if (element_geometry == mfem::Geometry::SQUARE) { + quad_ids_.push_back(geom_id); + mfem_quad_ids_.push_back(elem_id); + } else if (element_geometry == mfem::Geometry::TETRAHEDRON) { + tet_ids_.push_back(geom_id); + mfem_tet_ids_.push_back(elem_id); + } else if (element_geometry == mfem::Geometry::CUBE) { + hex_ids_.push_back(geom_id); + mfem_hex_ids_.push_back(elem_id); + } else { + SLIC_ERROR("unsupported element type"); + } +} + /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// @@ -385,6 +400,9 @@ Domain Domain::ofBoundaryElements(const mfem::Mesh& mesh, std::function(mesh, func); } +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + mfem::Array Domain::dof_list(mfem::FiniteElementSpace* fes) const { std::set dof_ids; diff --git a/src/serac/numerics/functional/domain.hpp b/src/serac/numerics/functional/domain.hpp index ad7c169117..66e2ee3149 100644 --- a/src/serac/numerics/functional/domain.hpp +++ b/src/serac/numerics/functional/domain.hpp @@ -130,6 +130,8 @@ struct Domain { exit(1); } + void addElement(int geom_id, int elem_id, mfem::Geometry::Type element_geometry); + /// @brief get mfem degree of freedom list for a given FiniteElementSpace mfem::Array dof_list(mfem::FiniteElementSpace* fes) const; }; From 36c8669d954fa0140a4f0f024fcaf687e9aa6bb7 Mon Sep 17 00:00:00 2001 From: Brandon Talamini Date: Thu, 21 Nov 2024 10:24:41 -0800 Subject: [PATCH 49/92] Make setter method for adding boundary elements --- src/serac/numerics/functional/domain.cpp | 25 ++++++++++++++++++------ src/serac/numerics/functional/domain.hpp | 2 ++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/serac/numerics/functional/domain.cpp b/src/serac/numerics/functional/domain.cpp index 4ef90871f9..def2374e82 100644 --- a/src/serac/numerics/functional/domain.cpp +++ b/src/serac/numerics/functional/domain.cpp @@ -362,22 +362,19 @@ static Domain domain_of_boundary_elems(const mfem::Mesh& switch (geom) { case mfem::Geometry::SEGMENT: if (add) { - output.edge_ids_.push_back(edge_id); - output.mfem_edge_ids_.push_back(f); + output.addBoundaryElement(edge_id, f, geom); } edge_id++; break; case mfem::Geometry::TRIANGLE: if (add) { - output.tri_ids_.push_back(tri_id); - output.mfem_tri_ids_.push_back(f); + output.addBoundaryElement(edge_id, f, geom); } tri_id++; break; case mfem::Geometry::SQUARE: if (add) { - output.quad_ids_.push_back(quad_id); - output.mfem_quad_ids_.push_back(f); + output.addBoundaryElement(edge_id, f, geom); } quad_id++; break; @@ -400,6 +397,22 @@ Domain Domain::ofBoundaryElements(const mfem::Mesh& mesh, std::function(mesh, func); } +void Domain::addBoundaryElement(int geom_id, int elem_id, mfem::Geometry::Type element_geometry) +{ + if (element_geometry == mfem::Geometry::SEGMENT) { + edge_ids_.push_back(geom_id); + mfem_edge_ids_.push_back(elem_id); + } else if (element_geometry == mfem::Geometry::TRIANGLE) { + tri_ids_.push_back(geom_id); + mfem_tri_ids_.push_back(elem_id); + } else if (element_geometry == mfem::Geometry::SQUARE) { + quad_ids_.push_back(geom_id); + mfem_quad_ids_.push_back(elem_id); + } else { + SLIC_ERROR("unsupported boundary element type"); + } +} + /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/serac/numerics/functional/domain.hpp b/src/serac/numerics/functional/domain.hpp index 66e2ee3149..7c8ec713af 100644 --- a/src/serac/numerics/functional/domain.hpp +++ b/src/serac/numerics/functional/domain.hpp @@ -130,6 +130,8 @@ struct Domain { exit(1); } + void addBoundaryElement(int geom_id, int elem_id, mfem::Geometry::Type element_geometry); + void addElement(int geom_id, int elem_id, mfem::Geometry::Type element_geometry); /// @brief get mfem degree of freedom list for a given FiniteElementSpace From 8a9a11aa794bcdb7808cb25e08cb3a17603bf3a0 Mon Sep 17 00:00:00 2001 From: Brandon Talamini Date: Thu, 21 Nov 2024 10:40:21 -0800 Subject: [PATCH 50/92] Simplify down to just one adder helper function --- src/serac/numerics/functional/domain.cpp | 39 +++++++----------------- src/serac/numerics/functional/domain.hpp | 2 -- 2 files changed, 11 insertions(+), 30 deletions(-) diff --git a/src/serac/numerics/functional/domain.cpp b/src/serac/numerics/functional/domain.cpp index def2374e82..b413a94282 100644 --- a/src/serac/numerics/functional/domain.cpp +++ b/src/serac/numerics/functional/domain.cpp @@ -117,13 +117,11 @@ static Domain domain_of_edges(const mfem::Mesh& mesh, std::function predicate int bdr_id = edge_id_to_bdr_id[i]; int attr = (bdr_id > 0) ? mesh.GetBdrAttribute(bdr_id) : -1; if (predicate(x, attr)) { - output.edge_ids_.push_back(i); - output.mfem_edge_ids_.push_back(i); + output.addElement(i, i, mfem::Geometry::SEGMENT); } } else { if (predicate(x)) { - output.edge_ids_.push_back(i); - output.mfem_edge_ids_.push_back(i); + output.addElement(i, i, mfem::Geometry::SEGMENT); } } } @@ -194,12 +192,10 @@ static Domain domain_of_faces(const mfem::Mesh& if (predicate(x, attr)) { if (x.size() == 3) { - output.tri_ids_.push_back(tri_id); - output.mfem_tri_ids_.push_back(i); + output.addElement(tri_id, i, mfem::Geometry::TRIANGLE); } if (x.size() == 4) { - output.quad_ids_.push_back(quad_id); - output.mfem_quad_ids_.push_back(i); + output.addElement(quad_id, i, mfem::Geometry::SQUARE); } } @@ -303,7 +299,10 @@ Domain Domain::ofElements(const mfem::Mesh& mesh, std::function(mesh, func); } -void Domain::addBoundaryElement(int geom_id, int elem_id, mfem::Geometry::Type element_geometry) -{ - if (element_geometry == mfem::Geometry::SEGMENT) { - edge_ids_.push_back(geom_id); - mfem_edge_ids_.push_back(elem_id); - } else if (element_geometry == mfem::Geometry::TRIANGLE) { - tri_ids_.push_back(geom_id); - mfem_tri_ids_.push_back(elem_id); - } else if (element_geometry == mfem::Geometry::SQUARE) { - quad_ids_.push_back(geom_id); - mfem_quad_ids_.push_back(elem_id); - } else { - SLIC_ERROR("unsupported boundary element type"); - } -} - /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/serac/numerics/functional/domain.hpp b/src/serac/numerics/functional/domain.hpp index 7c8ec713af..66e2ee3149 100644 --- a/src/serac/numerics/functional/domain.hpp +++ b/src/serac/numerics/functional/domain.hpp @@ -130,8 +130,6 @@ struct Domain { exit(1); } - void addBoundaryElement(int geom_id, int elem_id, mfem::Geometry::Type element_geometry); - void addElement(int geom_id, int elem_id, mfem::Geometry::Type element_geometry); /// @brief get mfem degree of freedom list for a given FiniteElementSpace From 88055d15f128ebc975e6f7ace40760147ae544b5 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Thu, 21 Nov 2024 10:58:35 -0800 Subject: [PATCH 51/92] revert experimental change to ParGridfunctions in geometric factor routines --- src/serac/numerics/functional/geometric_factors.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/serac/numerics/functional/geometric_factors.cpp b/src/serac/numerics/functional/geometric_factors.cpp index c00f1645e9..b9fc48d078 100644 --- a/src/serac/numerics/functional/geometric_factors.cpp +++ b/src/serac/numerics/functional/geometric_factors.cpp @@ -62,17 +62,19 @@ void compute_geometric_factors(mfem::Vector& positions_q, mfem::Vector& jacobian GeometricFactors::GeometricFactors(const Domain& domain, int q, mfem::Geometry::Type geom) { - const mfem::ParGridFunction* nodes = static_cast< const mfem::ParGridFunction * >(domain.mesh_.GetNodes()); - mfem::ParFiniteElementSpace * pfes = nodes->ParFESpace(); + //const mfem::ParGridFunction* nodes = static_cast< const mfem::ParGridFunction * >(domain.mesh_.GetNodes()); + // mfem::ParFiniteElementSpace * pfes = nodes->ParFESpace(); + const mfem::GridFunction* nodes = domain.mesh_.GetNodes(); + const mfem::FiniteElementSpace * fes = nodes->FESpace(); const std::vector & element_ids = domain.get_mfem_ids(geom); - auto restriction = serac::ElementRestriction(pfes, geom, element_ids); + auto restriction = serac::ElementRestriction(fes, geom, element_ids); mfem::Vector X_e(int(restriction.ESize())); restriction.Gather(*nodes, X_e); // assumes all elements are the same order - int p = pfes->GetElementOrder(0); + int p = fes->GetElementOrder(0); int spatial_dim = domain.mesh_.SpaceDimension(); int geometry_dim = dimension_of(geom); From a694fe17c596b374f3c0c5eb960ae47210907cec Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Thu, 21 Nov 2024 15:43:11 -0800 Subject: [PATCH 52/92] fix bug where set operations on domains were not populating the mfem_{...}_ids arrays --- src/serac/numerics/functional/domain.cpp | 104 +++++++++++++++++------ 1 file changed, 77 insertions(+), 27 deletions(-) diff --git a/src/serac/numerics/functional/domain.cpp b/src/serac/numerics/functional/domain.cpp index 4eb2ceaca3..46d697e5da 100644 --- a/src/serac/numerics/functional/domain.cpp +++ b/src/serac/numerics/functional/domain.cpp @@ -595,54 +595,104 @@ Domain InteriorFaces(const mesh_t & mesh) { /////////////////////////////////////////////////////////////////////////////////////// /// @cond -using c_iter = std::vector::const_iterator; -using b_iter = std::back_insert_iterator>; -using set_op = std::function; - -set_op union_op = std::set_union; -set_op intersection_op = std::set_intersection; -set_op difference_op = std::set_difference; +using int2 = std::tuple; +enum SET_OPERATION { UNION, INTERSECTION, DIFFERENCE }; /// @endcond +/// @brief combine a pair of arrays of ints into a single array of `int2`, see also: unzip() +void zip(std::vector< int2 > & ab, const std::vector& a, const std::vector& b) { + ab.resize(a.size()); + for (uint32_t i = 0; i < a.size(); i++) { + ab[i] = {a[i], b[i]}; + } +} + +/// @brief split an array of `int2` into a pair of arrays of ints, see also: zip() +void unzip(const std::vector< int2 > & ab, std::vector& a, std::vector& b) { + a.resize(ab.size()); + b.resize(ab.size()); + for (uint32_t i = 0; i < ab.size(); i++) { + auto ab_i = ab[i]; + a[i] = std::get<0>(ab_i); + b[i] = std::get<1>(ab_i); + } +} + /// @brief return a std::vector that is the result of applying (a op b) -std::vector set_operation(set_op op, const std::vector& a, const std::vector& b) +template < typename T > +std::vector set_operation(SET_OPERATION op, const std::vector& a, const std::vector& b) { - std::vector output; - op(a.begin(), a.end(), b.begin(), b.end(), back_inserter(output)); - return output; + using c_iter = typename std::vector::const_iterator; + using b_iter = std::back_insert_iterator>; + using set_op = std::function; + + set_op combine; + if (op == SET_OPERATION::UNION) { + combine = std::set_union; + } + if (op == SET_OPERATION::INTERSECTION) { + combine = std::set_intersection; + } + if (op == SET_OPERATION::DIFFERENCE) { + combine = std::set_difference; + } + + std::vector combined; + combine(a.begin(), a.end(), b.begin(), b.end(), back_inserter(combined)); + return combined; } /// @brief return a Domain that is the result of applying (a op b) -Domain set_operation(set_op op, const Domain& a, const Domain& b) +Domain set_operation(SET_OPERATION op, const Domain& a, const Domain& b) { assert(&a.mesh_ == &b.mesh_); assert(a.dim_ == b.dim_); - Domain output{a.mesh_, a.dim_}; + Domain combined{a.mesh_, a.dim_}; - if (output.dim_ == 0) { - output.vertex_ids_ = set_operation(op, a.vertex_ids_, b.vertex_ids_); + if (combined.dim_ == 0) { + combined.vertex_ids_ = set_operation(op, a.vertex_ids_, b.vertex_ids_); } - if (output.dim_ == 1) { - output.edge_ids_ = set_operation(op, a.edge_ids_, b.edge_ids_); + if (combined.dim_ == 1) { + std::vector a_zipped_ids, b_zipped_ids; + zip(a_zipped_ids, a.edge_ids_, a.mfem_edge_ids_); + zip(b_zipped_ids, b.edge_ids_, b.mfem_edge_ids_); + std::vector combined_zipped_ids = set_operation(op, a_zipped_ids, b_zipped_ids); + unzip(combined_zipped_ids, combined.edge_ids_, combined.mfem_edge_ids_); } - if (output.dim_ == 2) { - output.tri_ids_ = set_operation(op, a.tri_ids_, b.tri_ids_); - output.quad_ids_ = set_operation(op, a.quad_ids_, b.quad_ids_); + if (combined.dim_ == 2) { + std::vector a_zipped_ids, b_zipped_ids; + zip(a_zipped_ids, a.tri_ids_, a.mfem_tri_ids_); + zip(b_zipped_ids, b.tri_ids_, b.mfem_tri_ids_); + std::vector combined_zipped_ids = set_operation(op, a_zipped_ids, b_zipped_ids); + unzip(combined_zipped_ids, combined.tri_ids_, combined.mfem_tri_ids_); + + zip(a_zipped_ids, a.quad_ids_, a.mfem_quad_ids_); + zip(b_zipped_ids, b.quad_ids_, b.mfem_quad_ids_); + combined_zipped_ids = set_operation(op, a_zipped_ids, b_zipped_ids); + unzip(combined_zipped_ids, combined.quad_ids_, combined.mfem_quad_ids_); } - if (output.dim_ == 3) { - output.tet_ids_ = set_operation(op, a.tet_ids_, b.tet_ids_); - output.hex_ids_ = set_operation(op, a.hex_ids_, b.hex_ids_); + if (combined.dim_ == 3) { + std::vector a_zipped_ids, b_zipped_ids; + zip(a_zipped_ids, a.tet_ids_, a.mfem_tet_ids_); + zip(b_zipped_ids, b.tet_ids_, b.mfem_tet_ids_); + std::vector combined_zipped_ids = set_operation(op, a_zipped_ids, b_zipped_ids); + unzip(combined_zipped_ids, combined.tet_ids_, combined.mfem_tet_ids_); + + zip(a_zipped_ids, a.hex_ids_, a.mfem_hex_ids_); + zip(b_zipped_ids, b.hex_ids_, b.mfem_hex_ids_); + combined_zipped_ids = set_operation(op, a_zipped_ids, b_zipped_ids); + unzip(combined_zipped_ids, combined.hex_ids_, combined.mfem_hex_ids_); } - return output; + return combined; } -Domain operator|(const Domain& a, const Domain& b) { return set_operation(union_op, a, b); } -Domain operator&(const Domain& a, const Domain& b) { return set_operation(intersection_op, a, b); } -Domain operator-(const Domain& a, const Domain& b) { return set_operation(difference_op, a, b); } +Domain operator|(const Domain& a, const Domain& b) { return set_operation(SET_OPERATION::UNION, a, b); } +Domain operator&(const Domain& a, const Domain& b) { return set_operation(SET_OPERATION::INTERSECTION, a, b); } +Domain operator-(const Domain& a, const Domain& b) { return set_operation(SET_OPERATION::DIFFERENCE, a, b); } } // namespace serac From aee0ef3660fe0ce78e9c14cbce57c51a95cd9d6f Mon Sep 17 00:00:00 2001 From: Brandon Talamini Date: Fri, 22 Nov 2024 05:31:21 -0800 Subject: [PATCH 53/92] Fix set operations --- src/serac/numerics/functional/domain.cpp | 36 ++++++++++++++++--- .../functional/tests/domain_tests.cpp | 30 +++++++++++----- 2 files changed, 52 insertions(+), 14 deletions(-) diff --git a/src/serac/numerics/functional/domain.cpp b/src/serac/numerics/functional/domain.cpp index b413a94282..75bcd238e2 100644 --- a/src/serac/numerics/functional/domain.cpp +++ b/src/serac/numerics/functional/domain.cpp @@ -533,17 +533,43 @@ Domain set_operation(set_op op, const Domain& a, const Domain& b) } if (output.dim_ == 1) { - output.edge_ids_ = set_operation(op, a.edge_ids_, b.edge_ids_); + // BT: if this were Python, I'd use zip() here to iterate through + // both vectors simultaneously. Looks like C++23 will have this. + auto edges = set_operation(op, a.edge_ids_, b.edge_ids_); + auto mfem_edges = set_operation(op, a.mfem_edge_ids_, b.mfem_edge_ids_); + SLIC_ERROR_IF(edges.size() != mfem_edges.size(), + "Domain object has an invalid state, the edge index arrays should have the same sizes"); + for (std::vector::size_type i = 0; i < edges.size(); ++i) { + output.addElement(edges[i], mfem_edges[i], mfem::Geometry::SEGMENT); + } } if (output.dim_ == 2) { - output.tri_ids_ = set_operation(op, a.tri_ids_, b.tri_ids_); - output.quad_ids_ = set_operation(op, a.quad_ids_, b.quad_ids_); + auto tris = set_operation(op, a.tri_ids_, b.tri_ids_); + auto mfem_tris = set_operation(op, a.mfem_tri_ids_, b.mfem_tri_ids_); + for (std::vector::size_type i = 0; i < tris.size(); ++i) { + output.addElement(tris[i], mfem_tris[i], mfem::Geometry::TRIANGLE); + } + + auto quads = set_operation(op, a.quad_ids_, b.quad_ids_); + auto mfem_quads = set_operation(op, a.mfem_quad_ids_, b.mfem_quad_ids_); + for (std::vector::size_type i = 0; i < quads.size(); ++i) { + output.addElement(quads[i], mfem_quads[i], mfem::Geometry::SQUARE); + } } if (output.dim_ == 3) { - output.tet_ids_ = set_operation(op, a.tet_ids_, b.tet_ids_); - output.hex_ids_ = set_operation(op, a.hex_ids_, b.hex_ids_); + auto tets = set_operation(op, a.tet_ids_, b.tet_ids_); + auto mfem_tets = set_operation(op, a.mfem_tet_ids_, b.mfem_tet_ids_); + for (std::vector::size_type i = 0; i < tets.size(); ++i) { + output.addElement(tets[i], mfem_tets[i], mfem::Geometry::TETRAHEDRON); + } + + auto hexes = set_operation(op, a.hex_ids_, b.hex_ids_); + auto mfem_hexes = set_operation(op, a.mfem_hex_ids_, b.mfem_hex_ids_); + for (std::vector::size_type i = 0; i < hexes.size(); ++i) { + output.addElement(hexes[i], mfem_hexes[i], mfem::Geometry::CUBE); + } } return output; diff --git a/src/serac/numerics/functional/tests/domain_tests.cpp b/src/serac/numerics/functional/tests/domain_tests.cpp index f7d3f87ffe..4956f73965 100644 --- a/src/serac/numerics/functional/tests/domain_tests.cpp +++ b/src/serac/numerics/functional/tests/domain_tests.cpp @@ -379,6 +379,9 @@ TEST(domain, ofElements2dFindsDofs) constexpr int dim = 2; constexpr int p = 2; auto mesh = import_mesh("patch2D_tris_and_quads.mesh"); + + auto fec = mfem::H1_FECollection(p, dim); + auto fes = mfem::FiniteElementSpace(&mesh, &fec); auto find_element_0 = [](std::vector vertices, int /* attr */) { auto centroid = average(vertices); @@ -387,22 +390,31 @@ TEST(domain, ofElements2dFindsDofs) Domain d0 = Domain::ofElements(mesh, find_element_0); - auto fec = mfem::H1_FECollection(p, dim); - auto fes = mfem::FiniteElementSpace(&mesh, &fec); - mfem::Array dof_indices = d0.dof_list(&fes); + EXPECT_EQ(dof_indices.Size(), 9); - auto find_element_1 = [](std::vector vertices, int /* attr */) { + /////////////////////////////////////// + + auto find_element_4 = [](std::vector vertices, int) { auto centroid = average(vertices); - return (centroid[0] < 0.5) && (centroid[1] < 0.25); + tensor target{{0.533, 0.424}}; + return norm(centroid - target) < 1e-2; }; - Domain d1 = Domain::ofElements(mesh, find_element_1); + Domain d1 = Domain::ofElements(mesh, find_element_4); + + Domain elements_0_and_4 = d0 | d1; + + dof_indices = elements_0_and_4.dof_list(&fes); + EXPECT_EQ(dof_indices.Size(), 12); + + /////////////////////////////////////// + + Domain d2 = EntireDomain(mesh) - elements_0_and_4; - Domain elements_0_and_1 = d0 | d1; + dof_indices = d2.dof_list(&fes); - dof_indices = elements_0_and_1.dof_list(&fes); - EXPECT_EQ(dof_indices.Size(), 15); + EXPECT_EQ(dof_indices.Size(), 22); } int main(int argc, char* argv[]) From 85066126fd4b05b6716b8ba9b92731de17e1ef6c Mon Sep 17 00:00:00 2001 From: Brandon Talamini Date: Fri, 22 Nov 2024 06:12:59 -0800 Subject: [PATCH 54/92] Refactor set operation application to avoid repetition --- src/serac/numerics/functional/domain.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/serac/numerics/functional/domain.cpp b/src/serac/numerics/functional/domain.cpp index 75bcd238e2..d30b91050a 100644 --- a/src/serac/numerics/functional/domain.cpp +++ b/src/serac/numerics/functional/domain.cpp @@ -532,16 +532,24 @@ Domain set_operation(set_op op, const Domain& a, const Domain& b) output.vertex_ids_ = set_operation(op, a.vertex_ids_, b.vertex_ids_); } + // make helper function to populate id lists in output + using Ids = std::vector; + auto apply_op = [&](const Ids& x, const Ids& y) { return set_operation(op, x, y); }; + auto fill_output_members = [&apply_op, &output](const Ids& a_ids, const Ids& b_ids, const Ids& a_mfem_ids, const Ids& b_mfem_ids, mfem::Geometry::Type g) { + auto output_ids = apply_op(a_ids, b_ids); + auto output_mfem_ids = apply_op(a_mfem_ids, b_mfem_ids); + SLIC_ERROR_IF(output_ids.size() != output_mfem_ids.size(), + "Domain object has an invalid state"); + + for (Ids::size_type i = 0; i < output_ids.size(); ++i) { + output.addElement(output_ids[i], output_mfem_ids[i], g); + } + }; + if (output.dim_ == 1) { // BT: if this were Python, I'd use zip() here to iterate through // both vectors simultaneously. Looks like C++23 will have this. - auto edges = set_operation(op, a.edge_ids_, b.edge_ids_); - auto mfem_edges = set_operation(op, a.mfem_edge_ids_, b.mfem_edge_ids_); - SLIC_ERROR_IF(edges.size() != mfem_edges.size(), - "Domain object has an invalid state, the edge index arrays should have the same sizes"); - for (std::vector::size_type i = 0; i < edges.size(); ++i) { - output.addElement(edges[i], mfem_edges[i], mfem::Geometry::SEGMENT); - } + fill_output_members(a.edge_ids_, b.edge_ids_, a.mfem_edge_ids_, b.mfem_edge_ids_, mfem::Geometry::SEGMENT); } if (output.dim_ == 2) { From 3052767cfc3107d5e1f9c28876103b616c692082 Mon Sep 17 00:00:00 2001 From: Brandon Talamini Date: Fri, 22 Nov 2024 06:30:03 -0800 Subject: [PATCH 55/92] Make function to add lists of elements at once --- src/serac/numerics/functional/domain.cpp | 43 +++++++++--------------- src/serac/numerics/functional/domain.hpp | 2 ++ 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/src/serac/numerics/functional/domain.cpp b/src/serac/numerics/functional/domain.cpp index d30b91050a..d760a8d3e4 100644 --- a/src/serac/numerics/functional/domain.cpp +++ b/src/serac/numerics/functional/domain.cpp @@ -319,6 +319,15 @@ void Domain::addElement(int geom_id, int elem_id, mfem::Geometry::Type element_g } } +void Domain::addElements(const std::vector& geom_ids, const std::vector& elem_ids, mfem::Geometry::Type element_geometry) +{ + SLIC_ERROR_IF(geom_ids.size() != elem_ids.size(), "To add elements, you must specify a geom_id AND an elem_id for each element"); + + for (std::vector::size_type i = 0; i < geom_ids.size(); ++i) { + addElement(geom_ids[i], elem_ids[i], element_geometry); + } +} + /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// @@ -532,52 +541,32 @@ Domain set_operation(set_op op, const Domain& a, const Domain& b) output.vertex_ids_ = set_operation(op, a.vertex_ids_, b.vertex_ids_); } - // make helper function to populate id lists in output - using Ids = std::vector; - auto apply_op = [&](const Ids& x, const Ids& y) { return set_operation(op, x, y); }; - auto fill_output_members = [&apply_op, &output](const Ids& a_ids, const Ids& b_ids, const Ids& a_mfem_ids, const Ids& b_mfem_ids, mfem::Geometry::Type g) { - auto output_ids = apply_op(a_ids, b_ids); - auto output_mfem_ids = apply_op(a_mfem_ids, b_mfem_ids); - SLIC_ERROR_IF(output_ids.size() != output_mfem_ids.size(), - "Domain object has an invalid state"); - - for (Ids::size_type i = 0; i < output_ids.size(); ++i) { - output.addElement(output_ids[i], output_mfem_ids[i], g); - } - }; - if (output.dim_ == 1) { // BT: if this were Python, I'd use zip() here to iterate through // both vectors simultaneously. Looks like C++23 will have this. - fill_output_members(a.edge_ids_, b.edge_ids_, a.mfem_edge_ids_, b.mfem_edge_ids_, mfem::Geometry::SEGMENT); + auto edge_ids = set_operation(op, a.edge_ids_, b.edge_ids_); + auto mfem_edge_ids = set_operation(op, a.mfem_edge_ids_, b.edge_ids_); + output.addElements(edge_ids, mfem_edge_ids, mfem::Geometry::SEGMENT); } if (output.dim_ == 2) { auto tris = set_operation(op, a.tri_ids_, b.tri_ids_); auto mfem_tris = set_operation(op, a.mfem_tri_ids_, b.mfem_tri_ids_); - for (std::vector::size_type i = 0; i < tris.size(); ++i) { - output.addElement(tris[i], mfem_tris[i], mfem::Geometry::TRIANGLE); - } + output.addElements(tris, mfem_tris, mfem::Geometry::TRIANGLE); auto quads = set_operation(op, a.quad_ids_, b.quad_ids_); auto mfem_quads = set_operation(op, a.mfem_quad_ids_, b.mfem_quad_ids_); - for (std::vector::size_type i = 0; i < quads.size(); ++i) { - output.addElement(quads[i], mfem_quads[i], mfem::Geometry::SQUARE); - } + output.addElements(quads, mfem_quads, mfem::Geometry::SQUARE); } if (output.dim_ == 3) { auto tets = set_operation(op, a.tet_ids_, b.tet_ids_); auto mfem_tets = set_operation(op, a.mfem_tet_ids_, b.mfem_tet_ids_); - for (std::vector::size_type i = 0; i < tets.size(); ++i) { - output.addElement(tets[i], mfem_tets[i], mfem::Geometry::TETRAHEDRON); - } + output.addElements(tets, mfem_tets, mfem::Geometry::TETRAHEDRON); auto hexes = set_operation(op, a.hex_ids_, b.hex_ids_); auto mfem_hexes = set_operation(op, a.mfem_hex_ids_, b.mfem_hex_ids_); - for (std::vector::size_type i = 0; i < hexes.size(); ++i) { - output.addElement(hexes[i], mfem_hexes[i], mfem::Geometry::CUBE); - } + output.addElements(hexes, mfem_hexes, mfem::Geometry::CUBE); } return output; diff --git a/src/serac/numerics/functional/domain.hpp b/src/serac/numerics/functional/domain.hpp index 66e2ee3149..7dae448b02 100644 --- a/src/serac/numerics/functional/domain.hpp +++ b/src/serac/numerics/functional/domain.hpp @@ -132,6 +132,8 @@ struct Domain { void addElement(int geom_id, int elem_id, mfem::Geometry::Type element_geometry); + void addElements(const std::vector& geom_id, const std::vector& elem_id, mfem::Geometry::Type element_geometry); + /// @brief get mfem degree of freedom list for a given FiniteElementSpace mfem::Array dof_list(mfem::FiniteElementSpace* fes) const; }; From d29960d3b06df2c42d00814632bb210eccc5cc5c Mon Sep 17 00:00:00 2001 From: Brandon Talamini Date: Fri, 22 Nov 2024 06:47:08 -0800 Subject: [PATCH 56/92] Refactor more repeated code --- src/serac/numerics/functional/domain.cpp | 36 +++++++++++------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/serac/numerics/functional/domain.cpp b/src/serac/numerics/functional/domain.cpp index d760a8d3e4..7f75d41fff 100644 --- a/src/serac/numerics/functional/domain.cpp +++ b/src/serac/numerics/functional/domain.cpp @@ -537,36 +537,32 @@ Domain set_operation(set_op op, const Domain& a, const Domain& b) Domain output{a.mesh_, a.dim_}; + using Ids = std::vector; + auto apply_set_op = [&op](const Ids& x, const Ids& y) { return set_operation(op, x, y); }; + if (output.dim_ == 0) { - output.vertex_ids_ = set_operation(op, a.vertex_ids_, b.vertex_ids_); + output.vertex_ids_ = apply_set_op(a.vertex_ids_, b.vertex_ids_); } + auto fill_output_lists = [apply_set_op, &output](const Ids& a_ids, const Ids& a_mfem_ids, + const Ids& b_ids, const Ids& b_mfem_ids, mfem::Geometry::Type g) { + auto output_ids = apply_set_op(a_ids, b_ids); + auto output_mfem_ids = apply_set_op(a_mfem_ids, b_mfem_ids); + output.addElements(output_ids, output_mfem_ids, g); + }; + if (output.dim_ == 1) { - // BT: if this were Python, I'd use zip() here to iterate through - // both vectors simultaneously. Looks like C++23 will have this. - auto edge_ids = set_operation(op, a.edge_ids_, b.edge_ids_); - auto mfem_edge_ids = set_operation(op, a.mfem_edge_ids_, b.edge_ids_); - output.addElements(edge_ids, mfem_edge_ids, mfem::Geometry::SEGMENT); + fill_output_lists(a.edge_ids_, a.mfem_edge_ids_, b.edge_ids_, b.mfem_edge_ids_, mfem::Geometry::SEGMENT); } if (output.dim_ == 2) { - auto tris = set_operation(op, a.tri_ids_, b.tri_ids_); - auto mfem_tris = set_operation(op, a.mfem_tri_ids_, b.mfem_tri_ids_); - output.addElements(tris, mfem_tris, mfem::Geometry::TRIANGLE); - - auto quads = set_operation(op, a.quad_ids_, b.quad_ids_); - auto mfem_quads = set_operation(op, a.mfem_quad_ids_, b.mfem_quad_ids_); - output.addElements(quads, mfem_quads, mfem::Geometry::SQUARE); + fill_output_lists(a.tri_ids_, a.mfem_tri_ids_, b.tri_ids_, b.mfem_tri_ids_, mfem::Geometry::TRIANGLE); + fill_output_lists(a.quad_ids_, a.mfem_quad_ids_, b.quad_ids_, b.mfem_quad_ids_, mfem::Geometry::SQUARE); } if (output.dim_ == 3) { - auto tets = set_operation(op, a.tet_ids_, b.tet_ids_); - auto mfem_tets = set_operation(op, a.mfem_tet_ids_, b.mfem_tet_ids_); - output.addElements(tets, mfem_tets, mfem::Geometry::TETRAHEDRON); - - auto hexes = set_operation(op, a.hex_ids_, b.hex_ids_); - auto mfem_hexes = set_operation(op, a.mfem_hex_ids_, b.mfem_hex_ids_); - output.addElements(hexes, mfem_hexes, mfem::Geometry::CUBE); + fill_output_lists(a.tet_ids_, a.mfem_tet_ids_, b.tet_ids_, b.mfem_tet_ids_, mfem::Geometry::TETRAHEDRON); + fill_output_lists(a.hex_ids_, a.mfem_hex_ids_, b.hex_ids_, b.mfem_hex_ids_, mfem::Geometry::CUBE); } return output; From 0e201b47da007146e129265fedf4bc31b8b3d622 Mon Sep 17 00:00:00 2001 From: Brandon Talamini Date: Fri, 22 Nov 2024 10:16:26 -0800 Subject: [PATCH 57/92] Add test of finding dofs on 3D elements --- src/serac/numerics/functional/domain.hpp | 2 + .../functional/tests/domain_tests.cpp | 49 ++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/serac/numerics/functional/domain.hpp b/src/serac/numerics/functional/domain.hpp index 7dae448b02..9af03d1219 100644 --- a/src/serac/numerics/functional/domain.hpp +++ b/src/serac/numerics/functional/domain.hpp @@ -130,8 +130,10 @@ struct Domain { exit(1); } + /// @brief Add an element to the domain void addElement(int geom_id, int elem_id, mfem::Geometry::Type element_geometry); + /// @brief Add a batch of elements to the domain void addElements(const std::vector& geom_id, const std::vector& elem_id, mfem::Geometry::Type element_geometry); /// @brief get mfem degree of freedom list for a given FiniteElementSpace diff --git a/src/serac/numerics/functional/tests/domain_tests.cpp b/src/serac/numerics/functional/tests/domain_tests.cpp index 4956f73965..aae5d7158e 100644 --- a/src/serac/numerics/functional/tests/domain_tests.cpp +++ b/src/serac/numerics/functional/tests/domain_tests.cpp @@ -374,7 +374,7 @@ TEST(domain, entireDomain3d) EXPECT_EQ(dof_indices.Size(), 25); } -TEST(domain, ofElements2dFindsDofs) +TEST(domain, of2dElementsFindsDofs) { constexpr int dim = 2; constexpr int p = 2; @@ -417,6 +417,53 @@ TEST(domain, ofElements2dFindsDofs) EXPECT_EQ(dof_indices.Size(), 22); } +TEST(domain, of3dElementsFindsDofs) +{ + constexpr int dim = 3; + constexpr int p = 2; + auto mesh = import_mesh("patch3D_tets_and_hexes.mesh"); + + auto fec = mfem::H1_FECollection(p, dim); + auto fes = mfem::FiniteElementSpace(&mesh, &fec); + + auto find_element_0 = [](std::vector vertices, int /* attr */) { + auto centroid = average(vertices); + vec3 target{{3.275, 0.7 , 1.225}}; + return norm(centroid - target) < 1e-2; + }; + + Domain d0 = Domain::ofElements(mesh, find_element_0); + + mfem::Array dof_indices = d0.dof_list(&fes); + + // element 0 is a P2 tetrahedron, so it should have 10 dofs + EXPECT_EQ(dof_indices.Size(), 10); + + /////////////////////////////////////// + + auto find_element_1 = [](std::vector vertices, int) { + auto centroid = average(vertices); + vec3 target{{3.275, 1.2 , 0.725}}; + return norm(centroid - target) < 1e-2; + }; + Domain d1 = Domain::ofElements(mesh, find_element_1); + + Domain elements_0_and_1 = d0 | d1; + + dof_indices = elements_0_and_1.dof_list(&fes); + + // Elements 0 and 1 are P2 tets that share one face -> 14 dofs + EXPECT_EQ(dof_indices.Size(), 14); + + ///////////////////////////////////////// + + Domain d2 = EntireDomain(mesh) - elements_0_and_1; + + dof_indices = d2.dof_list(&fes); + + EXPECT_EQ(dof_indices.Size(), 113); +} + int main(int argc, char* argv[]) { int num_procs, myid; From 17f93beef463a2775dfd79a883a339b2e4931500 Mon Sep 17 00:00:00 2001 From: Brandon Talamini Date: Fri, 22 Nov 2024 11:22:41 -0800 Subject: [PATCH 58/92] Write docstrings --- src/serac/numerics/functional/domain.hpp | 51 +++++++++++++++++++----- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/src/serac/numerics/functional/domain.hpp b/src/serac/numerics/functional/domain.hpp index 9af03d1219..3e3cda9c21 100644 --- a/src/serac/numerics/functional/domain.hpp +++ b/src/serac/numerics/functional/domain.hpp @@ -37,14 +37,37 @@ struct Domain { /// @brief whether the elements in this domain are on the boundary or not Type type_; - /// note: only lists with appropriate dimension (see dim_) will be populated - /// for example, a 2D Domain may have `tri_ids_` and `quad_ids_` non-nonempty, - /// but all other lists will be empty + std::vector vertex_ids_; + + ///@{ + /// @name ElementIds + /// Indices of elements contained in the domain. + /// The first set, (vertex_ids_, edge_ids_, ...) hold the index of an element in + /// this Domain in the set of all elements of like geometry in the mesh. + /// For example, if edge_ids_[0] = 5, then element 0 in this domain is element + /// 5 in the grouping of all edges in the mesh. In other words, these lists + /// hold indices into the "E-vector" of the appropriate geometry. These are + /// used primarily for identifying elements in the domain for participation + /// in integrals. /// - /// these lists hold indices into the "E-vector" of the appropriate geometry + /// The second set, (mfem_edge_ids_, mfem_tri_ids_, ...), gives the ids of + /// elements in this domain in the global mfem::Mesh data structure. These + /// maps are needed to find the dofs that live on a Domain. + /// + /// Instances of Domain are meant to be homogeneous: only lists with + /// appropriate dimension (see dim_) will be populated by the factory + /// functions. For example, a 2D Domain may have `tri_ids_` and `quad_ids_` + /// non-empty, but all other lists will be empty. /// - /// @cond - std::vector vertex_ids_; + /// @note For every entry in the first group (say, edge_ids_), there should + /// be a corresponding entry into the second group (mfem_edge_ids_). This + /// is an intended invariant of the class, but it's not enforced by the data + /// structures. Prefer to use the factory methods (eg, \ref ofElements(...)) + /// to populate these lists automatically, as they repsect this invariant and + /// are tested. Otherwise, use the \ref addElements(...) or addElements(...) + /// methods to add new entities, as this requires you to add both entries and + /// keep the corresponding lists in sync. You are discouraged from + /// manipulating these lists directly. std::vector edge_ids_; std::vector tri_ids_; std::vector quad_ids_; @@ -56,7 +79,7 @@ struct Domain { std::vector mfem_quad_ids_; std::vector mfem_tet_ids_; std::vector mfem_hex_ids_; - /// @endcond + ///@} Domain(const mfem::Mesh& m, int d, Type type = Domain::Type::Elements) : mesh_(m), dim_(d), type_(type) {} @@ -130,14 +153,22 @@ struct Domain { exit(1); } + /// @brief get mfem degree of freedom list for a given FiniteElementSpace + mfem::Array dof_list(mfem::FiniteElementSpace* fes) const; + /// @brief Add an element to the domain + /// + /// This is meant for internal use on the class. Prefer to use the factory + /// methods (ofElements, ofBoundaryElements, etc) to create domains and + /// thereby populate the element lists. void addElement(int geom_id, int elem_id, mfem::Geometry::Type element_geometry); /// @brief Add a batch of elements to the domain + /// + /// This is meant for internal use on the class. Prefer to use the factory + /// methods (ofElements, ofBoundaryElements, etc) to create domains and + /// thereby populate the element lists. void addElements(const std::vector& geom_id, const std::vector& elem_id, mfem::Geometry::Type element_geometry); - - /// @brief get mfem degree of freedom list for a given FiniteElementSpace - mfem::Array dof_list(mfem::FiniteElementSpace* fes) const; }; /// @brief constructs a domain from all the elements in a mesh From 9cb905181820b5196234226b5ad3e53288985f0e Mon Sep 17 00:00:00 2001 From: Brandon Talamini Date: Fri, 22 Nov 2024 11:24:19 -0800 Subject: [PATCH 59/92] Make style --- src/serac/numerics/functional/domain.cpp | 34 ++-- src/serac/numerics/functional/domain.hpp | 7 +- .../functional/tests/domain_tests.cpp | 158 +++++++++--------- 3 files changed, 101 insertions(+), 98 deletions(-) diff --git a/src/serac/numerics/functional/domain.cpp b/src/serac/numerics/functional/domain.cpp index 7f75d41fff..cfeb5f7b89 100644 --- a/src/serac/numerics/functional/domain.cpp +++ b/src/serac/numerics/functional/domain.cpp @@ -319,9 +319,11 @@ void Domain::addElement(int geom_id, int elem_id, mfem::Geometry::Type element_g } } -void Domain::addElements(const std::vector& geom_ids, const std::vector& elem_ids, mfem::Geometry::Type element_geometry) +void Domain::addElements(const std::vector& geom_ids, const std::vector& elem_ids, + mfem::Geometry::Type element_geometry) { - SLIC_ERROR_IF(geom_ids.size() != elem_ids.size(), "To add elements, you must specify a geom_id AND an elem_id for each element"); + SLIC_ERROR_IF(geom_ids.size() != elem_ids.size(), + "To add elements, you must specify a geom_id AND an elem_id for each element"); for (std::vector::size_type i = 0; i < geom_ids.size(); ++i) { addElement(geom_ids[i], elem_ids[i], element_geometry); @@ -484,11 +486,11 @@ mfem::Array Domain::dof_list(mfem::FiniteElementSpace* fes) const Domain EntireDomain(const mfem::Mesh& mesh) { switch (mesh.SpaceDimension()) { - case 2: - return Domain::ofElements(mesh, [](std::vector, int) { return true; }); + case 2: + return Domain::ofElements(mesh, [](std::vector, int) { return true; }); break; - case 3: - return Domain::ofElements(mesh, [](std::vector, int) { return true; }); + case 3: + return Domain::ofElements(mesh, [](std::vector, int) { return true; }); break; default: SLIC_ERROR("In valid spatial dimension. Domains may only be created on 2D or 3D meshes."); @@ -499,11 +501,11 @@ Domain EntireDomain(const mfem::Mesh& mesh) Domain EntireBoundary(const mfem::Mesh& mesh) { switch (mesh.SpaceDimension()) { - case 2: - return Domain::ofBoundaryElements(mesh, [](std::vector, int) { return true; }); + case 2: + return Domain::ofBoundaryElements(mesh, [](std::vector, int) { return true; }); break; - case 3: - return Domain::ofBoundaryElements(mesh, [](std::vector, int) { return true; }); + case 3: + return Domain::ofBoundaryElements(mesh, [](std::vector, int) { return true; }); break; default: SLIC_ERROR("In valid spatial dimension. Domains may only be created on 2D or 3D meshes."); @@ -537,18 +539,18 @@ Domain set_operation(set_op op, const Domain& a, const Domain& b) Domain output{a.mesh_, a.dim_}; - using Ids = std::vector; + using Ids = std::vector; auto apply_set_op = [&op](const Ids& x, const Ids& y) { return set_operation(op, x, y); }; if (output.dim_ == 0) { output.vertex_ids_ = apply_set_op(a.vertex_ids_, b.vertex_ids_); } - auto fill_output_lists = [apply_set_op, &output](const Ids& a_ids, const Ids& a_mfem_ids, - const Ids& b_ids, const Ids& b_mfem_ids, mfem::Geometry::Type g) { - auto output_ids = apply_set_op(a_ids, b_ids); - auto output_mfem_ids = apply_set_op(a_mfem_ids, b_mfem_ids); - output.addElements(output_ids, output_mfem_ids, g); + auto fill_output_lists = [apply_set_op, &output](const Ids& a_ids, const Ids& a_mfem_ids, const Ids& b_ids, + const Ids& b_mfem_ids, mfem::Geometry::Type g) { + auto output_ids = apply_set_op(a_ids, b_ids); + auto output_mfem_ids = apply_set_op(a_mfem_ids, b_mfem_ids); + output.addElements(output_ids, output_mfem_ids, g); }; if (output.dim_ == 1) { diff --git a/src/serac/numerics/functional/domain.hpp b/src/serac/numerics/functional/domain.hpp index 3e3cda9c21..66f6e8ca82 100644 --- a/src/serac/numerics/functional/domain.hpp +++ b/src/serac/numerics/functional/domain.hpp @@ -53,8 +53,8 @@ struct Domain { /// The second set, (mfem_edge_ids_, mfem_tri_ids_, ...), gives the ids of /// elements in this domain in the global mfem::Mesh data structure. These /// maps are needed to find the dofs that live on a Domain. - /// - /// Instances of Domain are meant to be homogeneous: only lists with + /// + /// Instances of Domain are meant to be homogeneous: only lists with /// appropriate dimension (see dim_) will be populated by the factory /// functions. For example, a 2D Domain may have `tri_ids_` and `quad_ids_` /// non-empty, but all other lists will be empty. @@ -168,7 +168,8 @@ struct Domain { /// This is meant for internal use on the class. Prefer to use the factory /// methods (ofElements, ofBoundaryElements, etc) to create domains and /// thereby populate the element lists. - void addElements(const std::vector& geom_id, const std::vector& elem_id, mfem::Geometry::Type element_geometry); + void addElements(const std::vector& geom_id, const std::vector& elem_id, + mfem::Geometry::Type element_geometry); }; /// @brief constructs a domain from all the elements in a mesh diff --git a/src/serac/numerics/functional/tests/domain_tests.cpp b/src/serac/numerics/functional/tests/domain_tests.cpp index aae5d7158e..1fb197dc2f 100644 --- a/src/serac/numerics/functional/tests/domain_tests.cpp +++ b/src/serac/numerics/functional/tests/domain_tests.cpp @@ -338,130 +338,130 @@ TEST(domain, of_elements) TEST(domain, entireDomain2d) { - constexpr int dim = 2; - constexpr int p = 1; - auto mesh = import_mesh("patch2D_tris_and_quads.mesh"); + constexpr int dim = 2; + constexpr int p = 1; + auto mesh = import_mesh("patch2D_tris_and_quads.mesh"); - Domain d0 = EntireDomain(mesh); + Domain d0 = EntireDomain(mesh); - EXPECT_EQ(d0.dim_, 2); - EXPECT_EQ(d0.tri_ids_.size(), 2); - EXPECT_EQ(d0.quad_ids_.size(), 4); + EXPECT_EQ(d0.dim_, 2); + EXPECT_EQ(d0.tri_ids_.size(), 2); + EXPECT_EQ(d0.quad_ids_.size(), 4); - auto fec = mfem::H1_FECollection(p, dim); - auto fes = mfem::FiniteElementSpace(&mesh, &fec); + auto fec = mfem::H1_FECollection(p, dim); + auto fes = mfem::FiniteElementSpace(&mesh, &fec); - mfem::Array dof_indices = d0.dof_list(&fes); - EXPECT_EQ(dof_indices.Size(), 8); + mfem::Array dof_indices = d0.dof_list(&fes); + EXPECT_EQ(dof_indices.Size(), 8); } TEST(domain, entireDomain3d) { - constexpr int dim = 3; - constexpr int p = 1; - auto mesh = import_mesh("patch3D_tets_and_hexes.mesh"); + constexpr int dim = 3; + constexpr int p = 1; + auto mesh = import_mesh("patch3D_tets_and_hexes.mesh"); - Domain d0 = EntireDomain(mesh); + Domain d0 = EntireDomain(mesh); - EXPECT_EQ(d0.dim_, 3); - EXPECT_EQ(d0.tet_ids_.size(), 12); - EXPECT_EQ(d0.hex_ids_.size(), 7); + EXPECT_EQ(d0.dim_, 3); + EXPECT_EQ(d0.tet_ids_.size(), 12); + EXPECT_EQ(d0.hex_ids_.size(), 7); - auto fec = mfem::H1_FECollection(p, dim); - auto fes = mfem::FiniteElementSpace(&mesh, &fec); + auto fec = mfem::H1_FECollection(p, dim); + auto fes = mfem::FiniteElementSpace(&mesh, &fec); - mfem::Array dof_indices = d0.dof_list(&fes); - EXPECT_EQ(dof_indices.Size(), 25); + mfem::Array dof_indices = d0.dof_list(&fes); + EXPECT_EQ(dof_indices.Size(), 25); } TEST(domain, of2dElementsFindsDofs) { - constexpr int dim = 2; - constexpr int p = 2; - auto mesh = import_mesh("patch2D_tris_and_quads.mesh"); + constexpr int dim = 2; + constexpr int p = 2; + auto mesh = import_mesh("patch2D_tris_and_quads.mesh"); + + auto fec = mfem::H1_FECollection(p, dim); + auto fes = mfem::FiniteElementSpace(&mesh, &fec); - auto fec = mfem::H1_FECollection(p, dim); - auto fes = mfem::FiniteElementSpace(&mesh, &fec); - - auto find_element_0 = [](std::vector vertices, int /* attr */) { - auto centroid = average(vertices); - return (centroid[0] < 0.5) && (centroid[1] < 0.25); - }; + auto find_element_0 = [](std::vector vertices, int /* attr */) { + auto centroid = average(vertices); + return (centroid[0] < 0.5) && (centroid[1] < 0.25); + }; - Domain d0 = Domain::ofElements(mesh, find_element_0); + Domain d0 = Domain::ofElements(mesh, find_element_0); - mfem::Array dof_indices = d0.dof_list(&fes); + mfem::Array dof_indices = d0.dof_list(&fes); - EXPECT_EQ(dof_indices.Size(), 9); + EXPECT_EQ(dof_indices.Size(), 9); - /////////////////////////////////////// + /////////////////////////////////////// - auto find_element_4 = [](std::vector vertices, int) { - auto centroid = average(vertices); - tensor target{{0.533, 0.424}}; - return norm(centroid - target) < 1e-2; - }; - Domain d1 = Domain::ofElements(mesh, find_element_4); + auto find_element_4 = [](std::vector vertices, int) { + auto centroid = average(vertices); + tensor target{{0.533, 0.424}}; + return norm(centroid - target) < 1e-2; + }; + Domain d1 = Domain::ofElements(mesh, find_element_4); - Domain elements_0_and_4 = d0 | d1; + Domain elements_0_and_4 = d0 | d1; - dof_indices = elements_0_and_4.dof_list(&fes); - EXPECT_EQ(dof_indices.Size(), 12); + dof_indices = elements_0_and_4.dof_list(&fes); + EXPECT_EQ(dof_indices.Size(), 12); - /////////////////////////////////////// + /////////////////////////////////////// - Domain d2 = EntireDomain(mesh) - elements_0_and_4; + Domain d2 = EntireDomain(mesh) - elements_0_and_4; - dof_indices = d2.dof_list(&fes); + dof_indices = d2.dof_list(&fes); - EXPECT_EQ(dof_indices.Size(), 22); + EXPECT_EQ(dof_indices.Size(), 22); } TEST(domain, of3dElementsFindsDofs) { - constexpr int dim = 3; - constexpr int p = 2; - auto mesh = import_mesh("patch3D_tets_and_hexes.mesh"); + constexpr int dim = 3; + constexpr int p = 2; + auto mesh = import_mesh("patch3D_tets_and_hexes.mesh"); + + auto fec = mfem::H1_FECollection(p, dim); + auto fes = mfem::FiniteElementSpace(&mesh, &fec); - auto fec = mfem::H1_FECollection(p, dim); - auto fes = mfem::FiniteElementSpace(&mesh, &fec); - - auto find_element_0 = [](std::vector vertices, int /* attr */) { - auto centroid = average(vertices); - vec3 target{{3.275, 0.7 , 1.225}}; - return norm(centroid - target) < 1e-2; - }; + auto find_element_0 = [](std::vector vertices, int /* attr */) { + auto centroid = average(vertices); + vec3 target{{3.275, 0.7, 1.225}}; + return norm(centroid - target) < 1e-2; + }; - Domain d0 = Domain::ofElements(mesh, find_element_0); + Domain d0 = Domain::ofElements(mesh, find_element_0); - mfem::Array dof_indices = d0.dof_list(&fes); + mfem::Array dof_indices = d0.dof_list(&fes); - // element 0 is a P2 tetrahedron, so it should have 10 dofs - EXPECT_EQ(dof_indices.Size(), 10); + // element 0 is a P2 tetrahedron, so it should have 10 dofs + EXPECT_EQ(dof_indices.Size(), 10); - /////////////////////////////////////// + /////////////////////////////////////// - auto find_element_1 = [](std::vector vertices, int) { - auto centroid = average(vertices); - vec3 target{{3.275, 1.2 , 0.725}}; - return norm(centroid - target) < 1e-2; - }; - Domain d1 = Domain::ofElements(mesh, find_element_1); + auto find_element_1 = [](std::vector vertices, int) { + auto centroid = average(vertices); + vec3 target{{3.275, 1.2, 0.725}}; + return norm(centroid - target) < 1e-2; + }; + Domain d1 = Domain::ofElements(mesh, find_element_1); - Domain elements_0_and_1 = d0 | d1; + Domain elements_0_and_1 = d0 | d1; - dof_indices = elements_0_and_1.dof_list(&fes); + dof_indices = elements_0_and_1.dof_list(&fes); - // Elements 0 and 1 are P2 tets that share one face -> 14 dofs - EXPECT_EQ(dof_indices.Size(), 14); + // Elements 0 and 1 are P2 tets that share one face -> 14 dofs + EXPECT_EQ(dof_indices.Size(), 14); - ///////////////////////////////////////// + ///////////////////////////////////////// - Domain d2 = EntireDomain(mesh) - elements_0_and_1; + Domain d2 = EntireDomain(mesh) - elements_0_and_1; - dof_indices = d2.dof_list(&fes); + dof_indices = d2.dof_list(&fes); - EXPECT_EQ(dof_indices.Size(), 113); + EXPECT_EQ(dof_indices.Size(), 113); } int main(int argc, char* argv[]) From 3365ae3904676a5e2b749f1b2ed6392918517195 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Tue, 26 Nov 2024 11:04:49 -0800 Subject: [PATCH 60/92] add error message when trying to use an unsupported integral configuration --- src/serac/numerics/functional/functional.hpp | 11 +++++++++++ src/serac/numerics/functional/functional_qoi.inl | 11 ++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/serac/numerics/functional/functional.hpp b/src/serac/numerics/functional/functional.hpp index 85def6b9ce..d430a246c3 100644 --- a/src/serac/numerics/functional/functional.hpp +++ b/src/serac/numerics/functional/functional.hpp @@ -101,6 +101,15 @@ inline void check_for_unsupported_elements(const mfem::Mesh& mesh) } } +inline void check_interior_face_compatibility(const mfem::Mesh & mesh, const FunctionSpace space) { + if (space.family == Family::L2) { + const mfem::ParMesh * pmesh = dynamic_cast< const mfem::ParMesh * >(&mesh); + if (pmesh) { + SLIC_ERROR_IF(pmesh->GetNSharedFaces() > 0, "interior face integrals involving DG function spaces don't currently support meshes with shared faces"); + } + } +} + /** * @brief create an mfem::ParFiniteElementSpace from one of serac's * tag types: H1, Hcurl, L2 @@ -334,8 +343,10 @@ class Functional { std::vector< uint32_t > arg_vec = {args ...}; for (uint32_t i : arg_vec) { domain.insert_restriction(trial_space_[i], trial_function_spaces_[i]); + check_interior_face_compatibility(domain.mesh_, trial_function_spaces_[i]); } domain.insert_restriction(test_space_, test_function_space_); + check_interior_face_compatibility(domain.mesh_, test_function_space_); using signature = test(decltype(serac::type(trial_spaces))...); integrals_.push_back( diff --git a/src/serac/numerics/functional/functional_qoi.inl b/src/serac/numerics/functional/functional_qoi.inl index b291816622..a894624a1d 100644 --- a/src/serac/numerics/functional/functional_qoi.inl +++ b/src/serac/numerics/functional/functional_qoi.inl @@ -186,7 +186,15 @@ public: } /** - * @brief TODO + * @tparam dim The dimension of the boundary element (1 for line, 2 for quad, etc) + * @tparam lambda the type of the integrand functor: must implement operator() with an appropriate function signature + * @param[in] integrand The user-provided quadrature function, see @p Integral + * @param[in] domain which elements make up the domain of integration + * + * @brief Adds a interior face integral term to the Functional object + * + * @note The @p Dimension parameters are used to assist in the deduction of the @a geometry_dim + * and @a spatial_dim template parameter */ template void AddInteriorFaceIntegral(Dimension, DependsOn, const Integrand& integrand, Domain& domain) @@ -196,6 +204,7 @@ public: std::vector< uint32_t > arg_vec = {args ...}; for (uint32_t i : arg_vec) { domain.insert_restriction(trial_space_[i], trial_function_spaces_[i]); + check_interior_face_compatibility(domain.mesh_, trial_function_spaces_[i]); } using signature = test(decltype(serac::type(trial_spaces))...); From 32349707b852bc59e691790ca3191da9d2ca719e Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Tue, 26 Nov 2024 11:06:43 -0800 Subject: [PATCH 61/92] run dg test on one processor --- src/serac/numerics/functional/tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serac/numerics/functional/tests/CMakeLists.txt b/src/serac/numerics/functional/tests/CMakeLists.txt index ff8ce856d1..14d1a5a744 100644 --- a/src/serac/numerics/functional/tests/CMakeLists.txt +++ b/src/serac/numerics/functional/tests/CMakeLists.txt @@ -20,6 +20,7 @@ set(functional_serial_test_sources simplex_basis_function_unit_tests.cpp element_restriction_tests.cpp dg_restriction_operators.cpp + functional_basic_dg.cpp bug_boundary_qoi.cpp bug_residual.cpp domain_tests.cpp @@ -41,7 +42,6 @@ set(functional_parallel_test_sources functional_with_domain.cpp functional_basic_h1_scalar.cpp functional_basic_h1_vector.cpp - functional_basic_dg.cpp functional_multiphysics.cpp functional_qoi.cpp functional_nonlinear.cpp From e3a3fdde2062d03df6fc7c863e816b91fe6f91c4 Mon Sep 17 00:00:00 2001 From: Agent Style Date: Tue, 26 Nov 2024 11:11:34 -0800 Subject: [PATCH 62/92] Apply style updates --- examples/buckling/cylinder.cpp | 6 +- examples/contact/beam_bending.cpp | 6 +- examples/contact/ironing.cpp | 6 +- examples/contact/sphere.cpp | 6 +- examples/contact/twist.cpp | 6 +- .../simple_conduction/without_input_file.cpp | 2 +- .../functional/boundary_integral_kernels.hpp | 3 +- .../functional/detail/hexahedron_H1.inl | 2 +- .../functional/detail/hexahedron_Hcurl.inl | 2 +- .../functional/detail/hexahedron_L2.inl | 2 +- .../functional/detail/quadrilateral_H1.inl | 4 +- .../functional/detail/quadrilateral_Hcurl.inl | 2 +- .../functional/detail/quadrilateral_L2.inl | 58 ++- .../numerics/functional/detail/segment_H1.inl | 2 +- .../functional/detail/segment_Hcurl.inl | 2 +- .../numerics/functional/detail/segment_L2.inl | 11 +- .../functional/detail/triangle_H1.inl | 2 +- .../functional/detail/triangle_L2.inl | 36 +- src/serac/numerics/functional/domain.cpp | 71 ++- src/serac/numerics/functional/domain.hpp | 45 +- .../functional/domain_integral_kernels.hpp | 11 +- .../functional/element_restriction.cpp | 56 ++- .../functional/element_restriction.hpp | 12 +- .../numerics/functional/finite_element.hpp | 15 +- src/serac/numerics/functional/functional.hpp | 164 ++++--- .../numerics/functional/functional_qoi.inl | 76 ++-- .../numerics/functional/geometric_factors.cpp | 28 +- .../numerics/functional/geometric_factors.hpp | 1 - src/serac/numerics/functional/integral.hpp | 28 +- .../interior_face_integral_kernels.hpp | 32 +- .../functional/shape_aware_functional.hpp | 4 +- .../functional/tests/bug_residual.cpp | 51 ++- .../functional/tests/check_gradient.hpp | 9 +- .../numerics/functional/tests/cuda_sandbox.cu | 130 +++--- .../tests/dg_restriction_operators.cpp | 405 +++++++++--------- .../functional/tests/domain_tests.cpp | 1 - .../tests/element_restriction_tests.cpp | 90 ++-- .../functional/tests/functional_basic_dg.cpp | 22 +- .../functional/tests/functional_nonlinear.cpp | 4 +- .../functional/tests/functional_qoi.cpp | 10 +- .../tests/functional_with_domain.cpp | 1 - src/serac/numerics/functional/typedefs.hpp | 8 +- src/serac/physics/fit.hpp | 6 +- src/serac/physics/heat_transfer.hpp | 19 +- src/serac/physics/solid_mechanics.hpp | 26 +- .../physics/state/finite_element_vector.hpp | 3 +- src/serac/physics/tests/beam_bending.cpp | 10 +- src/serac/physics/tests/contact_beam.cpp | 4 +- src/serac/physics/tests/contact_patch.cpp | 4 +- .../physics/tests/contact_patch_tied.cpp | 4 +- .../physics/tests/dynamic_solid_adjoint.cpp | 31 +- .../physics/tests/dynamic_thermal_adjoint.cpp | 29 +- .../physics/tests/lce_Bertoldi_lattice.cpp | 2 +- .../physics/tests/lce_Brighenti_tensile.cpp | 26 +- .../physics/tests/parameterized_thermal.cpp | 2 +- .../parameterized_thermomechanics_example.cpp | 10 +- src/serac/physics/tests/solid.cpp | 17 +- .../physics/tests/solid_dynamics_patch.cpp | 16 +- src/serac/physics/tests/solid_finite_diff.cpp | 20 +- .../physics/tests/solid_reaction_adjoint.cpp | 16 +- .../physics/tests/solid_robin_condition.cpp | 4 +- .../physics/tests/thermal_dynamics_patch.cpp | 7 +- .../physics/tests/thermal_finite_diff.cpp | 2 +- src/serac/physics/tests/thermal_mechanics.cpp | 4 +- .../physics/tests/thermal_nonlinear_solve.cpp | 6 +- .../physics/tests/thermal_robin_condition.cpp | 6 +- src/serac/physics/thermomechanics.hpp | 15 +- 67 files changed, 846 insertions(+), 875 deletions(-) diff --git a/examples/buckling/cylinder.cpp b/examples/buckling/cylinder.cpp index 483b454144..6730887fff 100644 --- a/examples/buckling/cylinder.cpp +++ b/examples/buckling/cylinder.cpp @@ -124,8 +124,8 @@ int main(int argc, char* argv[]) // Create and refine mesh std::string filename = SERAC_REPO_DIR "/data/meshes/hollow-cylinder.mesh"; - auto mesh = mesh::refineAndDistribute(serac::buildMeshFromFile(filename), serial_refinement, parallel_refinement); - auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto mesh = mesh::refineAndDistribute(serac::buildMeshFromFile(filename), serial_refinement, parallel_refinement); + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // Surface attributes for boundary conditions std::set xneg{2}; @@ -159,7 +159,7 @@ int main(int argc, char* argv[]) auto lambda = 1.0; auto G = 0.1; solid_mechanics::NeoHookean mat{.density = 1.0, .K = (3 * lambda + 2 * G) / 3, .G = G}; - Domain whole_mesh = EntireDomain(pmesh); + Domain whole_mesh = EntireDomain(pmesh); solid_solver->setMaterial(mat, whole_mesh); // Set up essential boundary conditions diff --git a/examples/contact/beam_bending.cpp b/examples/contact/beam_bending.cpp index 694c3660e8..c9d6c0f5a7 100644 --- a/examples/contact/beam_bending.cpp +++ b/examples/contact/beam_bending.cpp @@ -35,8 +35,8 @@ int main(int argc, char* argv[]) // Construct the appropriate dimension mesh and give it to the data store std::string filename = SERAC_REPO_DIR "/data/meshes/beam-hex-with-contact-block.mesh"; - auto mesh = serac::mesh::refineAndDistribute(serac::buildMeshFromFile(filename), 2, 0); - auto & pmesh = serac::StateManager::setMesh(std::move(mesh), "beam_mesh"); + auto mesh = serac::mesh::refineAndDistribute(serac::buildMeshFromFile(filename), 2, 0); + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), "beam_mesh"); serac::LinearSolverOptions linear_options{.linear_solver = serac::LinearSolver::Strumpack, .print_level = 1}; #ifndef MFEM_USE_STRUMPACK @@ -78,7 +78,7 @@ int main(int argc, char* argv[]) solid_solver.setParameter(1, G_field); serac::solid_mechanics::ParameterizedNeoHookeanSolid mat{1.0, 0.0, 0.0}; - serac::Domain whole_mesh = serac::EntireDomain(pmesh); + serac::Domain whole_mesh = serac::EntireDomain(pmesh); solid_solver.setMaterial(serac::DependsOn<0, 1>{}, mat, whole_mesh); // Pass the BC information to the solver object diff --git a/examples/contact/ironing.cpp b/examples/contact/ironing.cpp index 6257f8003e..cf1deb289e 100644 --- a/examples/contact/ironing.cpp +++ b/examples/contact/ironing.cpp @@ -35,8 +35,8 @@ int main(int argc, char* argv[]) // Construct the appropriate dimension mesh and give it to the data store std::string filename = SERAC_REPO_DIR "/data/meshes/ironing.mesh"; - auto mesh = serac::mesh::refineAndDistribute(serac::buildMeshFromFile(filename), 2, 0); - auto & pmesh = serac::StateManager::setMesh(std::move(mesh), "ironing_mesh"); + auto mesh = serac::mesh::refineAndDistribute(serac::buildMeshFromFile(filename), 2, 0); + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), "ironing_mesh"); serac::LinearSolverOptions linear_options{.linear_solver = serac::LinearSolver::Strumpack, .print_level = 1}; @@ -79,7 +79,7 @@ int main(int argc, char* argv[]) solid_solver.setParameter(1, G_field); serac::solid_mechanics::ParameterizedNeoHookeanSolid mat{1.0, 0.0, 0.0}; - serac::Domain whole_mesh = serac::EntireDomain(pmesh); + serac::Domain whole_mesh = serac::EntireDomain(pmesh); solid_solver.setMaterial(serac::DependsOn<0, 1>{}, mat, whole_mesh); // Pass the BC information to the solver object diff --git a/examples/contact/sphere.cpp b/examples/contact/sphere.cpp index a5d23a3d28..4c34b44f70 100644 --- a/examples/contact/sphere.cpp +++ b/examples/contact/sphere.cpp @@ -51,8 +51,8 @@ int main(int argc, char* argv[]) cube_mesh.SetCurvature(p); std::vector mesh_ptrs{&ball_mesh, &cube_mesh}; - auto mesh = serac::mesh::refineAndDistribute(mfem::Mesh(mesh_ptrs.data(), static_cast(mesh_ptrs.size())), 0, 0); - auto & pmesh = serac::StateManager::setMesh(std::move(mesh), "sphere_mesh"); + auto mesh = serac::mesh::refineAndDistribute(mfem::Mesh(mesh_ptrs.data(), static_cast(mesh_ptrs.size())), 0, 0); + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), "sphere_mesh"); serac::LinearSolverOptions linear_options{.linear_solver = serac::LinearSolver::Strumpack, .print_level = 1}; #ifndef MFEM_USE_STRUMPACK @@ -75,7 +75,7 @@ int main(int argc, char* argv[]) nonlinear_options, linear_options, serac::solid_mechanics::default_quasistatic_options, name, "sphere_mesh"); serac::solid_mechanics::NeoHookean mat{1.0, 10.0, 0.25}; - serac::Domain whole_mesh = serac::EntireDomain(pmesh); + serac::Domain whole_mesh = serac::EntireDomain(pmesh); solid_solver.setMaterial(mat, whole_mesh); // Pass the BC information to the solver object diff --git a/examples/contact/twist.cpp b/examples/contact/twist.cpp index ca096c4178..1afa8499c9 100644 --- a/examples/contact/twist.cpp +++ b/examples/contact/twist.cpp @@ -37,8 +37,8 @@ int main(int argc, char* argv[]) // Construct the appropriate dimension mesh and give it to the data store std::string filename = SERAC_REPO_DIR "/data/meshes/twohex_for_contact.mesh"; - auto mesh = serac::mesh::refineAndDistribute(serac::buildMeshFromFile(filename), 3, 0); - auto & pmesh = serac::StateManager::setMesh(std::move(mesh), "twist_mesh"); + auto mesh = serac::mesh::refineAndDistribute(serac::buildMeshFromFile(filename), 3, 0); + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), "twist_mesh"); serac::LinearSolverOptions linear_options{.linear_solver = serac::LinearSolver::Strumpack, .print_level = 1}; #ifndef MFEM_USE_STRUMPACK @@ -61,7 +61,7 @@ int main(int argc, char* argv[]) nonlinear_options, linear_options, serac::solid_mechanics::default_quasistatic_options, name, "twist_mesh"); serac::solid_mechanics::NeoHookean mat{1.0, 10.0, 10.0}; - serac::Domain whole_mesh = serac::EntireDomain(pmesh); + serac::Domain whole_mesh = serac::EntireDomain(pmesh); solid_solver.setMaterial(mat, whole_mesh); // Pass the BC information to the solver object diff --git a/examples/simple_conduction/without_input_file.cpp b/examples/simple_conduction/without_input_file.cpp index 3e6ab5fe52..3994fba2e0 100644 --- a/examples/simple_conduction/without_input_file.cpp +++ b/examples/simple_conduction/without_input_file.cpp @@ -38,7 +38,7 @@ int main(int argc, char* argv[]) std::string mesh_tag{"mesh"}; - auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // _create_mesh_end // _create_module_start diff --git a/src/serac/numerics/functional/boundary_integral_kernels.hpp b/src/serac/numerics/functional/boundary_integral_kernels.hpp index a8bff44e74..d1739f8e6c 100644 --- a/src/serac/numerics/functional/boundary_integral_kernels.hpp +++ b/src/serac/numerics/functional/boundary_integral_kernels.hpp @@ -297,7 +297,8 @@ void action_of_gradient_kernel(const double* dU, double* dR, derivatives_type* q * @param[in] num_elements The number of elements in the mesh */ template -void element_gradient_kernel(ExecArrayView dK, derivatives_type* qf_derivatives, std::size_t num_elements) +void element_gradient_kernel(ExecArrayView dK, derivatives_type* qf_derivatives, + std::size_t num_elements) { using test_element = finite_element; using trial_element = finite_element; diff --git a/src/serac/numerics/functional/detail/hexahedron_H1.inl b/src/serac/numerics/functional/detail/hexahedron_H1.inl index 062fd7ad53..1e8d0a02b2 100644 --- a/src/serac/numerics/functional/detail/hexahedron_H1.inl +++ b/src/serac/numerics/functional/detail/hexahedron_H1.inl @@ -152,7 +152,7 @@ struct finite_element > { tensor dphi_j_dxi = {G(qx, jx) * B(qy, jy) * B(qz, jz), B(qx, jx) * G(qy, jy) * B(qz, jz), B(qx, jx) * B(qy, jy) * G(qz, jz)}; - int Q = (qz * q + qy) * q + qx; + int Q = (qz * q + qy) * q + qx; const auto& d00 = get<0>(get<0>(input(Q))); const auto& d01 = get<1>(get<0>(input(Q))); const auto& d10 = get<0>(get<1>(input(Q))); diff --git a/src/serac/numerics/functional/detail/hexahedron_Hcurl.inl b/src/serac/numerics/functional/detail/hexahedron_Hcurl.inl index 30cfc2cbe6..d62e8f7860 100644 --- a/src/serac/numerics/functional/detail/hexahedron_Hcurl.inl +++ b/src/serac/numerics/functional/detail/hexahedron_Hcurl.inl @@ -318,7 +318,7 @@ struct finite_element> { break; } - int Q = (qz * q + qy) * q + qx; + int Q = (qz * q + qy) * q + qx; const auto& d00 = get<0>(get<0>(input(Q))); const auto& d01 = get<1>(get<0>(input(Q))); const auto& d10 = get<0>(get<1>(input(Q))); diff --git a/src/serac/numerics/functional/detail/hexahedron_L2.inl b/src/serac/numerics/functional/detail/hexahedron_L2.inl index 9498191817..379a36222f 100644 --- a/src/serac/numerics/functional/detail/hexahedron_L2.inl +++ b/src/serac/numerics/functional/detail/hexahedron_L2.inl @@ -156,7 +156,7 @@ struct finite_element > { tensor dphi_j_dxi = {G(qx, jx) * B(qy, jy) * B(qz, jz), B(qx, jx) * G(qy, jy) * B(qz, jz), B(qx, jx) * B(qy, jy) * G(qz, jz)}; - int Q = (qz * q + qy) * q + qx; + int Q = (qz * q + qy) * q + qx; const auto& d00 = get<0>(get<0>(input(Q))); const auto& d01 = get<1>(get<0>(input(Q))); const auto& d10 = get<0>(get<1>(input(Q))); diff --git a/src/serac/numerics/functional/detail/quadrilateral_H1.inl b/src/serac/numerics/functional/detail/quadrilateral_H1.inl index 30d92eb08c..b0ef136ce9 100644 --- a/src/serac/numerics/functional/detail/quadrilateral_H1.inl +++ b/src/serac/numerics/functional/detail/quadrilateral_H1.inl @@ -32,7 +32,7 @@ struct finite_element > { using residual_type = typename std::conditional, tensor >::type; - using dof_type = tensor; + using dof_type = tensor; using dof_type_if = dof_type; using value_type = typename std::conditional >::type; @@ -175,7 +175,7 @@ struct finite_element > { double phi_j = B(qx, jx) * B(qy, jy); tensor dphi_j_dxi = {G(qx, jx) * B(qy, jy), B(qx, jx) * G(qy, jy)}; - int Q = qy * q + qx; + int Q = qy * q + qx; const auto& d00 = get<0>(get<0>(input(Q))); const auto& d01 = get<1>(get<0>(input(Q))); const auto& d10 = get<0>(get<1>(input(Q))); diff --git a/src/serac/numerics/functional/detail/quadrilateral_Hcurl.inl b/src/serac/numerics/functional/detail/quadrilateral_Hcurl.inl index 054aa82efb..168d4fc0a6 100644 --- a/src/serac/numerics/functional/detail/quadrilateral_Hcurl.inl +++ b/src/serac/numerics/functional/detail/quadrilateral_Hcurl.inl @@ -272,7 +272,7 @@ struct finite_element > { double curl_phi_j = (dir == 0) * -B1(qx, jx) * G2(qy, jy) + (dir == 1) * B1(qy, jy) * G2(qx, jx); - int Q = qy * q + qx; + int Q = qy * q + qx; const auto& d00 = get<0>(get<0>(input(Q))); const auto& d01 = get<1>(get<0>(input(Q))); const auto& d10 = get<0>(get<1>(input(Q))); diff --git a/src/serac/numerics/functional/detail/quadrilateral_L2.inl b/src/serac/numerics/functional/detail/quadrilateral_L2.inl index a6c35a942d..30444d0ead 100644 --- a/src/serac/numerics/functional/detail/quadrilateral_L2.inl +++ b/src/serac/numerics/functional/detail/quadrilateral_L2.inl @@ -31,7 +31,7 @@ struct finite_element > { using residual_type = typename std::conditional, tensor >::type; - using dof_type = tensor; + using dof_type = tensor; using dof_type_if = tensor; using value_type = typename std::conditional >::type; @@ -174,11 +174,11 @@ struct finite_element > { double phi_j = B(qx, jx) * B(qy, jy); tensor dphi_j_dxi = {G(qx, jx) * B(qy, jy), B(qx, jx) * G(qy, jy)}; - int Q = qy * q + qx; - const auto & d00 = get<0>(get<0>(input(Q))); - const auto & d01 = get<1>(get<0>(input(Q))); - const auto & d10 = get<0>(get<1>(input(Q))); - const auto & d11 = get<1>(get<1>(input(Q))); + int Q = qy * q + qx; + const auto& d00 = get<0>(get<0>(input(Q))); + const auto& d01 = get<1>(get<0>(input(Q))); + const auto& d10 = get<0>(get<1>(input(Q))); + const auto& d11 = get<1>(get<1>(input(Q))); output[Q] = {d00 * phi_j + dot(d01, dphi_j_dxi), d10 * phi_j + dot(d11, dphi_j_dxi)}; } @@ -188,9 +188,8 @@ struct finite_element > { } template - static auto batch_apply_shape_fn_interior_face(int j, tensor input, const TensorProductQuadratureRule&) + static auto batch_apply_shape_fn_interior_face(int j, tensor input, const TensorProductQuadratureRule&) { - static constexpr bool apply_weights = false; static constexpr auto B = calculate_B(); @@ -198,21 +197,21 @@ struct finite_element > { using source1_t = decltype(get<0>(get<1>(T{})) + get<1>(get<1>(T{}))); int jx = j % n; - int jy = (j % ndof) / n; - int s = j / ndof; + int jy = (j % ndof) / n; + int s = j / ndof; - tensor, q*q> output; + tensor, q * q> output; for (int qy = 0; qy < q; qy++) { for (int qx = 0; qx < q; qx++) { double phi0_j = B(qx, jx) * B(qy, jy) * (s == 0); double phi1_j = B(qx, jx) * B(qy, jy) * (s == 1); - int Q = qy * q + qx; - const auto & d00 = get<0>(get<0>(input(Q))); - const auto & d01 = get<1>(get<0>(input(Q))); - const auto & d10 = get<0>(get<1>(input(Q))); - const auto & d11 = get<1>(get<1>(input(Q))); + int Q = qy * q + qx; + const auto& d00 = get<0>(get<0>(input(Q))); + const auto& d01 = get<1>(get<0>(input(Q))); + const auto& d10 = get<0>(get<1>(input(Q))); + const auto& d11 = get<1>(get<1>(input(Q))); output[Q] = {d00 * phi0_j + d01 * phi1_j, d10 * phi0_j + d11 * phi1_j}; } @@ -221,8 +220,6 @@ struct finite_element > { return output; } - - // we want to compute the following: // // X_q(u, v) := (B(u, i) * B(v, j)) * X_e(i, j) @@ -279,18 +276,19 @@ struct finite_element > { // overload for two-sided interior face kernels template - SERAC_HOST_DEVICE static auto interpolate([[maybe_unused]] const dof_type_if& X, const TensorProductQuadratureRule&) + SERAC_HOST_DEVICE static auto interpolate([[maybe_unused]] const dof_type_if& X, + const TensorProductQuadratureRule&) { static constexpr bool apply_weights = false; static constexpr auto B = calculate_B(); - tensor< tuple< value_type, value_type >, q * q> output; + tensor, q * q> output; tensor value{}; // side 0 for (int i = 0; i < c; i++) { - auto A0 = contract<1, 1>(X(i, 0), B); + auto A0 = contract<1, 1>(X(i, 0), B); value(i) = contract<0, 1>(A0, B); } @@ -308,7 +306,7 @@ struct finite_element > { // side 1 for (int i = 0; i < c; i++) { - auto A0 = contract<1, 1>(X(i, 1), B); + auto A0 = contract<1, 1>(X(i, 1), B); value(i) = contract<0, 1>(A0, B); } @@ -376,13 +374,11 @@ struct finite_element > { } template - SERAC_HOST_DEVICE static void integrate(const tensor, q*q>& qf_output, - const TensorProductQuadratureRule&, - dof_type_if * element_residual, + SERAC_HOST_DEVICE static void integrate(const tensor, q * q>& qf_output, + const TensorProductQuadratureRule&, dof_type_if* element_residual, [[maybe_unused]] int step = 1) { - - constexpr int ntrial = size(T{}) / c; + constexpr int ntrial = size(T{}) / c; static constexpr bool apply_weights = true; static constexpr auto B = calculate_B(); @@ -394,7 +390,7 @@ struct finite_element > { { for (int qy = 0; qy < q; qy++) { for (int qx = 0; qx < q; qx++) { - int Q = qy * q + qx; + int Q = qy * q + qx; source(qy, qx) = reinterpret_cast(&get<0>(qf_output[Q]))[i * ntrial + j]; } } @@ -407,7 +403,7 @@ struct finite_element > { { for (int qy = 0; qy < q; qy++) { for (int qx = 0; qx < q; qx++) { - int Q = qy * q + qx; + int Q = qy * q + qx; source(qy, qx) = reinterpret_cast(&get<1>(qf_output[Q]))[i * ntrial + j]; } } @@ -415,14 +411,10 @@ struct finite_element > { auto A0 = contract<1, 0>(source, B); element_residual[j * step](i, 1) += contract<0, 0>(A0, B); } - } } - } - - #if 0 template diff --git a/src/serac/numerics/functional/detail/segment_H1.inl b/src/serac/numerics/functional/detail/segment_H1.inl index c21d6ad56b..5d6819b696 100644 --- a/src/serac/numerics/functional/detail/segment_H1.inl +++ b/src/serac/numerics/functional/detail/segment_H1.inl @@ -28,7 +28,7 @@ struct finite_element > { static constexpr int VALUE = 0, GRADIENT = 1; static constexpr int SOURCE = 0, FLUX = 1; - using dof_type = tensor; + using dof_type = tensor; using dof_type_if = dof_type; using value_type = typename std::conditional >::type; diff --git a/src/serac/numerics/functional/detail/segment_Hcurl.inl b/src/serac/numerics/functional/detail/segment_Hcurl.inl index 8fab7e2302..0f52571bd2 100644 --- a/src/serac/numerics/functional/detail/segment_Hcurl.inl +++ b/src/serac/numerics/functional/detail/segment_Hcurl.inl @@ -26,7 +26,7 @@ struct finite_element > { static constexpr int dim = 1; static constexpr int ndof = (p + 1); - using dof_type = tensor; + using dof_type = tensor; using dof_type_if = dof_type; SERAC_HOST_DEVICE static constexpr tensor shape_functions(double xi) diff --git a/src/serac/numerics/functional/detail/segment_L2.inl b/src/serac/numerics/functional/detail/segment_L2.inl index 9fd801710c..165b70fe21 100644 --- a/src/serac/numerics/functional/detail/segment_L2.inl +++ b/src/serac/numerics/functional/detail/segment_L2.inl @@ -28,7 +28,7 @@ struct finite_element > { static constexpr int VALUE = 0, GRADIENT = 1; static constexpr int SOURCE = 0, FLUX = 1; - using dof_type = tensor; + using dof_type = tensor; using dof_type_if = tensor; using value_type = typename std::conditional >::type; @@ -158,7 +158,7 @@ struct finite_element > { tensor values{}; - tensor< tuple< value_type, value_type >, q > output{}; + tensor, q> output{}; // apply the shape functions for (int i = 0; i < c; i++) { @@ -256,12 +256,10 @@ struct finite_element > { } template - SERAC_HOST_DEVICE static void integrate(const tensor, q>& qf_output, - const TensorProductQuadratureRule&, - dof_type_if * element_residual, + SERAC_HOST_DEVICE static void integrate(const tensor, q>& qf_output, + const TensorProductQuadratureRule&, dof_type_if* element_residual, [[maybe_unused]] int step = 1) { - constexpr int ntrial = size(T{}) / c; using buffer_type = tensor; @@ -284,6 +282,5 @@ struct finite_element > { } } } - }; /// @endcond diff --git a/src/serac/numerics/functional/detail/triangle_H1.inl b/src/serac/numerics/functional/detail/triangle_H1.inl index 37990ffcfe..0c600bc8d0 100644 --- a/src/serac/numerics/functional/detail/triangle_H1.inl +++ b/src/serac/numerics/functional/detail/triangle_H1.inl @@ -35,7 +35,7 @@ struct finite_element > { using residual_type = typename std::conditional, tensor >::type; - using dof_type = tensor; + using dof_type = tensor; using dof_type_if = dof_type; using value_type = typename std::conditional >::type; diff --git a/src/serac/numerics/functional/detail/triangle_L2.inl b/src/serac/numerics/functional/detail/triangle_L2.inl index bd9f806efb..bdbe604047 100644 --- a/src/serac/numerics/functional/detail/triangle_L2.inl +++ b/src/serac/numerics/functional/detail/triangle_L2.inl @@ -33,7 +33,7 @@ struct finite_element > { using residual_type = typename std::conditional, tensor >::type; - using dof_type = tensor; + using dof_type = tensor; using dof_type_if = tensor; using value_type = typename std::conditional >::type; @@ -250,7 +250,8 @@ struct finite_element > { } template - static auto batch_apply_shape_fn(int j, const tensor & input, const TensorProductQuadratureRule&) + static auto batch_apply_shape_fn(int j, const tensor& input, + const TensorProductQuadratureRule&) { using source_t = decltype(get<0>(get<0>(in_t{})) + dot(get<1>(get<0>(in_t{})), tensor{})); using flux_t = decltype(get<0>(get<1>(in_t{})) + dot(get<1>(get<1>(in_t{})), tensor{})); @@ -276,14 +277,15 @@ struct finite_element > { } template - static auto batch_apply_shape_fn_interior_face(int jx, const tensor & input, const TensorProductQuadratureRule&) + static auto batch_apply_shape_fn_interior_face(int jx, const tensor& input, + const TensorProductQuadratureRule&) { constexpr auto xi = GaussLegendreNodes(); using source0_t = decltype(get<0>(get<0>(T{})) + get<1>(get<0>(T{}))); using source1_t = decltype(get<0>(get<1>(T{})) + get<1>(get<1>(T{}))); - static constexpr int Q = q * (q + 1) / 2; + static constexpr int Q = q * (q + 1) / 2; tensor, Q> output; for (int i = 0; i < Q; i++) { @@ -293,10 +295,10 @@ struct finite_element > { double phi0_j = shape_function(xi[i], j) * (s == 0); double phi1_j = shape_function(xi[i], j) * (s == 1); - const auto & d00 = get<0>(get<0>(input(i))); - const auto & d01 = get<1>(get<0>(input(i))); - const auto & d10 = get<0>(get<1>(input(i))); - const auto & d11 = get<1>(get<1>(input(i))); + const auto& d00 = get<0>(get<0>(input(i))); + const auto& d01 = get<1>(get<0>(input(i))); + const auto& d10 = get<0>(get<1>(input(i))); + const auto& d11 = get<1>(get<1>(input(i))); output[i] = {d00 * phi0_j + d01 * phi1_j, d10 * phi0_j + d11 * phi1_j}; } @@ -335,7 +337,7 @@ struct finite_element > { constexpr auto xi = GaussLegendreNodes(); static constexpr int num_quadrature_points = q * (q + 1) / 2; - tensor< tuple< value_type, value_type >, num_quadrature_points > output{}; + tensor, num_quadrature_points> output{}; // apply the shape functions for (int i = 0; i < c; i++) { @@ -400,16 +402,14 @@ struct finite_element > { } template - SERAC_HOST_DEVICE static void integrate(const tensor, (q*(q + 1))/2 > & qf_output, - const TensorProductQuadratureRule&, - dof_type_if * element_residual, + SERAC_HOST_DEVICE static void integrate(const tensor, (q * (q + 1)) / 2>& qf_output, + const TensorProductQuadratureRule&, dof_type_if* element_residual, [[maybe_unused]] int step = 1) { - - constexpr int ntrial = size(T{}) / c; - constexpr int num_quadrature_points = (q * (q + 1)) / 2; - constexpr auto integration_points = GaussLegendreNodes(); - constexpr auto integration_weights = GaussLegendreWeights(); + constexpr int ntrial = size(T{}) / c; + constexpr int num_quadrature_points = (q * (q + 1)) / 2; + constexpr auto integration_points = GaussLegendreNodes(); + constexpr auto integration_weights = GaussLegendreWeights(); for (int j = 0; j < ntrial; j++) { for (int i = 0; i < c; i++) { @@ -425,10 +425,8 @@ struct finite_element > { element_residual[j * step](i, 1, k) += (source_1 * shape_function(xi, k)) * wt; } } - } } } - }; /// @endcond diff --git a/src/serac/numerics/functional/domain.cpp b/src/serac/numerics/functional/domain.cpp index 46d697e5da..dd8c1beace 100644 --- a/src/serac/numerics/functional/domain.cpp +++ b/src/serac/numerics/functional/domain.cpp @@ -78,15 +78,9 @@ static Domain domain_of_vertices(const mesh_t& mesh, std::function func) -{ - return domain_of_vertices(mesh, func); -} +Domain Domain::ofVertices(const mesh_t& mesh, std::function func) { return domain_of_vertices(mesh, func); } -Domain Domain::ofVertices(const mesh_t& mesh, std::function func) -{ - return domain_of_vertices(mesh, func); -} +Domain Domain::ofVertices(const mesh_t& mesh, std::function func) { return domain_of_vertices(mesh, func); } /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// @@ -147,8 +141,7 @@ Domain Domain::ofEdges(const mesh_t& mesh, std::function) /////////////////////////////////////////////////////////////////////////////////////// template -static Domain domain_of_faces(const mesh_t& mesh, - std::function>, int)> predicate) +static Domain domain_of_faces(const mesh_t& mesh, std::function>, int)> predicate) { assert(mesh.SpaceDimension() == d); @@ -230,8 +223,7 @@ Domain Domain::ofFaces(const mesh_t& mesh, std::function, /////////////////////////////////////////////////////////////////////////////////////// template -static Domain domain_of_elems(const mesh_t& mesh, - std::function>, int)> predicate) +static Domain domain_of_elems(const mesh_t& mesh, std::function>, int)> predicate) { assert(mesh.SpaceDimension() == d); @@ -311,7 +303,7 @@ Domain Domain::ofElements(const mesh_t& mesh, std::function -static Domain domain_of_boundary_elems(const mesh_t& mesh, +static Domain domain_of_boundary_elems(const mesh_t& mesh, std::function>, int)> predicate) { assert(mesh.SpaceDimension() == d); @@ -387,7 +379,7 @@ Domain Domain::ofBoundaryElements(const mesh_t& mesh, std::function(mesh, func); } -mfem::Array Domain::dof_list(const serac::fes_t * fes) const +mfem::Array Domain::dof_list(const serac::fes_t* fes) const { std::set dof_ids; mfem::Array elem_dofs; @@ -455,19 +447,15 @@ mfem::Array Domain::dof_list(const serac::fes_t * fes) const return uniq_dof_ids; } -void Domain::insert_restriction(const serac::fes_t * fes, FunctionSpace space) { - +void Domain::insert_restriction(const serac::fes_t* fes, FunctionSpace space) +{ // if we don't already have a BlockElementRestriction for this FunctionSpace, make one if (restriction_operators.count(space) == 0) { restriction_operators[space] = BlockElementRestriction(fes, *this); } - } -const BlockElementRestriction & Domain::get_restriction(FunctionSpace space) { - return restriction_operators.at(space); -}; - +const BlockElementRestriction& Domain::get_restriction(FunctionSpace space) { return restriction_operators.at(space); }; /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// @@ -551,8 +539,8 @@ Domain EntireBoundary(const mesh_t& mesh) } /// @brief constructs a domain from all the interior face elements in a mesh -Domain InteriorFaces(const mesh_t & mesh) { - +Domain InteriorFaces(const mesh_t& mesh) +{ Domain output{mesh, mesh.SpaceDimension() - 1, Domain::Type::InteriorFaces}; int edge_id = 0; @@ -560,7 +548,6 @@ Domain InteriorFaces(const mesh_t & mesh) { int quad_id = 0; for (int f = 0; f < mesh.GetNumFaces(); f++) { - // discard faces with the wrong type if (!mesh.GetFaceInformation(f).IsInterior()) continue; @@ -586,7 +573,6 @@ Domain InteriorFaces(const mesh_t & mesh) { } return output; - } /////////////////////////////////////////////////////////////////////////////////////// @@ -596,11 +582,17 @@ Domain InteriorFaces(const mesh_t & mesh) { /// @cond using int2 = std::tuple; -enum SET_OPERATION { UNION, INTERSECTION, DIFFERENCE }; +enum SET_OPERATION +{ + UNION, + INTERSECTION, + DIFFERENCE +}; /// @endcond /// @brief combine a pair of arrays of ints into a single array of `int2`, see also: unzip() -void zip(std::vector< int2 > & ab, const std::vector& a, const std::vector& b) { +void zip(std::vector& ab, const std::vector& a, const std::vector& b) +{ ab.resize(a.size()); for (uint32_t i = 0; i < a.size(); i++) { ab[i] = {a[i], b[i]}; @@ -608,18 +600,19 @@ void zip(std::vector< int2 > & ab, const std::vector& a, const std::vector< } /// @brief split an array of `int2` into a pair of arrays of ints, see also: zip() -void unzip(const std::vector< int2 > & ab, std::vector& a, std::vector& b) { +void unzip(const std::vector& ab, std::vector& a, std::vector& b) +{ a.resize(ab.size()); b.resize(ab.size()); for (uint32_t i = 0; i < ab.size(); i++) { auto ab_i = ab[i]; - a[i] = std::get<0>(ab_i); - b[i] = std::get<1>(ab_i); + a[i] = std::get<0>(ab_i); + b[i] = std::get<1>(ab_i); } } /// @brief return a std::vector that is the result of applying (a op b) -template < typename T > +template std::vector set_operation(SET_OPERATION op, const std::vector& a, const std::vector& b) { using c_iter = typename std::vector::const_iterator; @@ -627,14 +620,14 @@ std::vector set_operation(SET_OPERATION op, const std::vector& a, const st using set_op = std::function; set_op combine; - if (op == SET_OPERATION::UNION) { - combine = std::set_union; + if (op == SET_OPERATION::UNION) { + combine = std::set_union; } - if (op == SET_OPERATION::INTERSECTION) { - combine = std::set_intersection; + if (op == SET_OPERATION::INTERSECTION) { + combine = std::set_intersection; } - if (op == SET_OPERATION::DIFFERENCE) { - combine = std::set_difference; + if (op == SET_OPERATION::DIFFERENCE) { + combine = std::set_difference; } std::vector combined; @@ -668,7 +661,7 @@ Domain set_operation(SET_OPERATION op, const Domain& a, const Domain& b) zip(b_zipped_ids, b.tri_ids_, b.mfem_tri_ids_); std::vector combined_zipped_ids = set_operation(op, a_zipped_ids, b_zipped_ids); unzip(combined_zipped_ids, combined.tri_ids_, combined.mfem_tri_ids_); - + zip(a_zipped_ids, a.quad_ids_, a.mfem_quad_ids_); zip(b_zipped_ids, b.quad_ids_, b.mfem_quad_ids_); combined_zipped_ids = set_operation(op, a_zipped_ids, b_zipped_ids); @@ -681,7 +674,7 @@ Domain set_operation(SET_OPERATION op, const Domain& a, const Domain& b) zip(b_zipped_ids, b.tet_ids_, b.mfem_tet_ids_); std::vector combined_zipped_ids = set_operation(op, a_zipped_ids, b_zipped_ids); unzip(combined_zipped_ids, combined.tet_ids_, combined.mfem_tet_ids_); - + zip(a_zipped_ids, a.hex_ids_, a.mfem_hex_ids_); zip(b_zipped_ids, b.hex_ids_, b.mfem_hex_ids_); combined_zipped_ids = set_operation(op, a_zipped_ids, b_zipped_ids); diff --git a/src/serac/numerics/functional/domain.hpp b/src/serac/numerics/functional/domain.hpp index 129601117e..2f25ff13c2 100644 --- a/src/serac/numerics/functional/domain.hpp +++ b/src/serac/numerics/functional/domain.hpp @@ -25,7 +25,6 @@ struct BlockElementRestriction; * This region can be an entire mesh or some subset of its elements */ struct Domain { - /// @brief enum describing what kind of elements are included in a Domain enum Type { @@ -66,7 +65,7 @@ struct Domain { std::vector mfem_hex_ids_; /// @endcond - std::map< FunctionSpace, BlockElementRestriction > restriction_operators; + std::map restriction_operators; /** * @brief empty Domain constructor, with connectivity info to be populated later @@ -156,28 +155,37 @@ struct Domain { } /** - * @brief returns how many elements of any type belong to this domain + * @brief returns how many elements of any type belong to this domain */ - int total_elements() const { - return int(vertex_ids_.size() + edge_ids_.size() + tri_ids_.size() + quad_ids_.size() + tet_ids_.size() + hex_ids_.size()); + int total_elements() const + { + return int(vertex_ids_.size() + edge_ids_.size() + tri_ids_.size() + quad_ids_.size() + tet_ids_.size() + + hex_ids_.size()); } /** - * @brief returns an array of the prefix sum of element counts belonging to this domain. + * @brief returns an array of the prefix sum of element counts belonging to this domain. * Primarily intended to be used in mfem::BlockVector::Update(double * data, mfem::Array bOffsets); */ - mfem::Array bOffsets() const { + mfem::Array bOffsets() const + { mfem::Array offsets(mfem::Geometry::NUM_GEOMETRIES + 1); - int total = 0; - offsets[mfem::Geometry::POINT] = total; total += vertex_ids_.size(); - offsets[mfem::Geometry::SEGMENT] = total; total += edge_ids_.size(); - offsets[mfem::Geometry::TRIANGLE] = total; total += tri_ids_.size(); - offsets[mfem::Geometry::SQUARE] = total; total += quad_ids_.size(); - offsets[mfem::Geometry::TETRAHEDRON] = total; total += tet_ids_.size(); - offsets[mfem::Geometry::CUBE] = total; total += hex_ids_.size(); - offsets[mfem::Geometry::PRISM] = total; - offsets[mfem::Geometry::PYRAMID] = total; + int total = 0; + offsets[mfem::Geometry::POINT] = total; + total += vertex_ids_.size(); + offsets[mfem::Geometry::SEGMENT] = total; + total += edge_ids_.size(); + offsets[mfem::Geometry::TRIANGLE] = total; + total += tri_ids_.size(); + offsets[mfem::Geometry::SQUARE] = total; + total += quad_ids_.size(); + offsets[mfem::Geometry::TETRAHEDRON] = total; + total += tet_ids_.size(); + offsets[mfem::Geometry::CUBE] = total; + total += hex_ids_.size(); + offsets[mfem::Geometry::PRISM] = total; + offsets[mfem::Geometry::PYRAMID] = total; offsets[mfem::Geometry::NUM_GEOMETRIES] = total; return offsets; @@ -187,11 +195,10 @@ struct Domain { mfem::Array dof_list(const fes_t* fes) const; /// @brief TODO - void insert_restriction(const fes_t * fes, FunctionSpace space); + void insert_restriction(const fes_t* fes, FunctionSpace space); /// @brief TODO - const BlockElementRestriction & get_restriction(FunctionSpace space); - + const BlockElementRestriction& get_restriction(FunctionSpace space); }; /// @brief constructs a domain from all the elements in a mesh diff --git a/src/serac/numerics/functional/domain_integral_kernels.hpp b/src/serac/numerics/functional/domain_integral_kernels.hpp index acc250ac8b..74a749c1d2 100644 --- a/src/serac/numerics/functional/domain_integral_kernels.hpp +++ b/src/serac/numerics/functional/domain_integral_kernels.hpp @@ -156,8 +156,8 @@ void evaluation_kernel_impl(trial_element_tuple trial_elements, test_element, do const std::vector& inputs, double* outputs, const double* positions, const double* jacobians, lambda_type qf, [[maybe_unused]] axom::ArrayView qf_state, - [[maybe_unused]] derivative_type* qf_derivatives, - uint32_t num_elements, bool update_state, camp::int_seq) + [[maybe_unused]] derivative_type* qf_derivatives, uint32_t num_elements, bool update_state, + camp::int_seq) { // mfem provides this information as opaque arrays of doubles, // so we reinterpret the pointer with @@ -275,8 +275,8 @@ SERAC_HOST_DEVICE auto batch_apply_chain_rule(derivative_type* qf_derivatives, c */ template -void action_of_gradient_kernel(const double* dU, double* dR, derivatives_type* qf_derivatives, std::size_t num_elements) { - +void action_of_gradient_kernel(const double* dU, double* dR, derivatives_type* qf_derivatives, std::size_t num_elements) +{ using test_element = finite_element; using trial_element = finite_element; @@ -324,7 +324,8 @@ void action_of_gradient_kernel(const double* dU, double* dR, derivatives_type* q * @param[in] num_elements The number of elements in the mesh */ template -void element_gradient_kernel(ExecArrayView dK, derivatives_type* qf_derivatives, std::size_t num_elements) +void element_gradient_kernel(ExecArrayView dK, derivatives_type* qf_derivatives, + std::size_t num_elements) { // quantities of interest have no flux term, so we pad the derivative // tuple with a "zero" type in the second position to treat it like the standard case diff --git a/src/serac/numerics/functional/element_restriction.cpp b/src/serac/numerics/functional/element_restriction.cpp index 0cf517bfe2..fca8af0351 100644 --- a/src/serac/numerics/functional/element_restriction.cpp +++ b/src/serac/numerics/functional/element_restriction.cpp @@ -216,8 +216,7 @@ std::vector > geom_local_face_dofs(int p) return output; } -axom::Array GetElementRestriction(const serac::fes_t* fes, - mfem::Geometry::Type geom) +axom::Array GetElementRestriction(const serac::fes_t* fes, mfem::Geometry::Type geom) { std::vector elem_dofs{}; mfem::Mesh* mesh = fes->GetMesh(); @@ -279,9 +278,8 @@ axom::Array GetElementRestriction(const serac:: } } -axom::Array GetElementDofs(const serac::fes_t* fes, - mfem::Geometry::Type geom, - const std::vector< int > & mfem_elem_ids) +axom::Array GetElementDofs(const serac::fes_t* fes, mfem::Geometry::Type geom, + const std::vector& mfem_elem_ids) { std::vector elem_dofs{}; @@ -294,7 +292,6 @@ axom::Array GetElementDofs(const serac::fes_t* uint64_t n = 0; for (auto elem : mfem_elem_ids) { - // discard elements with the wrong geometry if (mesh->GetElementGeometry(elem) != geom) { SLIC_ERROR("encountered incorrect element geometry type"); @@ -347,8 +344,8 @@ axom::Array GetElementDofs(const serac::fes_t* } } -axom::Array GetFaceDofs(const serac::fes_t* fes, - mfem::Geometry::Type face_geom, FaceType type) +axom::Array GetFaceDofs(const serac::fes_t* fes, mfem::Geometry::Type face_geom, + FaceType type) { std::vector face_dofs; mfem::Mesh* mesh = fes->GetMesh(); @@ -462,9 +459,8 @@ axom::Array GetFaceDofs(const serac::fes_t* fes } } -axom::Array GetFaceDofs(const serac::fes_t* fes, - mfem::Geometry::Type face_geom, - const std::vector & mfem_face_ids) +axom::Array GetFaceDofs(const serac::fes_t* fes, mfem::Geometry::Type face_geom, + const std::vector& mfem_face_ids) { std::vector face_dofs; mfem::Mesh* mesh = fes->GetMesh(); @@ -476,25 +472,23 @@ axom::Array GetFaceDofs(const serac::fes_t* fes std::vector > local_face_dofs = geom_local_face_dofs(p); std::vector > lex_perm = lexicographic_permutations(p); - //int components_per_node = fes->GetVDim(); - //bool by_vdim = fes->GetOrdering() == mfem::Ordering::byVDIM; - //fes->ExchangeFaceNbrData(); - //int LSize = fes->GetProlongationMatrix()->Height(); + // int components_per_node = fes->GetVDim(); + // bool by_vdim = fes->GetOrdering() == mfem::Ordering::byVDIM; + // fes->ExchangeFaceNbrData(); + // int LSize = fes->GetProlongationMatrix()->Height(); uint64_t n = 0; for (int f : mfem_face_ids) { - mfem::Mesh::FaceInformation info = mesh->GetFaceInformation(f); - if (mesh->GetFaceGeometry(f) != face_geom) { + if (mesh->GetFaceGeometry(f) != face_geom) { SLIC_ERROR("encountered incorrect face geometry type"); } // mfem doesn't provide this connectivity info for DG spaces directly (?), // so we have to get at it indirectly in several steps: if (isDG(*fes)) { - // 1. find the element(s) that this face belongs to mfem::Array elem_ids; face_to_elem->GetRow(f, elem_ids); @@ -589,7 +583,6 @@ axom::Array GetFaceDofs(const serac::fes_t* fes mpi::out << std::endl; } #endif - } // H1 and Hcurl spaces are more straight-forward, since @@ -631,15 +624,16 @@ axom::Array GetFaceDofs(const serac::fes_t* fes namespace serac { -ElementRestriction::ElementRestriction(const fes_t* fes, mfem::Geometry::Type elem_geom, const std::vector< int > & elem_ids) { - +ElementRestriction::ElementRestriction(const fes_t* fes, mfem::Geometry::Type elem_geom, + const std::vector& elem_ids) +{ int sdim = fes->GetMesh()->Dimension(); int gdim = dimension_of(elem_geom); if (gdim == sdim) { dof_info = GetElementDofs(fes, elem_geom, elem_ids); } - if (gdim+1 == sdim) { + if (gdim + 1 == sdim) { dof_info = GetFaceDofs(fes, elem_geom, elem_ids); } @@ -653,7 +647,6 @@ ElementRestriction::ElementRestriction(const fes_t* fes, mfem::Geometry::Type el num_elements = uint64_t(dof_info.shape()[0]); nodes_per_elem = uint64_t(dof_info.shape()[1]); esize = num_elements * nodes_per_elem * components; - } uint64_t ElementRestriction::ESize() const { return esize; } @@ -707,8 +700,8 @@ void ElementRestriction::ScatterAdd(const mfem::Vector& E_vector, mfem::Vector& //////////////////////////////////////////////////////////////////////// /// create a BlockElementRestriction for the elements in a given domain -BlockElementRestriction::BlockElementRestriction(const fes_t* fes, const Domain & domain) { - +BlockElementRestriction::BlockElementRestriction(const fes_t* fes, const Domain& domain) +{ // TODO: changing the mfem_XXX_ids arrays to mfem_ids[XXX] would simplify this if (domain.mfem_edge_ids_.size() > 0) { restrictions[mfem::Geometry::SEGMENT] = ElementRestriction(fes, mfem::Geometry::SEGMENT, domain.mfem_edge_ids_); @@ -723,18 +716,19 @@ BlockElementRestriction::BlockElementRestriction(const fes_t* fes, const Domain } if (domain.mfem_tet_ids_.size() > 0) { - restrictions[mfem::Geometry::TETRAHEDRON] = ElementRestriction(fes, mfem::Geometry::TETRAHEDRON, domain.mfem_tet_ids_); + restrictions[mfem::Geometry::TETRAHEDRON] = + ElementRestriction(fes, mfem::Geometry::TETRAHEDRON, domain.mfem_tet_ids_); } if (domain.mfem_hex_ids_.size() > 0) { restrictions[mfem::Geometry::CUBE] = ElementRestriction(fes, mfem::Geometry::CUBE, domain.mfem_hex_ids_); } - } -uint64_t BlockElementRestriction::ESize() const { +uint64_t BlockElementRestriction::ESize() const +{ uint64_t total = 0; - for (auto & [geom, restriction] : restrictions) { + for (auto& [geom, restriction] : restrictions) { total += restriction.ESize(); } return total; @@ -760,14 +754,14 @@ mfem::Array BlockElementRestriction::bOffsets() const void BlockElementRestriction::Gather(const mfem::Vector& L_vector, mfem::BlockVector& E_block_vector) const { - for (auto & [geom, restriction] : restrictions) { + for (auto& [geom, restriction] : restrictions) { restriction.Gather(L_vector, E_block_vector.GetBlock(geom)); } } void BlockElementRestriction::ScatterAdd(const mfem::BlockVector& E_block_vector, mfem::Vector& L_vector) const { - for (auto & [geom, restriction] : restrictions) { + for (auto& [geom, restriction] : restrictions) { restriction.ScatterAdd(E_block_vector.GetBlock(geom), L_vector); } } diff --git a/src/serac/numerics/functional/element_restriction.hpp b/src/serac/numerics/functional/element_restriction.hpp index 821571fd7a..c77ced11c5 100644 --- a/src/serac/numerics/functional/element_restriction.hpp +++ b/src/serac/numerics/functional/element_restriction.hpp @@ -153,7 +153,7 @@ struct ElementRestriction { ElementRestriction() {} /// ctor from a list of elements (e.g. from a serac::Domain) - ElementRestriction(const fes_t* fes, mfem::Geometry::Type elem_geom, const std::vector & domain_elements); + ElementRestriction(const fes_t* fes, mfem::Geometry::Type elem_geom, const std::vector& domain_elements); /// the size of the "E-vector" associated with this restriction operator uint64_t ESize() const; @@ -196,7 +196,7 @@ struct ElementRestriction { /// the number of nodes in each element uint64_t nodes_per_elem; - /// an array mapping from domain element ids [0, num_elements) to + /// an array mapping from domain element ids [0, num_elements) to std::vector element_ids; /// a 2D array (num_elements-by-nodes_per_elem) holding the dof info extracted from the finite element space @@ -216,7 +216,7 @@ struct BlockElementRestriction { BlockElementRestriction() {} /// create a BlockElementRestriction for the elements in a given domain - BlockElementRestriction(const fes_t* fes, const Domain & domain); + BlockElementRestriction(const fes_t* fes, const Domain& domain); /// the size of the "E-vector" associated with this restriction operator uint64_t ESize() const; @@ -254,7 +254,9 @@ axom::Array GetElementDofs(const serac::fes_t* * @param geom the kind of element geometry * @param type whether the face is of interior or boundary type */ -axom::Array GetFaceDofs(const serac::fes_t* fes, mfem::Geometry::Type face_geom, FaceType type); +axom::Array GetFaceDofs(const serac::fes_t* fes, mfem::Geometry::Type face_geom, + FaceType type); /// @overload -axom::Array GetFaceDofs(const serac::fes_t* fes, mfem::Geometry::Type face_geom, const std::vector & mfem_face_ids); +axom::Array GetFaceDofs(const serac::fes_t* fes, mfem::Geometry::Type face_geom, + const std::vector& mfem_face_ids); diff --git a/src/serac/numerics/functional/finite_element.hpp b/src/serac/numerics/functional/finite_element.hpp index 3d4c6f4e27..dcbab1f8e0 100644 --- a/src/serac/numerics/functional/finite_element.hpp +++ b/src/serac/numerics/functional/finite_element.hpp @@ -232,30 +232,25 @@ struct QOI { static constexpr Family family = Family::QOI; ///< the family of the basis functions }; - /** * @brief a small POD class for tracking function space metadata */ struct FunctionSpace { - Family family; ///< either H1, Hcurl, L2 - int order; ///< polynomial order - int components; ///< how many values are stored at each node + Family family; ///< either H1, Hcurl, L2 + int order; ///< polynomial order + int components; ///< how many values are stored at each node /** * @brief return the data contained in this struct as a tuple * @note the main point of this conversion is to take advantage of std::tuple's automatic * lexicographic-ordering comparison operators */ - std::tuple as_tuple() const { - return std::tuple(int(family), order, components); - } + std::tuple as_tuple() const { return std::tuple(int(family), order, components); } /** * @brief defines an ordering over FunctionSpaces, to enable use in containers like std::map */ - bool operator<(FunctionSpace other) const { - return this->as_tuple() < other.as_tuple(); - } + bool operator<(FunctionSpace other) const { return this->as_tuple() < other.as_tuple(); } }; /** diff --git a/src/serac/numerics/functional/functional.hpp b/src/serac/numerics/functional/functional.hpp index d430a246c3..768dfcfa75 100644 --- a/src/serac/numerics/functional/functional.hpp +++ b/src/serac/numerics/functional/functional.hpp @@ -101,11 +101,14 @@ inline void check_for_unsupported_elements(const mfem::Mesh& mesh) } } -inline void check_interior_face_compatibility(const mfem::Mesh & mesh, const FunctionSpace space) { +inline void check_interior_face_compatibility(const mfem::Mesh& mesh, const FunctionSpace space) +{ if (space.family == Family::L2) { - const mfem::ParMesh * pmesh = dynamic_cast< const mfem::ParMesh * >(&mesh); + const mfem::ParMesh* pmesh = dynamic_cast(&mesh); if (pmesh) { - SLIC_ERROR_IF(pmesh->GetNSharedFaces() > 0, "interior face integrals involving DG function spaces don't currently support meshes with shared faces"); + SLIC_ERROR_IF( + pmesh->GetNSharedFaces() > 0, + "interior face integrals involving DG function spaces don't currently support meshes with shared faces"); } } } @@ -244,16 +247,16 @@ class Functional { test_function_space_ = {test::family, test::order, test::components}; - std::array trial_families = {trials::family ...}; - std::array trial_orders = {trials::order ...}; - std::array trial_components = {trials::components ...}; + std::array trial_families = {trials::family...}; + std::array trial_orders = {trials::order...}; + std::array trial_components = {trials::components...}; for (uint32_t i = 0; i < num_trial_spaces; i++) { trial_function_spaces_[i] = {trial_families[i], trial_orders[i], trial_components[i]}; } - //for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements, Domain::Type::InteriorFaces}) { - // output_E_[type].Update(G_test_[type].bOffsets(), mem_type); - //} + // for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements, Domain::Type::InteriorFaces}) { + // output_E_[type].Update(G_test_[type].bOffsets(), mem_type); + // } P_test_ = test_space_->GetProlongationMatrix(); @@ -267,7 +270,6 @@ class Functional { for (uint32_t i = 0; i < num_trial_spaces; i++) { grad_.emplace_back(*this, i); } - } /** @@ -292,7 +294,7 @@ class Functional { check_for_unsupported_elements(domain.mesh_); check_for_missing_nodal_gridfunc(domain.mesh_); - std::vector< uint32_t > arg_vec = {args ...}; + std::vector arg_vec = {args...}; for (uint32_t i : arg_vec) { domain.insert_restriction(trial_space_[i], trial_function_spaces_[i]); } @@ -322,7 +324,7 @@ class Functional { check_for_missing_nodal_gridfunc(domain.mesh_); - std::vector< uint32_t > arg_vec = {args ...}; + std::vector arg_vec = {args...}; for (uint32_t i : arg_vec) { domain.insert_restriction(trial_space_[i], trial_function_spaces_[i]); } @@ -340,7 +342,7 @@ class Functional { { check_for_missing_nodal_gridfunc(domain.mesh_); - std::vector< uint32_t > arg_vec = {args ...}; + std::vector arg_vec = {args...}; for (uint32_t i : arg_vec) { domain.insert_restriction(trial_space_[i], trial_function_spaces_[i]); check_interior_face_compatibility(domain.mesh_, trial_function_spaces_[i]); @@ -410,16 +412,15 @@ class Functional { output_L_ = 0.0; for (auto& integral : integrals_) { - if (integral.DependsOn(which)) { - Domain & dom = integral.domain_; + Domain& dom = integral.domain_; - const serac::BlockElementRestriction & G_trial = dom.get_restriction(trial_function_spaces_[which]); + const serac::BlockElementRestriction& G_trial = dom.get_restriction(trial_function_spaces_[which]); input_E_buffer_[which].SetSize(int(G_trial.ESize())); input_E_[which].Update(input_E_buffer_[which], G_trial.bOffsets()); G_trial.Gather(input_L_[which], input_E_[which]); - const serac::BlockElementRestriction & G_test = dom.get_restriction(test_function_space_); + const serac::BlockElementRestriction& G_test = dom.get_restriction(test_function_space_); output_E_buffer_.SetSize(int(G_test.ESize())); output_E_.Update(output_E_buffer_, G_test.bOffsets()); @@ -428,12 +429,10 @@ class Functional { // scatter-add to compute residuals on the local processor G_test.ScatterAdd(output_E_, output_L_); } - } // scatter-add to compute global residuals P_test_->MultTranspose(output_L_, output_T); - } /** @@ -455,7 +454,7 @@ class Functional { // get the values for each local processor for (uint32_t i = 0; i < num_trial_spaces; i++) { - #if 0 +#if 0 if (trial_function_spaces_[i].family == Family::L2) { // make a PGF on the fly @@ -482,21 +481,21 @@ class Functional { P_trial_[i]->Mult(*input_T[i], input_L_[i]); } - #else +#else P_trial_[i]->Mult(*input_T[i], input_L_[i]); - #endif +#endif } output_L_ = 0.0; for (auto& integral : integrals_) { - Domain & dom = integral.domain_; + Domain& dom = integral.domain_; - const serac::BlockElementRestriction & G_test = dom.get_restriction(test_function_space_); + const serac::BlockElementRestriction& G_test = dom.get_restriction(test_function_space_); mpi::out << "G_test.ESize() " << G_test.ESize() << std::endl; for (auto i : integral.active_trial_spaces_) { - const serac::BlockElementRestriction & G_trial = dom.get_restriction(trial_function_spaces_[i]); + const serac::BlockElementRestriction& G_trial = dom.get_restriction(trial_function_spaces_[i]); input_E_buffer_[i].SetSize(int(G_trial.ESize())); mpi::out << "G_trial.ESize() " << G_trial.ESize() << std::endl; input_E_[i].Update(input_E_buffer_[i], G_trial.bOffsets()); @@ -603,15 +602,15 @@ class Functional { } #if 1 - void initialize_sparsity_pattern() { + void initialize_sparsity_pattern() + { + using row_col = std::tuple; - using row_col = std::tuple; - - std::set< row_col > nonzero_entries; + std::set nonzero_entries; for (auto& integral : form_.integrals_) { if (integral.DependsOn(which_argument)) { - Domain & dom = integral.domain_; + Domain& dom = integral.domain_; const auto& G_test = dom.get_restriction(form_.test_function_space_); const auto& G_trial = dom.get_restriction(form_.trial_function_spaces_[which_argument]); for (const auto& [geom, test_restriction] : G_test.restrictions) { @@ -623,7 +622,6 @@ class Functional { auto num_elements = static_cast(test_restriction.num_elements); for (uint32_t e = 0; e < num_elements; e++) { - for (uint32_t i = 0; i < test_restriction.nodes_per_elem; i++) { auto test_dof = test_restriction.dof_info(e, i); for (uint32_t j = 0; j < test_restriction.components; j++) { @@ -634,7 +632,8 @@ class Functional { for (uint32_t i = 0; i < trial_restriction.nodes_per_elem; i++) { auto trial_dof = trial_restriction.dof_info(e, i); for (uint32_t j = 0; j < trial_restriction.components; j++) { - trial_vdofs[i * trial_restriction.components + j] = int(trial_restriction.GetVDof(trial_dof, j).index()); + trial_vdofs[i * trial_restriction.components + j] = + int(trial_restriction.GetVDof(trial_dof, j).index()); } } @@ -648,91 +647,86 @@ class Functional { } } - uint64_t nnz = nonzero_entries.size(); - int nrows = form_.output_L_.Size(); + uint64_t nnz = nonzero_entries.size(); + int nrows = form_.output_L_.Size(); row_ptr.resize(uint32_t(nrows + 1)); col_ind.resize(nnz); - int nz = 0; + int nz = 0; int last_row = -1; for (auto [row, col] : nonzero_entries) { col_ind[uint32_t(nz)] = col; - for (int i = last_row+1; i <= row; i++) { row_ptr[uint32_t(i)] = nz; } + for (int i = last_row + 1; i <= row; i++) { + row_ptr[uint32_t(i)] = nz; + } last_row = row; nz++; } - for (int i = last_row+1; i <= nrows; i++) { row_ptr[uint32_t(i)] = nz; } + for (int i = last_row + 1; i <= nrows; i++) { + row_ptr[uint32_t(i)] = nz; + } }; - uint64_t max_buffer_size() { + uint64_t max_buffer_size() + { uint64_t max_entries = 0; - for (auto & integral : form_.integrals_) { + for (auto& integral : form_.integrals_) { if (integral.DependsOn(which_argument)) { - Domain & dom = integral.domain_; + Domain& dom = integral.domain_; const auto& G_test = dom.get_restriction(form_.test_function_space_); const auto& G_trial = dom.get_restriction(form_.trial_function_spaces_[which_argument]); for (const auto& [geom, test_restriction] : G_test.restrictions) { - const auto& trial_restriction = G_trial.restrictions.at(geom); - uint64_t nrows_per_element = test_restriction.nodes_per_elem * test_restriction.components; - uint64_t ncols_per_element = trial_restriction.nodes_per_elem * trial_restriction.components; - uint64_t entries_per_element = nrows_per_element * ncols_per_element; - uint64_t entries_needed = test_restriction.num_elements * entries_per_element; - max_entries = std::max(entries_needed, max_entries); + const auto& trial_restriction = G_trial.restrictions.at(geom); + uint64_t nrows_per_element = test_restriction.nodes_per_elem * test_restriction.components; + uint64_t ncols_per_element = trial_restriction.nodes_per_elem * trial_restriction.components; + uint64_t entries_per_element = nrows_per_element * ncols_per_element; + uint64_t entries_needed = test_restriction.num_elements * entries_per_element; + max_entries = std::max(entries_needed, max_entries); } } } return max_entries; } - std::unique_ptr assemble() { - + std::unique_ptr assemble() + { if (row_ptr.empty()) { initialize_sparsity_pattern(); } - // since we own the storage for row_ptr, col_ind, values, + // since we own the storage for row_ptr, col_ind, values, // we ask mfem to not deallocate those pointers in the SparseMatrix dtor constexpr bool sparse_matrix_frees_graph_ptrs = false; constexpr bool sparse_matrix_frees_values_ptr = false; - constexpr bool col_ind_is_sorted = true; + constexpr bool col_ind_is_sorted = true; // note: we make a copy of col_ind since mfem::HypreParMatrix // changes it in the constructor std::vector col_ind_copy = col_ind; - int nnz = row_ptr.back(); + int nnz = row_ptr.back(); std::vector values(uint32_t(nnz), 0.0); - auto A_local = mfem::SparseMatrix( - row_ptr.data(), - col_ind_copy.data(), - values.data(), - form_.output_L_.Size(), - form_.input_L_[which_argument].Size(), - sparse_matrix_frees_graph_ptrs, - sparse_matrix_frees_values_ptr, - col_ind_is_sorted - ); + auto A_local = mfem::SparseMatrix(row_ptr.data(), col_ind_copy.data(), values.data(), form_.output_L_.Size(), + form_.input_L_[which_argument].Size(), sparse_matrix_frees_graph_ptrs, + sparse_matrix_frees_values_ptr, col_ind_is_sorted); std::vector K_elem_buffer(max_buffer_size()); - for (auto & integral : form_.integrals_) { - + for (auto& integral : form_.integrals_) { // if this integral's derivative isn't identically zero if (integral.functional_to_integral_index_.count(which_argument) > 0) { - Domain & dom = integral.domain_; + Domain& dom = integral.domain_; - uint32_t id = integral.functional_to_integral_index_.at(which_argument); + uint32_t id = integral.functional_to_integral_index_.at(which_argument); const auto& G_test = dom.get_restriction(form_.test_function_space_); const auto& G_trial = dom.get_restriction(form_.trial_function_spaces_[which_argument]); for (const auto& [geom, calculate_element_matrices_func] : integral.element_gradient_[id]) { - - const auto& test_restriction = G_test.restrictions.at(geom); + const auto& test_restriction = G_test.restrictions.at(geom); const auto& trial_restriction = G_trial.restrictions.at(geom); // prepare a buffer to hold the element matrices - CPUArrayView K_e(K_elem_buffer.data(), - test_restriction.num_elements, + CPUArrayView K_e(K_elem_buffer.data(), test_restriction.num_elements, trial_restriction.nodes_per_elem * trial_restriction.components, test_restriction.nodes_per_elem * test_restriction.components); detail::zero_out(K_e); @@ -740,7 +734,7 @@ class Functional { // perform the actual calculations calculate_element_matrices_func(K_e); - const std::vector & element_ids = integral.domain_.get(geom); + const std::vector& element_ids = integral.domain_.get(geom); uint32_t rows_per_elem = uint32_t(test_restriction.nodes_per_elem * test_restriction.components); uint32_t cols_per_elem = uint32_t(trial_restriction.nodes_per_elem * trial_restriction.components); @@ -761,15 +755,15 @@ class Functional { } } } - } } } auto* R = form_.test_space_->Dof_TrueDof_Matrix(); - auto* A_hypre = new mfem::HypreParMatrix(test_space_->GetComm(), test_space_->GlobalVSize(), trial_space_->GlobalVSize(), - test_space_->GetDofOffsets(), trial_space_->GetDofOffsets(), &A_local); + auto* A_hypre = + new mfem::HypreParMatrix(test_space_->GetComm(), test_space_->GlobalVSize(), trial_space_->GlobalVSize(), + test_space_->GetDofOffsets(), trial_space_->GetDofOffsets(), &A_local); auto* P = trial_space_->Dof_TrueDof_Matrix(); @@ -803,8 +797,8 @@ class Functional { std::map> element_gradients[Domain::num_types]; for (auto& integral : form_.integrals_) { - auto& K_elem = element_gradients[integral.domain_.type_]; - auto& test_restrictions = form_.G_test_[integral.domain_.type_].restrictions; + auto& K_elem = element_gradients[integral.domain_.type_]; + auto& test_restrictions = form_.G_test_[integral.domain_.type_].restrictions; auto& trial_restrictions = form_.G_trial_[integral.domain_.type_][which_argument].restrictions; if (K_elem.empty()) { @@ -823,8 +817,8 @@ class Functional { } for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements}) { - auto& K_elem = element_gradients[type]; - auto& test_restrictions = form_.G_test_[type].restrictions; + auto& K_elem = element_gradients[type]; + auto& test_restrictions = form_.G_test_[type].restrictions; auto& trial_restrictions = form_.G_trial_[type][which_argument].restrictions; if (!K_elem.empty()) { @@ -856,7 +850,6 @@ class Functional { } } } - } // Copy the column indices to an auxilliary array as MFEM can mutate these during HypreParMatrix construction @@ -881,7 +874,7 @@ class Functional { return K; }; - #endif +#endif friend auto assemble(Gradient& g) { return g.assemble(); } @@ -918,10 +911,10 @@ class Functional { const mfem::ParFiniteElementSpace* test_space_; /// @brief Manages DOFs for the trial space - std::array< const mfem::ParFiniteElementSpace*, num_trial_spaces> trial_space_; + std::array trial_space_; - std::array< FunctionSpace, num_trial_spaces > trial_function_spaces_; - FunctionSpace test_function_space_; + std::array trial_function_spaces_; + FunctionSpace test_function_space_; /** * @brief Operator that converts true (global) DOF values to local (current rank) DOF values @@ -932,12 +925,12 @@ class Functional { /// @brief The input set of local DOF values (i.e., on the current rank) mutable mfem::Vector input_L_[num_trial_spaces]; - mutable std::vector input_E_buffer_; + mutable std::vector input_E_buffer_; mutable std::vector input_E_; mutable std::vector integrals_; - mutable mfem::Vector output_E_buffer_; + mutable mfem::Vector output_E_buffer_; mutable mfem::BlockVector output_E_; /// @brief The output set of local DOF values (i.e., on the current rank) @@ -949,10 +942,9 @@ class Functional { mutable mfem::Vector output_T_; /// @brief The objects representing the gradients w.r.t. each input argument of the Functional - mutable std::vector< Gradient > grad_; + mutable std::vector grad_; const mfem::MemoryType mem_type; - }; } // namespace serac diff --git a/src/serac/numerics/functional/functional_qoi.inl b/src/serac/numerics/functional/functional_qoi.inl index a894624a1d..bec030cadc 100644 --- a/src/serac/numerics/functional/functional_qoi.inl +++ b/src/serac/numerics/functional/functional_qoi.inl @@ -83,7 +83,7 @@ public: Functional(std::array trial_fes) : test_fec_(0, trial_fes[0]->GetMesh()->Dimension()), test_space_(dynamic_cast(trial_fes[0]->GetMesh()), &test_fec_, 1, serac::ordering), - trial_space_(trial_fes), + trial_space_(trial_fes), mem_type(mfem::Device::GetMemoryType()) { SERAC_MARK_FUNCTION; @@ -98,9 +98,9 @@ public: input_E_buffer_.push_back({}); } - std::array trial_families = {trials::family ...}; - std::array trial_orders = {trials::order ...}; - std::array trial_components = {trials::components ...}; + std::array trial_families = {trials::family...}; + std::array trial_orders = {trials::order...}; + std::array trial_components = {trials::components...}; for (uint32_t i = 0; i < num_trial_spaces; i++) { trial_function_spaces_[i] = {trial_families[i], trial_orders[i], trial_components[i]}; } @@ -118,7 +118,6 @@ public: for (uint32_t i = 0; i < num_trial_spaces; i++) { grad_.emplace_back(*this, i); } - } /** @@ -133,7 +132,6 @@ public: * and @a spatial_dim template parameter */ - template void AddDomainIntegral(Dimension, DependsOn, const lambda& integrand, Domain& domain, std::shared_ptr> qdata = NoQData) @@ -145,7 +143,7 @@ public: check_for_unsupported_elements(domain.mesh_); check_for_missing_nodal_gridfunc(domain.mesh_); - std::vector< uint32_t > arg_vec = {args ...}; + std::vector arg_vec = {args...}; for (uint32_t i : arg_vec) { domain.insert_restriction(trial_space_[i], trial_function_spaces_[i]); } @@ -176,7 +174,7 @@ public: check_for_missing_nodal_gridfunc(domain.mesh_); - std::vector< uint32_t > arg_vec = {args ...}; + std::vector arg_vec = {args...}; for (uint32_t i : arg_vec) { domain.insert_restriction(trial_space_[i], trial_function_spaces_[i]); } @@ -201,15 +199,14 @@ public: { check_for_missing_nodal_gridfunc(domain.mesh_); - std::vector< uint32_t > arg_vec = {args ...}; + std::vector arg_vec = {args...}; for (uint32_t i : arg_vec) { domain.insert_restriction(trial_space_[i], trial_function_spaces_[i]); check_interior_face_compatibility(domain.mesh_, trial_function_spaces_[i]); } using signature = test(decltype(serac::type(trial_spaces))...); - integrals_.push_back( - MakeInteriorFaceIntegral(domain, integrand, arg_vec)); + integrals_.push_back(MakeInteriorFaceIntegral(domain, integrand, arg_vec)); } /** @@ -223,7 +220,7 @@ public: * @brief Adds an area integral, i.e., over 2D elements in R^2 */ template - void AddAreaIntegral(DependsOn which_args, const lambda& integrand, Domain & domain, + void AddAreaIntegral(DependsOn which_args, const lambda& integrand, Domain& domain, std::shared_ptr>& data = NoQData) { AddDomainIntegral(Dimension<2>{}, which_args, integrand, domain, data); @@ -240,7 +237,7 @@ public: * @brief Adds a volume integral, i.e., over 3D elements in R^3 */ template - void AddVolumeIntegral(DependsOn which_args, const lambda& integrand, Domain & domain, + void AddVolumeIntegral(DependsOn which_args, const lambda& integrand, Domain& domain, std::shared_ptr>& data = NoQData) { AddDomainIntegral(Dimension<3>{}, which_args, integrand, domain, data); @@ -248,7 +245,7 @@ public: /// @brief alias for Functional::AddBoundaryIntegral(Dimension<2>{}, integrand, domain); template - void AddSurfaceIntegral(DependsOn which_args, const lambda& integrand, Domain & domain) + void AddSurfaceIntegral(DependsOn which_args, const lambda& integrand, Domain& domain) { AddBoundaryIntegral(Dimension<2>{}, which_args, integrand, domain); } @@ -270,11 +267,10 @@ public: output_L_ = 0.0; for (auto& integral : integrals_) { - if (integral.DependsOn(which)) { - Domain & dom = integral.domain_; + Domain& dom = integral.domain_; - const serac::BlockElementRestriction & G_trial = dom.get_restriction(trial_function_spaces_[which]); + const serac::BlockElementRestriction& G_trial = dom.get_restriction(trial_function_spaces_[which]); input_E_buffer_[which].SetSize(int(G_trial.ESize())); input_E_[which].Update(input_E_buffer_[which], G_trial.bOffsets()); G_trial.Gather(input_L_[which], input_E_[which]); @@ -287,7 +283,6 @@ public: // scatter-add to compute QoI value for the local processor G_test_.ScatterAdd(output_E_, output_L_); } - } // compute global QoI value by summing values from different processors @@ -319,10 +314,10 @@ public: output_L_ = 0.0; for (auto& integral : integrals_) { - Domain & dom = integral.domain_; + Domain& dom = integral.domain_; for (auto i : integral.active_trial_spaces_) { - const serac::BlockElementRestriction & G_trial = dom.get_restriction(trial_function_spaces_[i]); + const serac::BlockElementRestriction& G_trial = dom.get_restriction(trial_function_spaces_[i]); input_E_buffer_[i].SetSize(int(G_trial.ESize())); input_E_[i].Update(input_E_buffer_[i], G_trial.bOffsets()); G_trial.Gather(input_L_[i], input_E_[i]); @@ -405,15 +400,16 @@ private: double operator()(const mfem::Vector& x) const { return form_.ActionOfGradient(x, which_argument); } - uint64_t max_buffer_size() { + uint64_t max_buffer_size() + { uint64_t max_entries = 0; - for (auto & integral : form_.integrals_) { + for (auto& integral : form_.integrals_) { if (integral.DependsOn(which_argument)) { - Domain & dom = integral.domain_; + Domain& dom = integral.domain_; const auto& G_trial = dom.get_restriction(form_.trial_function_spaces_[which_argument]); for (const auto& [geom, test_restriction] : G_trial.restrictions) { - const auto& trial_restriction = G_trial.restrictions.at(geom); - uint64_t entries_per_element = trial_restriction.nodes_per_elem * trial_restriction.components; + const auto& trial_restriction = G_trial.restrictions.at(geom); + uint64_t entries_per_element = trial_restriction.nodes_per_elem * trial_restriction.components; max_entries = std::max(max_entries, trial_restriction.num_elements * entries_per_element); } } @@ -433,32 +429,28 @@ private: std::map> element_gradients[Domain::num_types]; -//////////////////////////////////////////////////////////////////////////////// - - for (auto & integral : form_.integrals_) { + //////////////////////////////////////////////////////////////////////////////// - Domain & dom = integral.domain_; + for (auto& integral : form_.integrals_) { + Domain& dom = integral.domain_; // if this integral's derivative isn't identically zero if (integral.functional_to_integral_index_.count(which_argument) > 0) { - - uint32_t id = integral.functional_to_integral_index_.at(which_argument); + uint32_t id = integral.functional_to_integral_index_.at(which_argument); const auto& G_trial = dom.get_restriction(form_.trial_function_spaces_[which_argument]); for (const auto& [geom, calculate_element_gradients] : integral.element_gradient_[id]) { - const auto& trial_restriction = G_trial.restrictions.at(geom); // prepare a buffer to hold the element matrices - CPUArrayView K_e(K_elem_buffer.data(), - trial_restriction.num_elements, 1, - trial_restriction.nodes_per_elem * trial_restriction.components); + CPUArrayView K_e(K_elem_buffer.data(), trial_restriction.num_elements, 1, + trial_restriction.nodes_per_elem * trial_restriction.components); detail::zero_out(K_e); calculate_element_gradients(K_e); - const std::vector & element_ids = integral.domain_.get(geom); + const std::vector& element_ids = integral.domain_.get(geom); - uint32_t cols_per_elem = uint32_t(trial_restriction.nodes_per_elem * trial_restriction.components); + uint32_t cols_per_elem = uint32_t(trial_restriction.nodes_per_elem * trial_restriction.components); std::vector trial_vdofs(cols_per_elem); for (uint32_t e = 0; e < element_ids.size(); e++) { @@ -469,12 +461,11 @@ private: gradient_L_[col] += K_e(e, 0, i); } } - } } } -//////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// form_.P_trial_[which_argument]->MultTranspose(gradient_L_, *gradient_T); @@ -501,7 +492,7 @@ private: /// @brief Manages DOFs for the trial space std::array trial_space_; - std::array< FunctionSpace, num_trial_spaces > trial_function_spaces_; + std::array trial_function_spaces_; /** * @brief Operator that converts true (global) DOF values to local (current rank) DOF values @@ -512,12 +503,12 @@ private: /// @brief The input set of local DOF values (i.e., on the current rank) mutable mfem::Vector input_L_[num_trial_spaces]; - mutable std::vector input_E_buffer_; + mutable std::vector input_E_buffer_; mutable std::vector input_E_; mutable std::vector integrals_; - mutable mfem::Vector output_E_buffer_; + mutable mfem::Vector output_E_buffer_; mutable mfem::BlockVector output_E_; QoIElementRestriction G_test_; @@ -534,7 +525,6 @@ private: mutable std::vector grad_; const mfem::MemoryType mem_type; - }; } // namespace serac diff --git a/src/serac/numerics/functional/geometric_factors.cpp b/src/serac/numerics/functional/geometric_factors.cpp index b9fc48d078..f286783470 100644 --- a/src/serac/numerics/functional/geometric_factors.cpp +++ b/src/serac/numerics/functional/geometric_factors.cpp @@ -35,7 +35,6 @@ void compute_geometric_factors(mfem::Vector& positions_q, mfem::Vector& jacobian // for each element in the domain for (uint32_t e = 0; e < num_elements; e++) { - // load the positions for the nodes in this element auto X_e = X[e]; @@ -62,12 +61,12 @@ void compute_geometric_factors(mfem::Vector& positions_q, mfem::Vector& jacobian GeometricFactors::GeometricFactors(const Domain& domain, int q, mfem::Geometry::Type geom) { - //const mfem::ParGridFunction* nodes = static_cast< const mfem::ParGridFunction * >(domain.mesh_.GetNodes()); - // mfem::ParFiniteElementSpace * pfes = nodes->ParFESpace(); - const mfem::GridFunction* nodes = domain.mesh_.GetNodes(); - const mfem::FiniteElementSpace * fes = nodes->FESpace(); + // const mfem::ParGridFunction* nodes = static_cast< const mfem::ParGridFunction * >(domain.mesh_.GetNodes()); + // mfem::ParFiniteElementSpace * pfes = nodes->ParFESpace(); + const mfem::GridFunction* nodes = domain.mesh_.GetNodes(); + const mfem::FiniteElementSpace* fes = nodes->FESpace(); - const std::vector & element_ids = domain.get_mfem_ids(geom); + const std::vector& element_ids = domain.get_mfem_ids(geom); auto restriction = serac::ElementRestriction(fes, geom, element_ids); mfem::Vector X_e(int(restriction.ESize())); @@ -85,10 +84,11 @@ GeometricFactors::GeometricFactors(const Domain& domain, int q, mfem::Geometry:: X = mfem::Vector(int(num_elements) * qpts_per_elem * spatial_dim); J = mfem::Vector(int(num_elements) * qpts_per_elem * spatial_dim * geometry_dim); -#define DISPATCH_KERNEL(GEOM, P, Q, BDR) \ - if (geom == mfem::Geometry::GEOM && p == P && q == Q && (spatial_dim - geometry_dim) == BDR) { \ - compute_geometric_factors >(X, J, X_e, element_ids); \ - return; \ +#define DISPATCH_KERNEL(GEOM, P, Q, BDR) \ + if (geom == mfem::Geometry::GEOM && p == P && q == Q && (spatial_dim - geometry_dim) == BDR) { \ + compute_geometric_factors >(X, J, X_e, \ + element_ids); \ + return; \ } DISPATCH_KERNEL(SEGMENT, 1, 1, 1); @@ -106,7 +106,7 @@ GeometricFactors::GeometricFactors(const Domain& domain, int q, mfem::Geometry:: DISPATCH_KERNEL(SEGMENT, 3, 3, 1); DISPATCH_KERNEL(SEGMENT, 3, 4, 1); -/////////////////////////////////////// + /////////////////////////////////////// DISPATCH_KERNEL(TRIANGLE, 1, 1, 0); DISPATCH_KERNEL(TRIANGLE, 1, 2, 0); @@ -138,7 +138,7 @@ GeometricFactors::GeometricFactors(const Domain& domain, int q, mfem::Geometry:: DISPATCH_KERNEL(TRIANGLE, 3, 3, 1); DISPATCH_KERNEL(TRIANGLE, 3, 4, 1); -/////////////////////////////////////// + /////////////////////////////////////// DISPATCH_KERNEL(SQUARE, 1, 1, 0); DISPATCH_KERNEL(SQUARE, 1, 2, 0); @@ -170,7 +170,7 @@ GeometricFactors::GeometricFactors(const Domain& domain, int q, mfem::Geometry:: DISPATCH_KERNEL(SQUARE, 3, 3, 1); DISPATCH_KERNEL(SQUARE, 3, 4, 1); -/////////////////////////////////////// + /////////////////////////////////////// DISPATCH_KERNEL(TETRAHEDRON, 1, 1, 0); DISPATCH_KERNEL(TETRAHEDRON, 1, 2, 0); @@ -187,7 +187,7 @@ GeometricFactors::GeometricFactors(const Domain& domain, int q, mfem::Geometry:: DISPATCH_KERNEL(TETRAHEDRON, 3, 3, 0); DISPATCH_KERNEL(TETRAHEDRON, 3, 4, 0); -/////////////////////////////////////// + /////////////////////////////////////// DISPATCH_KERNEL(CUBE, 1, 1, 0); DISPATCH_KERNEL(CUBE, 1, 2, 0); diff --git a/src/serac/numerics/functional/geometric_factors.hpp b/src/serac/numerics/functional/geometric_factors.hpp index 4a0659c710..9983e6aff9 100644 --- a/src/serac/numerics/functional/geometric_factors.hpp +++ b/src/serac/numerics/functional/geometric_factors.hpp @@ -14,7 +14,6 @@ namespace serac { * calculations on boundary elements and on simplex elements */ struct GeometricFactors { - /// @brief default ctor, leaving this object uninitialized GeometricFactors(){}; diff --git a/src/serac/numerics/functional/integral.hpp b/src/serac/numerics/functional/integral.hpp index f5759cd6c2..c36407b731 100644 --- a/src/serac/numerics/functional/integral.hpp +++ b/src/serac/numerics/functional/integral.hpp @@ -80,7 +80,8 @@ struct Integral { mpi::out << geometry << std::endl; for (std::size_t i = 0; i < active_trial_spaces_.size(); i++) { inputs[i] = input_E[uint32_t(active_trial_spaces_[i])].GetBlock(geometry).Read(); - mpi::out << "input_E[" << i << "].Size(): " << input_E[uint32_t(active_trial_spaces_[i])].GetBlock(geometry).Size() << std::endl; + mpi::out << "input_E[" << i + << "].Size(): " << input_E[uint32_t(active_trial_spaces_[i])].GetBlock(geometry).Size() << std::endl; } func(t, inputs, output_E.GetBlock(geometry).ReadWrite(), update_state); } @@ -96,7 +97,8 @@ struct Integral { * @param differentiation_index a non-negative value indicates directional derivative with respect to the trial space * with that index. */ - void GradientMult(const mfem::BlockVector& dinput_E, mfem::BlockVector& doutput_E, uint32_t differentiation_index) const + void GradientMult(const mfem::BlockVector& dinput_E, mfem::BlockVector& doutput_E, + uint32_t differentiation_index) const { doutput_E = 0.0; @@ -125,14 +127,15 @@ struct Integral { } } } - + /** - * @brief returns whether or not this integral depends on argument `which` - * + * @brief returns whether or not this integral depends on argument `which` + * * @param which an argument index - * @return true when if this Integral object was created with a DependsOn<...> statement that includes `i` + * @return true when if this Integral object was created with a DependsOn<...> statement that includes `i` */ - bool DependsOn(uint32_t which) const { + bool DependsOn(uint32_t which) const + { for (uint32_t i : active_trial_spaces_) { if (which == i) return true; } @@ -229,11 +232,10 @@ void generate_kernels(FunctionSignature s, Integral& integral, using derivative_type = decltype(domain_integral::get_derivative_type(qf, qpt_data_type{})); auto ptr = accelerator::make_shared_array(num_elements * qpts_per_element); - integral.evaluation_with_AD_[index][geom] = domain_integral::evaluation_kernel( - s, qf, positions, jacobians, qdata, ptr, num_elements); + integral.evaluation_with_AD_[index][geom] = + domain_integral::evaluation_kernel(s, qf, positions, jacobians, qdata, ptr, num_elements); - integral.jvp_[index][geom] = - domain_integral::jacobian_vector_product_kernel(s, ptr, num_elements); + integral.jvp_[index][geom] = domain_integral::jacobian_vector_product_kernel(s, ptr, num_elements); integral.element_gradient_[index][geom] = domain_integral::element_gradient_kernel(s, ptr, num_elements); }); @@ -364,8 +366,8 @@ Integral MakeBoundaryIntegral(const Domain& domain, const lambda_type& qf, std:: } /** - * @brief function to generate kernels held by an `Integral` object of type "InteriorFaceDomain", with a specific element - * type + * @brief function to generate kernels held by an `Integral` object of type "InteriorFaceDomain", with a specific + * element type * * @tparam geom the element geometry * @tparam Q a parameter that controls the number of quadrature points diff --git a/src/serac/numerics/functional/interior_face_integral_kernels.hpp b/src/serac/numerics/functional/interior_face_integral_kernels.hpp index 4b5667276f..6d61808e51 100644 --- a/src/serac/numerics/functional/interior_face_integral_kernels.hpp +++ b/src/serac/numerics/functional/interior_face_integral_kernels.hpp @@ -53,15 +53,15 @@ struct QFunctionArgument, Dimension> { }; /// @overload -template -struct QFunctionArgument, Dimension > { +template +struct QFunctionArgument, Dimension> { using type = serac::tuple; ///< what will be passed to the q-function }; /// @overload -template -struct QFunctionArgument, Dimension > { - using type = serac::tuple< tensor , tensor >; ///< what will be passed to the q-function +template +struct QFunctionArgument, Dimension> { + using type = serac::tuple, tensor>; ///< what will be passed to the q-function }; /// @overload @@ -167,11 +167,12 @@ void evaluation_kernel_impl(trial_element_type trial_elements, test_element, dou [[maybe_unused]] tuple u = { reinterpret_cast(trial_elements))::dof_type_if*>(inputs[indices])...}; - ((mpi::out << "sizeof(dof_type_if) / sizeof(double): " << sizeof(decltype(get(u)[0])) / sizeof(double) << std::endl), ...); + ((mpi::out << "sizeof(dof_type_if) / sizeof(double): " << sizeof(decltype(get(u)[0])) / sizeof(double) + << std::endl), + ...); // for each element in the domain for (uint32_t e = 0; e < num_elements; e++) { - mpi::out << "e: " << e << " / " << num_elements << std::endl; // load the jacobians and positions for each quadrature point in this element @@ -261,10 +262,10 @@ void action_of_gradient_kernel(const double* dU, double* dR, derivatives_type* q // mfem provides this information in 1D arrays, so we reshape it // into strided multidimensional arrays before using - constexpr bool is_QOI = (test::family == Family::QOI); - constexpr int nqp = num_quadrature_points(geom, Q); - auto du = reinterpret_cast(dU); - auto dr = reinterpret_cast(dR); + constexpr bool is_QOI = (test::family == Family::QOI); + constexpr int nqp = num_quadrature_points(geom, Q); + auto du = reinterpret_cast(dU); + auto dr = reinterpret_cast(dR); static constexpr TensorProductQuadratureRule rule{}; // for each element in the domain @@ -302,9 +303,9 @@ void action_of_gradient_kernel(const double* dU, double* dR, derivatives_type* q * @param[in] num_elements The number of elements in the mesh */ template -void element_gradient_kernel([[maybe_unused]] ExecArrayView dK, - [[maybe_unused]] derivatives_type* qf_derivatives, - [[maybe_unused]] std::size_t num_elements) +void element_gradient_kernel([[maybe_unused]] ExecArrayView dK, + [[maybe_unused]] derivatives_type* qf_derivatives, + [[maybe_unused]] std::size_t num_elements) { using test_element = finite_element; using trial_element = finite_element; @@ -340,7 +341,6 @@ void element_gradient_kernel([[maybe_unused]] ExecArrayView)> element_gradi }; } -} // namespace boundary_integral +} // namespace interior_face_integral } // namespace serac diff --git a/src/serac/numerics/functional/shape_aware_functional.hpp b/src/serac/numerics/functional/shape_aware_functional.hpp index 0a0e688684..9de6eb1ae9 100644 --- a/src/serac/numerics/functional/shape_aware_functional.hpp +++ b/src/serac/numerics/functional/shape_aware_functional.hpp @@ -453,7 +453,7 @@ class ShapeAwareFunctional { * and @a spatial_dim template parameter */ template - void AddDomainIntegral(Dimension, DependsOn, const lambda& integrand, Domain & domain, + void AddDomainIntegral(Dimension, DependsOn, const lambda& integrand, Domain& domain, std::shared_ptr> qdata = NoQData) { if constexpr (std::is_same_v) { @@ -512,7 +512,7 @@ class ShapeAwareFunctional { * and @a spatial_dim template parameter */ template - void AddBoundaryIntegral(Dimension, DependsOn, const lambda& integrand, Domain & domain) + void AddBoundaryIntegral(Dimension, DependsOn, const lambda& integrand, Domain& domain) { functional_->AddBoundaryIntegral(Dimension{}, DependsOn<0, (args + 1)...>{}, ShapeAwareBoundaryIntegrandWrapper(integrand), domain); diff --git a/src/serac/numerics/functional/tests/bug_residual.cpp b/src/serac/numerics/functional/tests/bug_residual.cpp index 9d48398e98..4f393facd4 100644 --- a/src/serac/numerics/functional/tests/bug_residual.cpp +++ b/src/serac/numerics/functional/tests/bug_residual.cpp @@ -32,7 +32,7 @@ int num_procs, myid; // this is an attempt to reproduce an error message described by Mike void test() { - constexpr int p = 1; + constexpr int p = 1; constexpr int dim = 2; #ifdef L2_SCALAR_SPACE @@ -43,51 +43,48 @@ void test() using vector_space = serac::H1; std::string meshfile = SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"; - auto pmesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 1); + auto pmesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 1); - auto L2fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); + auto L2fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); mfem::ParFiniteElementSpace L2_fespace(pmesh.get(), &L2fec, 1, serac::ordering); #ifdef L2_SCALAR_SPACE auto scalar_fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); #else - auto scalar_fec = mfem::H1_FECollection(p, dim); + auto scalar_fec = mfem::H1_FECollection(p, dim); #endif mfem::ParFiniteElementSpace scalar_fespace(pmesh.get(), &scalar_fec, 1, serac::ordering); - auto vector_fec = mfem::H1_FECollection(p, dim); + auto vector_fec = mfem::H1_FECollection(p, dim); mfem::ParFiniteElementSpace vector_fespace(pmesh.get(), &vector_fec, dim, serac::ordering); Domain whole_domain = EntireDomain(*pmesh); - Functional< scalar_space(scalar_space, scalar_space, vector_space, vector_space) > residual( - &scalar_fespace, - {&scalar_fespace, &scalar_fespace, &vector_fespace, &vector_fespace} - ); + Functional residual( + &scalar_fespace, {&scalar_fespace, &scalar_fespace, &vector_fespace, &vector_fespace}); - residual.AddDomainIntegral(serac::Dimension{}, serac::DependsOn<0,1,2,3>{}, - [=](double time, auto /*X*/, auto Rho, auto Rho_dot, auto U0, auto UF) { - auto U = UF * time + U0 * (1.0 - time); + residual.AddDomainIntegral( + serac::Dimension{}, serac::DependsOn<0, 1, 2, 3>{}, + [=](double time, auto /*X*/, auto Rho, auto Rho_dot, auto U0, auto UF) { + auto U = UF * time + U0 * (1.0 - time); auto dx_dX = get(U) + Identity(); auto dX_dx = inv(dx_dX); - - auto v = get(UF) - get(U0); - auto v_X = get(UF) - get(U0); - auto v_dx = dot(v_X, dX_dx); + + auto v = get(UF) - get(U0); + auto v_X = get(UF) - get(U0); + auto v_dx = dot(v_X, dX_dx); auto div_v = serac::tr(v_dx); - + auto rho_dot = get(Rho_dot); - auto rho = get(Rho); - - auto J = det(dx_dX); - auto JrhoV = J * dot(v, transpose(dX_dx)); - //std::string y = rho; - //std::string z = rho*JrhoV; - return serac::tuple{J*(rho_dot + rho*div_v), rho * JrhoV}; - }, - whole_domain - ); + auto rho = get(Rho); + auto J = det(dx_dX); + auto JrhoV = J * dot(v, transpose(dX_dx)); + // std::string y = rho; + // std::string z = rho*JrhoV; + return serac::tuple{J * (rho_dot + rho * div_v), rho * JrhoV}; + }, + whole_domain); } int main(int argc, char* argv[]) diff --git a/src/serac/numerics/functional/tests/check_gradient.hpp b/src/serac/numerics/functional/tests/check_gradient.hpp index 53e8f531b9..e8fcdc3766 100644 --- a/src/serac/numerics/functional/tests/check_gradient.hpp +++ b/src/serac/numerics/functional/tests/check_gradient.hpp @@ -14,8 +14,9 @@ #include "serac/numerics/functional/functional.hpp" template -void debug_sparse_matrix(serac::Functional& f, double t, const mfem::Vector& U, [[maybe_unused]] double epsilon = 1.0e-4) { - +void debug_sparse_matrix(serac::Functional& f, double t, const mfem::Vector& U, + [[maybe_unused]] double epsilon = 1.0e-4) +{ mfem::Vector dU(U.Size()); dU = 0.0; @@ -24,7 +25,7 @@ void debug_sparse_matrix(serac::Functional& f, double t, const mfem::Vector& std::cout << "{"; for (int i = 0; i < U.Size(); i++) { - dU[i] = 1; + dU[i] = 1; mfem::Vector df_jvp = dfdU(dU); // matrix-free std::cout << "{"; @@ -48,7 +49,6 @@ void debug_sparse_matrix(serac::Functional& f, double t, const mfem::Vector& std::cout << "}" << std::endl; dfdU_matrix->Print("K.mtx"); - } template @@ -125,7 +125,6 @@ void check_gradient(serac::Functional& f, double t, mfem::Vector& U, do double e3 = df1_cd[0].DistanceTo(df_jvp1.GetData()) / denominator; double e4 = df1_cd[1].DistanceTo(df_jvp1.GetData()) / denominator; EXPECT_TRUE((fabs(e3 / e4 - 4.0) < 0.1) || fmin(e3, e4) < 1.0e-9); - } template diff --git a/src/serac/numerics/functional/tests/cuda_sandbox.cu b/src/serac/numerics/functional/tests/cuda_sandbox.cu index aae7098ad8..7d6ddaf1c9 100644 --- a/src/serac/numerics/functional/tests/cuda_sandbox.cu +++ b/src/serac/numerics/functional/tests/cuda_sandbox.cu @@ -4,84 +4,119 @@ namespace serac { -enum class Family { - H1, - Hcurl, - Hdiv, +enum class Family +{ + H1, + Hcurl, + Hdiv, DG }; -template < mfem::Geometry::Type g, Family f > +template struct FiniteElement; template <> -struct FiniteElement< mfem::Geometry::TRIANGLE, Family::H1 > { - +struct FiniteElement { using source_type = double; - using flux_type = vec2; + using flux_type = vec2; static constexpr int dim = 2; uint32_t num_nodes() const { return ((p + 1) * (p + 2)) / 2; } - constexpr double shape_function(vec2 xi, uint32_t i) const { + constexpr double shape_function(vec2 xi, uint32_t i) const + { if (p == 1) { if (i == 0) return 1.0 - xi[0] - xi[1]; if (i == 1) return xi[0]; if (i == 2) return xi[1]; } if (p == 2) { - if (i == 0) return (-1+xi[0]+xi[1])*(-1+2*xi[0]+2*xi[1]); - if (i == 1) return -4*xi[0]*(-1+xi[0]+xi[1]); - if (i == 2) return xi[0]*(-1+2*xi[0]); - if (i == 3) return -4*xi[1]*(-1+xi[0]+xi[1]); - if (i == 4) return 4*xi[0]*xi[1]; - if (i == 5) return xi[1]*(-1+2*xi[1]); + if (i == 0) return (-1 + xi[0] + xi[1]) * (-1 + 2 * xi[0] + 2 * xi[1]); + if (i == 1) return -4 * xi[0] * (-1 + xi[0] + xi[1]); + if (i == 2) return xi[0] * (-1 + 2 * xi[0]); + if (i == 3) return -4 * xi[1] * (-1 + xi[0] + xi[1]); + if (i == 4) return 4 * xi[0] * xi[1]; + if (i == 5) return xi[1] * (-1 + 2 * xi[1]); } if (p == 3) { double sqrt5 = 2.23606797749978981; - if (i == 0) return -((-1+xi[0]+xi[1])*(1+5*xi[0]*xi[0]+5*(-1+xi[1])*xi[1]+xi[0]*(-5+11*xi[1]))); - if (i == 1) return (5*xi[0]*(-1+xi[0]+xi[1])*(-1-sqrt5+2*sqrt5*xi[0]+(3+sqrt5)*xi[1]))/2.0; - if (i == 2) return (-5*xi[0]*(-1+xi[0]+xi[1])*(1-sqrt5+2*sqrt5*xi[0]+(-3+sqrt5)*xi[1]))/2.0; - if (i == 3) return xi[0]*(1+5*xi[0]*xi[0]+xi[1]-xi[1]*xi[1]-xi[0]*(5+xi[1])); - if (i == 4) return (5*xi[1]*(-1+xi[0]+xi[1])*(-1-sqrt5+(3+sqrt5)*xi[0]+2*sqrt5*xi[1]))/2.0; - if (i == 5) return -27*xi[0]*xi[1]*(-1+xi[0]+xi[1]); - if (i == 6) return (5*xi[0]*xi[1]*(-2+(3+sqrt5)*xi[0]-(-3+sqrt5)*xi[1]))/2.; - if (i == 7) return (5*xi[1]*(-1+xi[0]+xi[1])*(5-3*sqrt5+2*(-5+2*sqrt5)*xi[0]+5*(-1+sqrt5)*xi[1]))/(-5+sqrt5); - if (i == 8) return (-5*xi[0]*xi[1]*(2+(-3+sqrt5)*xi[0]-(3+sqrt5)*xi[1]))/2.; - if (i == 9) return xi[1]*(1+xi[0]-xi[0]*xi[0]-xi[0]*xi[1]+5*(-1+xi[1])*xi[1]); + if (i == 0) + return -((-1 + xi[0] + xi[1]) * (1 + 5 * xi[0] * xi[0] + 5 * (-1 + xi[1]) * xi[1] + xi[0] * (-5 + 11 * xi[1]))); + if (i == 1) + return (5 * xi[0] * (-1 + xi[0] + xi[1]) * (-1 - sqrt5 + 2 * sqrt5 * xi[0] + (3 + sqrt5) * xi[1])) / 2.0; + if (i == 2) + return (-5 * xi[0] * (-1 + xi[0] + xi[1]) * (1 - sqrt5 + 2 * sqrt5 * xi[0] + (-3 + sqrt5) * xi[1])) / 2.0; + if (i == 3) return xi[0] * (1 + 5 * xi[0] * xi[0] + xi[1] - xi[1] * xi[1] - xi[0] * (5 + xi[1])); + if (i == 4) + return (5 * xi[1] * (-1 + xi[0] + xi[1]) * (-1 - sqrt5 + (3 + sqrt5) * xi[0] + 2 * sqrt5 * xi[1])) / 2.0; + if (i == 5) return -27 * xi[0] * xi[1] * (-1 + xi[0] + xi[1]); + if (i == 6) return (5 * xi[0] * xi[1] * (-2 + (3 + sqrt5) * xi[0] - (-3 + sqrt5) * xi[1])) / 2.; + if (i == 7) + return (5 * xi[1] * (-1 + xi[0] + xi[1]) * + (5 - 3 * sqrt5 + 2 * (-5 + 2 * sqrt5) * xi[0] + 5 * (-1 + sqrt5) * xi[1])) / + (-5 + sqrt5); + if (i == 8) return (-5 * xi[0] * xi[1] * (2 + (-3 + sqrt5) * xi[0] - (3 + sqrt5) * xi[1])) / 2.; + if (i == 9) return xi[1] * (1 + xi[0] - xi[0] * xi[0] - xi[0] * xi[1] + 5 * (-1 + xi[1]) * xi[1]); } return -1.0; } - vec2 shape_function_gradient(vec2 xi, uint32_t i) const { + vec2 shape_function_gradient(vec2 xi, uint32_t i) const + { // expressions generated symbolically by mathematica if (p == 1) { if (i == 0) return {-1.0, -1.0}; - if (i == 1) return { 1.0, 0.0}; - if (i == 2) return { 0.0, 1.0}; + if (i == 1) return {1.0, 0.0}; + if (i == 2) return {0.0, 1.0}; } if (p == 2) { - if (i == 0) return {-3+4*xi[0]+4*xi[1], -3+4*xi[0]+4*xi[1]}; - if (i == 1) return {-4*(-1+2*xi[0]+xi[1]), -4*xi[0]}; - if (i == 2) return {-1+4*xi[0], 0}; - if (i == 3) return {-4*xi[1], -4*(-1+xi[0]+2*xi[1])}; - if (i == 4) return {4*xi[1], 4*xi[0]}; - if (i == 5) return {0, -1+4*xi[1]}; + if (i == 0) return {-3 + 4 * xi[0] + 4 * xi[1], -3 + 4 * xi[0] + 4 * xi[1]}; + if (i == 1) return {-4 * (-1 + 2 * xi[0] + xi[1]), -4 * xi[0]}; + if (i == 2) return {-1 + 4 * xi[0], 0}; + if (i == 3) return {-4 * xi[1], -4 * (-1 + xi[0] + 2 * xi[1])}; + if (i == 4) return {4 * xi[1], 4 * xi[0]}; + if (i == 5) return {0, -1 + 4 * xi[1]}; } if (p == 3) { double sqrt5 = 2.23606797749978981; - if (i == 0) return {-6-15*xi[0]*xi[0]+4*xi[0]*(5-8*xi[1])+(21-16*xi[1])*xi[1], -6-16*xi[0]*xi[0]+xi[0]*(21-32*xi[1])+5*(4-3*xi[1])*xi[1]}; - if (i == 1) return {(5*(6*sqrt5*xi[0]*xi[0]+xi[0]*(-2-6*sqrt5+6*(1+sqrt5)*xi[1])+(-1+xi[1])*(-1-sqrt5+(3+sqrt5)*xi[1])))/2., (5*xi[0]*(-2*(2+sqrt5)+3*(1+sqrt5)*xi[0]+2*(3+sqrt5)*xi[1]))/2.}; - if (i == 2) return {(-5*(6*sqrt5*xi[0]*xi[0]+(-1+xi[1])*(1-sqrt5+(-3+sqrt5)*xi[1])+xi[0]*(2-6*sqrt5+6*(-1+sqrt5)*xi[1])))/2., (-5*xi[0]*(4-2*sqrt5+3*(-1+sqrt5)*xi[0]+2*(-3+sqrt5)*xi[1]))/2.}; - if (i == 3) return {1+15*xi[0]*xi[0]+xi[1]-xi[1]*xi[1]-2*xi[0]*(5+xi[1]), -(xi[0]*(-1+xi[0]+2*xi[1]))}; - if (i == 4) return {(5*xi[1]*(-2*(2+sqrt5)+2*(3+sqrt5)*xi[0]+3*(1+sqrt5)*xi[1]))/2., (5*(1+sqrt5-2*(2+sqrt5)*xi[0]+(3+sqrt5)*xi[0]*xi[0]+6*(1+sqrt5)*xi[0]*xi[1]+2*xi[1]*(-1-3*sqrt5+3*sqrt5*xi[1])))/2.}; - if (i == 5) return {-27*xi[1]*(-1+2*xi[0]+xi[1]), -27*xi[0]*(-1+xi[0]+2*xi[1])}; - if (i == 6) return {(-5*xi[1]*(2-2*(3+sqrt5)*xi[0]+(-3+sqrt5)*xi[1]))/2., (5*xi[0]*(-2+(3+sqrt5)*xi[0]-2*(-3+sqrt5)*xi[1]))/2.}; - if (i == 7) return {(-5*xi[1]*(4-2*sqrt5+2*(-3+sqrt5)*xi[0]+3*(-1+sqrt5)*xi[1]))/2., (-5*(-1+sqrt5+(-3+sqrt5)*xi[0]*xi[0]+2*xi[1]*(1-3*sqrt5+3*sqrt5*xi[1])+xi[0]*(4-2*sqrt5+6*(-1+sqrt5)*xi[1])))/2.}; - if (i == 8) return {(5*xi[1]*(-2-2*(-3+sqrt5)*xi[0]+(3+sqrt5)*xi[1]))/2., (-5*xi[0]*(2+(-3+sqrt5)*xi[0]-2*(3+sqrt5)*xi[1]))/2.}; - if (i == 9) return {-(xi[1]*(-1+2*xi[0]+xi[1])), 1+xi[0]-xi[0]*xi[0]-2*(5+xi[0])*xi[1]+15*xi[1]*xi[1]}; + if (i == 0) + return {-6 - 15 * xi[0] * xi[0] + 4 * xi[0] * (5 - 8 * xi[1]) + (21 - 16 * xi[1]) * xi[1], + -6 - 16 * xi[0] * xi[0] + xi[0] * (21 - 32 * xi[1]) + 5 * (4 - 3 * xi[1]) * xi[1]}; + if (i == 1) + return {(5 * (6 * sqrt5 * xi[0] * xi[0] + xi[0] * (-2 - 6 * sqrt5 + 6 * (1 + sqrt5) * xi[1]) + + (-1 + xi[1]) * (-1 - sqrt5 + (3 + sqrt5) * xi[1]))) / + 2., + (5 * xi[0] * (-2 * (2 + sqrt5) + 3 * (1 + sqrt5) * xi[0] + 2 * (3 + sqrt5) * xi[1])) / 2.}; + if (i == 2) + return {(-5 * (6 * sqrt5 * xi[0] * xi[0] + (-1 + xi[1]) * (1 - sqrt5 + (-3 + sqrt5) * xi[1]) + + xi[0] * (2 - 6 * sqrt5 + 6 * (-1 + sqrt5) * xi[1]))) / + 2., + (-5 * xi[0] * (4 - 2 * sqrt5 + 3 * (-1 + sqrt5) * xi[0] + 2 * (-3 + sqrt5) * xi[1])) / 2.}; + if (i == 3) + return {1 + 15 * xi[0] * xi[0] + xi[1] - xi[1] * xi[1] - 2 * xi[0] * (5 + xi[1]), + -(xi[0] * (-1 + xi[0] + 2 * xi[1]))}; + if (i == 4) + return {(5 * xi[1] * (-2 * (2 + sqrt5) + 2 * (3 + sqrt5) * xi[0] + 3 * (1 + sqrt5) * xi[1])) / 2., + (5 * (1 + sqrt5 - 2 * (2 + sqrt5) * xi[0] + (3 + sqrt5) * xi[0] * xi[0] + + 6 * (1 + sqrt5) * xi[0] * xi[1] + 2 * xi[1] * (-1 - 3 * sqrt5 + 3 * sqrt5 * xi[1]))) / + 2.}; + if (i == 5) return {-27 * xi[1] * (-1 + 2 * xi[0] + xi[1]), -27 * xi[0] * (-1 + xi[0] + 2 * xi[1])}; + if (i == 6) + return {(-5 * xi[1] * (2 - 2 * (3 + sqrt5) * xi[0] + (-3 + sqrt5) * xi[1])) / 2., + (5 * xi[0] * (-2 + (3 + sqrt5) * xi[0] - 2 * (-3 + sqrt5) * xi[1])) / 2.}; + if (i == 7) + return {(-5 * xi[1] * (4 - 2 * sqrt5 + 2 * (-3 + sqrt5) * xi[0] + 3 * (-1 + sqrt5) * xi[1])) / 2., + (-5 * (-1 + sqrt5 + (-3 + sqrt5) * xi[0] * xi[0] + 2 * xi[1] * (1 - 3 * sqrt5 + 3 * sqrt5 * xi[1]) + + xi[0] * (4 - 2 * sqrt5 + 6 * (-1 + sqrt5) * xi[1]))) / + 2.}; + if (i == 8) + return {(5 * xi[1] * (-2 - 2 * (-3 + sqrt5) * xi[0] + (3 + sqrt5) * xi[1])) / 2., + (-5 * xi[0] * (2 + (-3 + sqrt5) * xi[0] - 2 * (3 + sqrt5) * xi[1])) / 2.}; + if (i == 9) + return {-(xi[1] * (-1 + 2 * xi[0] + xi[1])), + 1 + xi[0] - xi[0] * xi[0] - 2 * (5 + xi[0]) * xi[1] + 15 * xi[1] * xi[1]}; } return {}; @@ -194,10 +229,9 @@ struct FiniteElement< mfem::Geometry::TRIANGLE, Family::H1 > { #endif int p; - }; -} +} // namespace serac //#include "elements/h1_edge.hpp" //#include "elements/h1_triangle.hpp" @@ -207,6 +241,4 @@ struct FiniteElement< mfem::Geometry::TRIANGLE, Family::H1 > { __global__ void kernel() {} -int main() { - kernel<<<1,1>>>(); -} +int main() { kernel<<<1, 1>>>(); } diff --git a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp index 3df6a451a6..67775dbeb9 100644 --- a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp +++ b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp @@ -9,7 +9,8 @@ using namespace serac; std::string mesh_dir = SERAC_REPO_DIR "/data/meshes/"; -constexpr mfem::Geometry::Type face_type(mfem::Geometry::Type geom) { +constexpr mfem::Geometry::Type face_type(mfem::Geometry::Type geom) +{ if (geom == mfem::Geometry::TRIANGLE) return mfem::Geometry::SEGMENT; if (geom == mfem::Geometry::SQUARE) return mfem::Geometry::SEGMENT; if (geom == mfem::Geometry::TETRAHEDRON) return mfem::Geometry::TRIANGLE; @@ -17,7 +18,8 @@ constexpr mfem::Geometry::Type face_type(mfem::Geometry::Type geom) { return mfem::Geometry::INVALID; } -int possible_permutations(mfem::Geometry::Type geom) { +int possible_permutations(mfem::Geometry::Type geom) +{ if (geom == mfem::Geometry::TRIANGLE) return 3; if (geom == mfem::Geometry::SQUARE) return 4; if (geom == mfem::Geometry::TETRAHEDRON) return 12; @@ -25,8 +27,9 @@ int possible_permutations(mfem::Geometry::Type geom) { return -1; } -template < uint32_t n > -std::array< int, n > apply_permutation(const int (&arr)[n], const int (&p)[n]) { +template +std::array apply_permutation(const int (&arr)[n], const int (&p)[n]) +{ std::array permuted_arr{}; for (uint32_t i = 0; i < n; i++) { permuted_arr[i] = arr[p[i]]; @@ -34,14 +37,14 @@ std::array< int, n > apply_permutation(const int (&arr)[n], const int (&p)[n]) { return permuted_arr; } -mfem::Mesh generate_permuted_mesh(mfem::Geometry::Type geom, int i) { - +mfem::Mesh generate_permuted_mesh(mfem::Geometry::Type geom, int i) +{ if (geom == mfem::Geometry::TRIANGLE) { - constexpr int dim = 2; - constexpr int num_elements = 2; - constexpr int num_vertices = 4; - constexpr int num_permutations = 3; - int positive_permutations[num_permutations][3] = {{0, 1, 2}, {1, 2, 0}, {2, 0, 1}}; + constexpr int dim = 2; + constexpr int num_elements = 2; + constexpr int num_vertices = 4; + constexpr int num_permutations = 3; + int positive_permutations[num_permutations][3] = {{0, 1, 2}, {1, 2, 0}, {2, 0, 1}}; /* y @@ -53,14 +56,16 @@ mfem::Mesh generate_permuted_mesh(mfem::Geometry::Type geom, int i) { | '. | | '. | | '.| - 0----------1--> x - */ - int elements[num_elements][3] = {{0, 1, 3}, {1, 2, 3}}; + 0----------1--> x + */ + int elements[num_elements][3] = {{0, 1, 3}, {1, 2, 3}}; double vertices[num_vertices][dim] = {{0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}, {0.0, 1.0}}; mfem::Mesh output(dim, num_vertices, num_elements); - for (auto vertex : vertices) { output.AddVertex(vertex); } + for (auto vertex : vertices) { + output.AddVertex(vertex); + } // the first element is always fixed output.AddTri(elements[0]); @@ -75,12 +80,11 @@ mfem::Mesh generate_permuted_mesh(mfem::Geometry::Type geom, int i) { } if (geom == mfem::Geometry::SQUARE) { - constexpr int dim = 2; - constexpr int num_elements = 2; - constexpr int num_vertices = 6; - constexpr int num_permutations = 4; - int positive_permutations[num_permutations][4] = {{0, 1, 2, 3}, {1, 2, 3, 0}, {2, 3, 0, 1}, {3, 0, 1, 2}}; - + constexpr int dim = 2; + constexpr int num_elements = 2; + constexpr int num_vertices = 6; + constexpr int num_permutations = 4; + int positive_permutations[num_permutations][4] = {{0, 1, 2, 3}, {1, 2, 3, 0}, {2, 3, 0, 1}, {3, 0, 1, 2}}; /* y @@ -91,14 +95,16 @@ mfem::Mesh generate_permuted_mesh(mfem::Geometry::Type geom, int i) { | | | | | | | | | - 0----------1----------2--> x - */ - int elements[num_elements][4] = {{0, 1, 4, 3}, {1, 2, 5, 4}}; + 0----------1----------2--> x + */ + int elements[num_elements][4] = {{0, 1, 4, 3}, {1, 2, 5, 4}}; double vertices[num_vertices][dim] = {{0.0, 0.0}, {1.0, 0.0}, {2.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}, {2.0, 1.0}}; mfem::Mesh output(dim, num_vertices, num_elements); - for (auto vertex : vertices) { output.AddVertex(vertex); } + for (auto vertex : vertices) { + output.AddVertex(vertex); + } // the first element is always fixed output.AddQuad(elements[0]); @@ -113,39 +119,39 @@ mfem::Mesh generate_permuted_mesh(mfem::Geometry::Type geom, int i) { } if (geom == mfem::Geometry::TETRAHEDRON) { - constexpr int dim = 3; - constexpr int num_elements = 2; - constexpr int num_vertices = 5; - constexpr int num_permutations = 12; - int positive_permutations[num_permutations][4] = { - {0, 1, 2, 3}, {0, 2, 3, 1}, {0, 3, 1, 2}, {1, 0, 3, 2}, - {1, 2, 0, 3}, {1, 3, 2, 0}, {2, 0, 1, 3}, {2, 1, 3, 0}, - {2, 3, 0, 1}, {3, 0, 2, 1}, {3, 1, 0, 2}, {3, 2, 1, 0} - }; + constexpr int dim = 3; + constexpr int num_elements = 2; + constexpr int num_vertices = 5; + constexpr int num_permutations = 12; + int positive_permutations[num_permutations][4] = {{0, 1, 2, 3}, {0, 2, 3, 1}, {0, 3, 1, 2}, {1, 0, 3, 2}, + {1, 2, 0, 3}, {1, 3, 2, 0}, {2, 0, 1, 3}, {2, 1, 3, 0}, + {2, 3, 0, 1}, {3, 0, 2, 1}, {3, 1, 0, 2}, {3, 2, 1, 0}}; /* .4. - y .*'/ '*. + y .*'/ '*. \ .*' / '*. - 2--.../ '*. + 2--.../ '*. |\ / '---... '*. x - | \ / '''---...'*. .*' - | / :::>1 - z | / \ ...---'''.*' - '*. |/ ...---''' .*' - 3--'''\ .*' - '*. \ .*' - '*.\ .*' + | \ / '''---...'*. .*' + | / :::>1 + z | / \ ...---'''.*' + '*. |/ ...---''' .*' + 3--'''\ .*' + '*. \ .*' + '*.\ .*' '0' */ - int elements[num_elements][4] = {{0, 1, 2, 3}, {1, 2, 3, 4}}; + int elements[num_elements][4] = {{0, 1, 2, 3}, {1, 2, 3, 4}}; double vertices[num_vertices][dim] = {{0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, {1, 1, 1}}; mfem::Mesh output(dim, num_vertices, num_elements); - for (auto vertex : vertices) { output.AddVertex(vertex); } + for (auto vertex : vertices) { + output.AddVertex(vertex); + } // the first element is always fixed output.AddTet(elements[0]); @@ -160,60 +166,53 @@ mfem::Mesh generate_permuted_mesh(mfem::Geometry::Type geom, int i) { } if (geom == mfem::Geometry::CUBE) { - constexpr int dim = 3; - constexpr int num_elements = 2; - constexpr int num_vertices = 12; - constexpr int num_permutations = 24; - int positive_permutations[num_permutations][8] = { - {0, 1, 2, 3, 4, 5, 6, 7}, {0, 3, 7, 4, 1, 2, 6, 5}, {0, 4, 5, 1, 3, 7, 6, 2}, - {1, 0, 4, 5, 2, 3, 7, 6}, {1, 2, 3, 0, 5, 6, 7, 4}, {1, 5, 6, 2, 0, 4, 7, 3}, - {2, 1, 5, 6, 3, 0, 4, 7}, {2, 3, 0, 1, 6, 7, 4, 5}, {2, 6, 7, 3, 1, 5, 4, 0}, - {3, 0, 1, 2, 7, 4, 5, 6}, {3, 2, 6, 7, 0, 1, 5, 4}, {3, 7, 4, 0, 2, 6, 5, 1}, - {4, 0, 3, 7, 5, 1, 2, 6}, {4, 5, 1, 0, 7, 6, 2, 3}, {4, 7, 6, 5, 0, 3, 2, 1}, - {5, 1, 0, 4, 6, 2, 3, 7}, {5, 4, 7, 6, 1, 0, 3, 2}, {5, 6, 2, 1, 4, 7, 3, 0}, - {6, 2, 1, 5, 7, 3, 0, 4}, {6, 5, 4, 7, 2, 1, 0, 3}, {6, 7, 3, 2, 5, 4, 0, 1}, - {7, 3, 2, 6, 4, 0, 1, 5}, {7, 4, 0, 3, 6, 5, 1, 2}, {7, 6, 5, 4, 3, 2, 1, 0} - }; + constexpr int dim = 3; + constexpr int num_elements = 2; + constexpr int num_vertices = 12; + constexpr int num_permutations = 24; + int positive_permutations[num_permutations][8] = { + {0, 1, 2, 3, 4, 5, 6, 7}, {0, 3, 7, 4, 1, 2, 6, 5}, {0, 4, 5, 1, 3, 7, 6, 2}, {1, 0, 4, 5, 2, 3, 7, 6}, + {1, 2, 3, 0, 5, 6, 7, 4}, {1, 5, 6, 2, 0, 4, 7, 3}, {2, 1, 5, 6, 3, 0, 4, 7}, {2, 3, 0, 1, 6, 7, 4, 5}, + {2, 6, 7, 3, 1, 5, 4, 0}, {3, 0, 1, 2, 7, 4, 5, 6}, {3, 2, 6, 7, 0, 1, 5, 4}, {3, 7, 4, 0, 2, 6, 5, 1}, + {4, 0, 3, 7, 5, 1, 2, 6}, {4, 5, 1, 0, 7, 6, 2, 3}, {4, 7, 6, 5, 0, 3, 2, 1}, {5, 1, 0, 4, 6, 2, 3, 7}, + {5, 4, 7, 6, 1, 0, 3, 2}, {5, 6, 2, 1, 4, 7, 3, 0}, {6, 2, 1, 5, 7, 3, 0, 4}, {6, 5, 4, 7, 2, 1, 0, 3}, + {6, 7, 3, 2, 5, 4, 0, 1}, {7, 3, 2, 6, 4, 0, 1, 5}, {7, 4, 0, 3, 6, 5, 1, 2}, {7, 6, 5, 4, 3, 2, 1, 0}}; /* z ^ | - 8----------11 + 8----------11 |\ |\ | \ | \ | \ | \ - | 9------+---10 + | 9------+---10 + | | | | + 4---+------7 | + |\ | |\ | + | \ | | \ | + | \| | \| + | 5------+---6 | | | | - 4---+------7 | - |\ | |\ | - | \ | | \ | - | \| | \| - | 5------+---6 - | | | | - 0---+------3---|--> y - \ | \ | - \ | \ | - \| \| - 1----------2 + 0---+------3---|--> y + \ | \ | + \ | \ | + \| \| + 1----------2 \ v - x - */ - double vertices[num_vertices][dim] = { - {0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}, - {0, 0, 1}, {1, 0, 1}, {1, 1, 1}, {0, 1, 1}, - {0, 0, 2}, {1, 0, 2}, {1, 1, 2}, {0, 1, 2} - }; - - int elements[num_elements][8] = { - {0, 1, 2, 3, 4, 5, 6, 7}, - {4, 5, 6, 7, 8, 9, 10, 11} - }; + x + */ + double vertices[num_vertices][dim] = {{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}, {0, 0, 1}, {1, 0, 1}, + {1, 1, 1}, {0, 1, 1}, {0, 0, 2}, {1, 0, 2}, {1, 1, 2}, {0, 1, 2}}; + + int elements[num_elements][8] = {{0, 1, 2, 3, 4, 5, 6, 7}, {4, 5, 6, 7, 8, 9, 10, 11}}; mfem::Mesh output(dim, num_vertices, num_elements); - for (auto vertex : vertices) { output.AddVertex(vertex); } + for (auto vertex : vertices) { + output.AddVertex(vertex); + } // the first element is always fixed output.AddHex(elements[0]); @@ -228,10 +227,10 @@ mfem::Mesh generate_permuted_mesh(mfem::Geometry::Type geom, int i) { } return {}; - } -std::ostream & operator<<(std::ostream & out, axom::Array arr) { +std::ostream& operator<<(std::ostream& out, axom::Array arr) +{ for (int i = 0; i < arr.shape()[0]; i++) { for (int j = 0; j < arr.shape()[1]; j++) { out << arr[i][j].index() << " "; @@ -241,41 +240,44 @@ std::ostream & operator<<(std::ostream & out, axom::Array -double scalar_func_mfem(const mfem::Vector & x, double /*t*/) { - if constexpr (dim == 2) { - return x[0] + 10 * x[1]; - } else { - return x[0] + 10 * x[1] + 100 * x[2]; - } +template +double scalar_func_mfem(const mfem::Vector& x, double /*t*/) +{ + if constexpr (dim == 2) { + return x[0] + 10 * x[1]; + } else { + return x[0] + 10 * x[1] + 100 * x[2]; + } } -template < int dim > -double scalar_func(const tensor x) { - if constexpr (dim == 2) { - return x[0] + 10 * x[1]; - } else { - return x[0] + 10 * x[1] + 100 * x[2]; - } +template +double scalar_func(const tensor x) +{ + if constexpr (dim == 2) { + return x[0] + 10 * x[1]; + } else { + return x[0] + 10 * x[1] + 100 * x[2]; + } } -template < int dim > -void vector_func_mfem(const mfem::Vector & x, double /*t*/, mfem::Vector & output) { - if constexpr (dim == 2) { - output[0] = +x[1]; - output[1] = -x[0]; - } else { - output[0] = +x[1]; - output[1] = -x[0]+x[2]; - output[2] = -x[1]; - } +template +void vector_func_mfem(const mfem::Vector& x, double /*t*/, mfem::Vector& output) +{ + if constexpr (dim == 2) { + output[0] = +x[1]; + output[1] = -x[0]; + } else { + output[0] = +x[1]; + output[1] = -x[0] + x[2]; + output[2] = -x[1]; + } } -template < mfem::Geometry::Type geom, int polynomial_order > -void parametrized_test(int permutation) { - +template +void parametrized_test(int permutation) +{ constexpr mfem::Geometry::Type face_geom = face_type(geom); - constexpr int dim = dimension_of(geom); + constexpr int dim = dimension_of(geom); mfem::Mesh mesh = generate_permuted_mesh(geom, permutation); @@ -305,26 +307,26 @@ void parametrized_test(int permutation) { face_ids = interior_faces.mfem_quad_ids_; } - auto H1_fec = std::make_unique(polynomial_order, dim); + auto H1_fec = std::make_unique(polynomial_order, dim); auto Hcurl_fec = std::make_unique(polynomial_order, dim); - auto L2_fec = std::make_unique(polynomial_order, dim, mfem::BasisType::GaussLobatto); + auto L2_fec = std::make_unique(polynomial_order, dim, mfem::BasisType::GaussLobatto); - auto H1_fes = std::make_unique(&mesh, H1_fec.get()); + auto H1_fes = std::make_unique(&mesh, H1_fec.get()); auto Hcurl_fes = std::make_unique(&mesh, Hcurl_fec.get()); - auto L2_fes = std::make_unique(&mesh, L2_fec.get()); + auto L2_fes = std::make_unique(&mesh, L2_fec.get()); mfem::GridFunction H1_gf(H1_fes.get()); mfem::GridFunction Hcurl_gf(Hcurl_fes.get()); mfem::GridFunction L2_gf(L2_fes.get()); - mfem::FunctionCoefficient sfunc(scalar_func_mfem); + mfem::FunctionCoefficient sfunc(scalar_func_mfem); mfem::VectorFunctionCoefficient vfunc(dim, vector_func_mfem); H1_gf.ProjectCoefficient(sfunc); Hcurl_gf.ProjectCoefficient(vfunc); L2_gf.ProjectCoefficient(sfunc); - auto H1_dofs = GetFaceDofs(H1_fes.get(), face_geom, FaceType::INTERIOR); + auto H1_dofs = GetFaceDofs(H1_fes.get(), face_geom, FaceType::INTERIOR); auto Hcurl_dofs = GetFaceDofs(Hcurl_fes.get(), face_geom, FaceType::INTERIOR); #if 0 @@ -332,55 +334,52 @@ void parametrized_test(int permutation) { #else auto L2_dofs = GetFaceDofs(L2_fes.get(), face_geom, face_ids); #endif - + // verify that the dofs for the L2 faces are aligned properly int dofs_per_side = L2_dofs.shape()[1] / 2; for (int i = 0; i < dofs_per_side; i++) { int id1 = int(L2_dofs(0, i).index()); int id2 = int(L2_dofs(0, i + dofs_per_side).index()); - //std::cout << id1 << " " << id2 << " " << L2_gf[id1] << " " << L2_gf[id2] << std::endl; + // std::cout << id1 << " " << id2 << " " << L2_gf[id1] << " " << L2_gf[id2] << std::endl; EXPECT_NEAR(L2_gf[id1], L2_gf[id2], 5.0e-14); } -//////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// using space = L2; - auto pmesh = mesh::refineAndDistribute(std::move(mesh), 0); + auto pmesh = mesh::refineAndDistribute(std::move(mesh), 0); Domain pinterior_faces = InteriorFaces(*pmesh); auto L2_pfes = mfem::ParFiniteElementSpace(pmesh.get(), L2_fec.get()); - Functional< double(space) > r({&L2_pfes}); + Functional r({&L2_pfes}); r.AddInteriorFaceIntegral( - Dimension{}, - DependsOn<0>{}, - [](double /*t*/, auto X, auto rho){ - //std::cout << get<0>(X) << std::endl; - double expected = scalar_func(get<0>(X)); - auto [rho1, rho2] = rho; - EXPECT_NEAR(expected, get_value(rho1), 5.0e-14); - EXPECT_NEAR(expected, get_value(rho2), 5.0e-14); - auto difference = rho1 - rho2; - return difference * difference; - }, - pinterior_faces - ); - - mfem::Vector U(L2_pfes.TrueVSize()); + Dimension{}, DependsOn<0>{}, + [](double /*t*/, auto X, auto rho) { + // std::cout << get<0>(X) << std::endl; + double expected = scalar_func(get<0>(X)); + auto [rho1, rho2] = rho; + EXPECT_NEAR(expected, get_value(rho1), 5.0e-14); + EXPECT_NEAR(expected, get_value(rho2), 5.0e-14); + auto difference = rho1 - rho2; + return difference * difference; + }, + pinterior_faces); + + mfem::Vector U(L2_pfes.TrueVSize()); mfem::ParGridFunction U_pgf(&L2_pfes); U_pgf.ProjectCoefficient(sfunc); U_pgf.GetTrueDofs(U); - double t = 0.0; + double t = 0.0; double output = r(t, U); EXPECT_NEAR(output, 0.0, 5.0e-14); // TODO: check that the actual values match their respective functions // evaluated directly at the nodes (for H1, Hcurl, and L2 gfs) - } //////////////////////////////////////////////////////////////////////////////// @@ -419,57 +418,57 @@ TEST(DomainInterior, QuadMesh33) { parametrized_test( //////////////////////////////////////////////////////////////////////////////// -TEST(DomainInterior, TetMesh100) { parametrized_test( 0); } -TEST(DomainInterior, TetMesh101) { parametrized_test( 1); } -TEST(DomainInterior, TetMesh102) { parametrized_test( 2); } -TEST(DomainInterior, TetMesh103) { parametrized_test( 3); } -TEST(DomainInterior, TetMesh104) { parametrized_test( 4); } -TEST(DomainInterior, TetMesh105) { parametrized_test( 5); } -TEST(DomainInterior, TetMesh106) { parametrized_test( 6); } -TEST(DomainInterior, TetMesh107) { parametrized_test( 7); } -TEST(DomainInterior, TetMesh108) { parametrized_test( 8); } -TEST(DomainInterior, TetMesh109) { parametrized_test( 9); } +TEST(DomainInterior, TetMesh100) { parametrized_test(0); } +TEST(DomainInterior, TetMesh101) { parametrized_test(1); } +TEST(DomainInterior, TetMesh102) { parametrized_test(2); } +TEST(DomainInterior, TetMesh103) { parametrized_test(3); } +TEST(DomainInterior, TetMesh104) { parametrized_test(4); } +TEST(DomainInterior, TetMesh105) { parametrized_test(5); } +TEST(DomainInterior, TetMesh106) { parametrized_test(6); } +TEST(DomainInterior, TetMesh107) { parametrized_test(7); } +TEST(DomainInterior, TetMesh108) { parametrized_test(8); } +TEST(DomainInterior, TetMesh109) { parametrized_test(9); } TEST(DomainInterior, TetMesh110) { parametrized_test(10); } TEST(DomainInterior, TetMesh111) { parametrized_test(11); } -TEST(DomainInterior, TetMesh200) { parametrized_test( 0); } -TEST(DomainInterior, TetMesh201) { parametrized_test( 1); } -TEST(DomainInterior, TetMesh202) { parametrized_test( 2); } -TEST(DomainInterior, TetMesh203) { parametrized_test( 3); } -TEST(DomainInterior, TetMesh204) { parametrized_test( 4); } -TEST(DomainInterior, TetMesh205) { parametrized_test( 5); } -TEST(DomainInterior, TetMesh206) { parametrized_test( 6); } -TEST(DomainInterior, TetMesh207) { parametrized_test( 7); } -TEST(DomainInterior, TetMesh208) { parametrized_test( 8); } -TEST(DomainInterior, TetMesh209) { parametrized_test( 9); } +TEST(DomainInterior, TetMesh200) { parametrized_test(0); } +TEST(DomainInterior, TetMesh201) { parametrized_test(1); } +TEST(DomainInterior, TetMesh202) { parametrized_test(2); } +TEST(DomainInterior, TetMesh203) { parametrized_test(3); } +TEST(DomainInterior, TetMesh204) { parametrized_test(4); } +TEST(DomainInterior, TetMesh205) { parametrized_test(5); } +TEST(DomainInterior, TetMesh206) { parametrized_test(6); } +TEST(DomainInterior, TetMesh207) { parametrized_test(7); } +TEST(DomainInterior, TetMesh208) { parametrized_test(8); } +TEST(DomainInterior, TetMesh209) { parametrized_test(9); } TEST(DomainInterior, TetMesh210) { parametrized_test(10); } TEST(DomainInterior, TetMesh211) { parametrized_test(11); } -TEST(DomainInterior, TetMesh300) { parametrized_test( 0); } -TEST(DomainInterior, TetMesh301) { parametrized_test( 1); } -TEST(DomainInterior, TetMesh302) { parametrized_test( 2); } -TEST(DomainInterior, TetMesh303) { parametrized_test( 3); } -TEST(DomainInterior, TetMesh304) { parametrized_test( 4); } -TEST(DomainInterior, TetMesh305) { parametrized_test( 5); } -TEST(DomainInterior, TetMesh306) { parametrized_test( 6); } -TEST(DomainInterior, TetMesh307) { parametrized_test( 7); } -TEST(DomainInterior, TetMesh308) { parametrized_test( 8); } -TEST(DomainInterior, TetMesh309) { parametrized_test( 9); } +TEST(DomainInterior, TetMesh300) { parametrized_test(0); } +TEST(DomainInterior, TetMesh301) { parametrized_test(1); } +TEST(DomainInterior, TetMesh302) { parametrized_test(2); } +TEST(DomainInterior, TetMesh303) { parametrized_test(3); } +TEST(DomainInterior, TetMesh304) { parametrized_test(4); } +TEST(DomainInterior, TetMesh305) { parametrized_test(5); } +TEST(DomainInterior, TetMesh306) { parametrized_test(6); } +TEST(DomainInterior, TetMesh307) { parametrized_test(7); } +TEST(DomainInterior, TetMesh308) { parametrized_test(8); } +TEST(DomainInterior, TetMesh309) { parametrized_test(9); } TEST(DomainInterior, TetMesh310) { parametrized_test(10); } TEST(DomainInterior, TetMesh311) { parametrized_test(11); } //////////////////////////////////////////////////////////////////////////////// -TEST(DomainInterior, HexMesh100) { parametrized_test( 0); } -TEST(DomainInterior, HexMesh101) { parametrized_test( 1); } -TEST(DomainInterior, HexMesh102) { parametrized_test( 2); } -TEST(DomainInterior, HexMesh103) { parametrized_test( 3); } -TEST(DomainInterior, HexMesh104) { parametrized_test( 4); } -TEST(DomainInterior, HexMesh105) { parametrized_test( 5); } -TEST(DomainInterior, HexMesh106) { parametrized_test( 6); } -TEST(DomainInterior, HexMesh107) { parametrized_test( 7); } -TEST(DomainInterior, HexMesh108) { parametrized_test( 8); } -TEST(DomainInterior, HexMesh109) { parametrized_test( 9); } +TEST(DomainInterior, HexMesh100) { parametrized_test(0); } +TEST(DomainInterior, HexMesh101) { parametrized_test(1); } +TEST(DomainInterior, HexMesh102) { parametrized_test(2); } +TEST(DomainInterior, HexMesh103) { parametrized_test(3); } +TEST(DomainInterior, HexMesh104) { parametrized_test(4); } +TEST(DomainInterior, HexMesh105) { parametrized_test(5); } +TEST(DomainInterior, HexMesh106) { parametrized_test(6); } +TEST(DomainInterior, HexMesh107) { parametrized_test(7); } +TEST(DomainInterior, HexMesh108) { parametrized_test(8); } +TEST(DomainInterior, HexMesh109) { parametrized_test(9); } TEST(DomainInterior, HexMesh110) { parametrized_test(10); } TEST(DomainInterior, HexMesh111) { parametrized_test(11); } TEST(DomainInterior, HexMesh112) { parametrized_test(12); } @@ -485,16 +484,16 @@ TEST(DomainInterior, HexMesh121) { parametrized_test(21 TEST(DomainInterior, HexMesh122) { parametrized_test(22); } TEST(DomainInterior, HexMesh123) { parametrized_test(23); } -TEST(DomainInterior, HexMesh200) { parametrized_test( 0); } -TEST(DomainInterior, HexMesh201) { parametrized_test( 1); } -TEST(DomainInterior, HexMesh202) { parametrized_test( 2); } -TEST(DomainInterior, HexMesh203) { parametrized_test( 3); } -TEST(DomainInterior, HexMesh204) { parametrized_test( 4); } -TEST(DomainInterior, HexMesh205) { parametrized_test( 5); } -TEST(DomainInterior, HexMesh206) { parametrized_test( 6); } -TEST(DomainInterior, HexMesh207) { parametrized_test( 7); } -TEST(DomainInterior, HexMesh208) { parametrized_test( 8); } -TEST(DomainInterior, HexMesh209) { parametrized_test( 9); } +TEST(DomainInterior, HexMesh200) { parametrized_test(0); } +TEST(DomainInterior, HexMesh201) { parametrized_test(1); } +TEST(DomainInterior, HexMesh202) { parametrized_test(2); } +TEST(DomainInterior, HexMesh203) { parametrized_test(3); } +TEST(DomainInterior, HexMesh204) { parametrized_test(4); } +TEST(DomainInterior, HexMesh205) { parametrized_test(5); } +TEST(DomainInterior, HexMesh206) { parametrized_test(6); } +TEST(DomainInterior, HexMesh207) { parametrized_test(7); } +TEST(DomainInterior, HexMesh208) { parametrized_test(8); } +TEST(DomainInterior, HexMesh209) { parametrized_test(9); } TEST(DomainInterior, HexMesh210) { parametrized_test(10); } TEST(DomainInterior, HexMesh211) { parametrized_test(11); } TEST(DomainInterior, HexMesh212) { parametrized_test(12); } @@ -510,16 +509,16 @@ TEST(DomainInterior, HexMesh221) { parametrized_test(21 TEST(DomainInterior, HexMesh222) { parametrized_test(22); } TEST(DomainInterior, HexMesh223) { parametrized_test(23); } -TEST(DomainInterior, HexMesh300) { parametrized_test( 0); } -TEST(DomainInterior, HexMesh301) { parametrized_test( 1); } -TEST(DomainInterior, HexMesh302) { parametrized_test( 2); } -TEST(DomainInterior, HexMesh303) { parametrized_test( 3); } -TEST(DomainInterior, HexMesh304) { parametrized_test( 4); } -TEST(DomainInterior, HexMesh305) { parametrized_test( 5); } -TEST(DomainInterior, HexMesh306) { parametrized_test( 6); } -TEST(DomainInterior, HexMesh307) { parametrized_test( 7); } -TEST(DomainInterior, HexMesh308) { parametrized_test( 8); } -TEST(DomainInterior, HexMesh309) { parametrized_test( 9); } +TEST(DomainInterior, HexMesh300) { parametrized_test(0); } +TEST(DomainInterior, HexMesh301) { parametrized_test(1); } +TEST(DomainInterior, HexMesh302) { parametrized_test(2); } +TEST(DomainInterior, HexMesh303) { parametrized_test(3); } +TEST(DomainInterior, HexMesh304) { parametrized_test(4); } +TEST(DomainInterior, HexMesh305) { parametrized_test(5); } +TEST(DomainInterior, HexMesh306) { parametrized_test(6); } +TEST(DomainInterior, HexMesh307) { parametrized_test(7); } +TEST(DomainInterior, HexMesh308) { parametrized_test(8); } +TEST(DomainInterior, HexMesh309) { parametrized_test(9); } TEST(DomainInterior, HexMesh310) { parametrized_test(10); } TEST(DomainInterior, HexMesh311) { parametrized_test(11); } TEST(DomainInterior, HexMesh312) { parametrized_test(12); } diff --git a/src/serac/numerics/functional/tests/domain_tests.cpp b/src/serac/numerics/functional/tests/domain_tests.cpp index c841f2258f..9514e576d0 100644 --- a/src/serac/numerics/functional/tests/domain_tests.cpp +++ b/src/serac/numerics/functional/tests/domain_tests.cpp @@ -21,7 +21,6 @@ mfem::Mesh import_mesh(std::string meshfile) return mesh; } - TEST(domain, of_vertices) { { diff --git a/src/serac/numerics/functional/tests/element_restriction_tests.cpp b/src/serac/numerics/functional/tests/element_restriction_tests.cpp index 952e1a7521..ec543cdd20 100644 --- a/src/serac/numerics/functional/tests/element_restriction_tests.cpp +++ b/src/serac/numerics/functional/tests/element_restriction_tests.cpp @@ -5,7 +5,8 @@ using namespace serac; -std::ostream & operator<<(std::ostream & out, axom::Array arr) { +std::ostream& operator<<(std::ostream& out, axom::Array arr) +{ for (int i = 0; i < arr.shape()[0]; i++) { for (int j = 0; j < arr.shape()[1]; j++) { out << arr[i][j].index() << " "; @@ -15,11 +16,11 @@ std::ostream & operator<<(std::ostream & out, axom::Array(p, dim); auto H1_fes = std::make_unique(&mesh, H1_fec.get()); @@ -83,22 +84,21 @@ TEST(patch_test_meshes, triangle_domains) { BlockElementRestriction L2_BER(L2_fes.get(), interior); EXPECT_EQ(L2_BER.ESize(), 4 * (3 * 2)); } - } -TEST(patch_test_meshes, quadrilateral_domains) { +TEST(patch_test_meshes, quadrilateral_domains) +{ + int p = 2; + int dim = 2; + mfem::Mesh mesh(SERAC_REPO_DIR "/data/meshes/patch2D_quads.mesh"); - int p = 2; - int dim = 2; - mfem::Mesh mesh(SERAC_REPO_DIR"/data/meshes/patch2D_quads.mesh"); - - auto H1_fec = std::make_unique(p, dim); + auto H1_fec = std::make_unique(p, dim); auto Hcurl_fec = std::make_unique(p, dim); - auto L2_fec = std::make_unique(p, dim, mfem::BasisType::GaussLobatto); + auto L2_fec = std::make_unique(p, dim, mfem::BasisType::GaussLobatto); - auto H1_fes = std::make_unique(&mesh, H1_fec.get()); + auto H1_fes = std::make_unique(&mesh, H1_fec.get()); auto Hcurl_fes = std::make_unique(&mesh, Hcurl_fec.get()); - auto L2_fes = std::make_unique(&mesh, L2_fec.get()); + auto L2_fes = std::make_unique(&mesh, L2_fec.get()); Domain whole = EntireDomain(mesh); EXPECT_EQ(whole.mfem_edge_ids_.size(), 0); @@ -153,22 +153,21 @@ TEST(patch_test_meshes, quadrilateral_domains) { BlockElementRestriction L2_BER(L2_fes.get(), interior); EXPECT_EQ(L2_BER.ESize(), 8 * (3 * 2)); } - } -TEST(patch_test_meshes, triangle_and_quadrilateral_domains) { - - int p = 2; - int dim = 2; - mfem::Mesh mesh(SERAC_REPO_DIR"/data/meshes/patch2D_tris_and_quads.mesh"); +TEST(patch_test_meshes, triangle_and_quadrilateral_domains) +{ + int p = 2; + int dim = 2; + mfem::Mesh mesh(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); - auto H1_fec = std::make_unique(p, dim); + auto H1_fec = std::make_unique(p, dim); auto Hcurl_fec = std::make_unique(p, dim); - auto L2_fec = std::make_unique(p, dim, mfem::BasisType::GaussLobatto); + auto L2_fec = std::make_unique(p, dim, mfem::BasisType::GaussLobatto); - auto H1_fes = std::make_unique(&mesh, H1_fec.get()); + auto H1_fes = std::make_unique(&mesh, H1_fec.get()); auto Hcurl_fes = std::make_unique(&mesh, Hcurl_fec.get()); - auto L2_fes = std::make_unique(&mesh, L2_fec.get()); + auto L2_fes = std::make_unique(&mesh, L2_fec.get()); Domain whole = EntireDomain(mesh); EXPECT_EQ(whole.mfem_edge_ids_.size(), 0); @@ -223,22 +222,21 @@ TEST(patch_test_meshes, triangle_and_quadrilateral_domains) { BlockElementRestriction L2_BER(L2_fes.get(), interior); EXPECT_EQ(L2_BER.ESize(), 9 * (3 * 2)); } - } -TEST(patch_test_meshes, tetrahedron_domains) { - - int p = 2; - int dim = 3; - mfem::Mesh mesh(SERAC_REPO_DIR"/data/meshes/patch3D_tets.mesh"); +TEST(patch_test_meshes, tetrahedron_domains) +{ + int p = 2; + int dim = 3; + mfem::Mesh mesh(SERAC_REPO_DIR "/data/meshes/patch3D_tets.mesh"); - auto H1_fec = std::make_unique(p, dim); + auto H1_fec = std::make_unique(p, dim); auto Hcurl_fec = std::make_unique(p, dim); - auto L2_fec = std::make_unique(p, dim, mfem::BasisType::GaussLobatto); + auto L2_fec = std::make_unique(p, dim, mfem::BasisType::GaussLobatto); - auto H1_fes = std::make_unique(&mesh, H1_fec.get()); + auto H1_fes = std::make_unique(&mesh, H1_fec.get()); auto Hcurl_fes = std::make_unique(&mesh, Hcurl_fec.get()); - auto L2_fes = std::make_unique(&mesh, L2_fec.get()); + auto L2_fes = std::make_unique(&mesh, L2_fec.get()); Domain whole = EntireDomain(mesh); EXPECT_EQ(whole.mfem_edge_ids_.size(), 0); @@ -293,22 +291,21 @@ TEST(patch_test_meshes, tetrahedron_domains) { BlockElementRestriction L2_BER(L2_fes.get(), interior); EXPECT_EQ(L2_BER.ESize(), 18 * (6 * 2)); } - } -TEST(patch_test_meshes, hexahedron_domains) { +TEST(patch_test_meshes, hexahedron_domains) +{ + int p = 2; + int dim = 3; + mfem::Mesh mesh(SERAC_REPO_DIR "/data/meshes/patch3D_hexes.mesh"); - int p = 2; - int dim = 3; - mfem::Mesh mesh(SERAC_REPO_DIR"/data/meshes/patch3D_hexes.mesh"); - - auto H1_fec = std::make_unique(p, dim); + auto H1_fec = std::make_unique(p, dim); auto Hcurl_fec = std::make_unique(p, dim); - auto L2_fec = std::make_unique(p, dim, mfem::BasisType::GaussLobatto); + auto L2_fec = std::make_unique(p, dim, mfem::BasisType::GaussLobatto); - auto H1_fes = std::make_unique(&mesh, H1_fec.get()); + auto H1_fes = std::make_unique(&mesh, H1_fec.get()); auto Hcurl_fes = std::make_unique(&mesh, Hcurl_fec.get()); - auto L2_fes = std::make_unique(&mesh, L2_fec.get()); + auto L2_fes = std::make_unique(&mesh, L2_fec.get()); Domain whole = EntireDomain(mesh); EXPECT_EQ(whole.mfem_edge_ids_.size(), 0); @@ -363,5 +360,4 @@ TEST(patch_test_meshes, hexahedron_domains) { BlockElementRestriction L2_BER(L2_fes.get(), interior); EXPECT_EQ(L2_BER.ESize(), 18 * (9 * 2)); } - } diff --git a/src/serac/numerics/functional/tests/functional_basic_dg.cpp b/src/serac/numerics/functional/tests/functional_basic_dg.cpp index 632bb7528b..4bd174580d 100644 --- a/src/serac/numerics/functional/tests/functional_basic_dg.cpp +++ b/src/serac/numerics/functional/tests/functional_basic_dg.cpp @@ -40,7 +40,7 @@ using namespace serac::profiling; // // [ --- L --- | -- FND -- ] -// ^^ +// ^^ #if 0 void ParNonlinearForm::Mult(const Vector &x, Vector &y) const @@ -100,12 +100,12 @@ void L2_test(std::string meshfile) using test_space = L2; using trial_space = L2; - //int k = 0; - //while (k == 0); + // int k = 0; + // while (k == 0); auto mesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 0); - auto fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); + auto fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); mfem::ParFiniteElementSpace fespace(mesh.get(), &fec, dim, serac::ordering); mfem::Vector U(fespace.TrueVSize()); @@ -119,29 +119,29 @@ void L2_test(std::string meshfile) Domain interior_faces = InteriorFaces(*mesh); residual.AddInteriorFaceIntegral( - Dimension{}, DependsOn<0>{}, + Dimension{}, DependsOn<0>{}, [=](double /*t*/, auto X, auto velocity) { // compute the surface normal auto dX_dxi = get(X); - auto n = normalize(cross(dX_dxi)); + auto n = normalize(cross(dX_dxi)); // extract the velocity values from each side of the interface - // note: the orientation convention is such that the normal + // note: the orientation convention is such that the normal // computed as above will point from from side 1->2 - auto [u_1, u_2] = velocity; + auto [u_1, u_2] = velocity; auto a = dot(u_2 - u_1, n); auto f_1 = u_1 * a; auto f_2 = u_2 * a; return serac::tuple{f_1, f_2}; - }, interior_faces); + }, + interior_faces); double t = 0.0; auto value = residual(t, U); - //check_gradient(residual, t, U); - + // check_gradient(residual, t, U); } TEST(basic, L2_test_tris_and_quads_linear) { L2_test<2, 1>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); } diff --git a/src/serac/numerics/functional/tests/functional_nonlinear.cpp b/src/serac/numerics/functional/tests/functional_nonlinear.cpp index edab8c7c67..b411056c4b 100644 --- a/src/serac/numerics/functional/tests/functional_nonlinear.cpp +++ b/src/serac/numerics/functional/tests/functional_nonlinear.cpp @@ -142,8 +142,8 @@ void functional_test(mfem::ParMesh& mesh, H1 test, H1 trial, Dim [=](double /*t*/, auto position, auto displacement) { auto [X, dX_dxi] = position; std::cout << X << " " << dX_dxi << std::endl; - auto u = get<0>(displacement); - auto n = normalize(cross(dX_dxi)); + auto u = get<0>(displacement); + auto n = normalize(cross(dX_dxi)); return (X[0] + X[1] - cos(u[0])) * n; }, bdr); diff --git a/src/serac/numerics/functional/tests/functional_qoi.cpp b/src/serac/numerics/functional/tests/functional_qoi.cpp index f6e3b1fefd..17ad0d5c09 100644 --- a/src/serac/numerics/functional/tests/functional_qoi.cpp +++ b/src/serac/numerics/functional/tests/functional_qoi.cpp @@ -211,8 +211,8 @@ void qoi_test(mfem::ParMesh& mesh, H1

trial, Dimension, WhichTest which) mfem::HypreParVector V = *tmp2; V_gf.GetTrueDofs(V); - Domain domain = EntireDomain(mesh); - Domain boundary = EntireBoundary(mesh); + Domain domain = EntireDomain(mesh); + Domain boundary = EntireBoundary(mesh); switch (which) { case WhichTest::Measure: { @@ -318,8 +318,8 @@ void qoi_test(mfem::ParMesh& mesh, H1 trial1, H1 trial2, Dimension) mfem::HypreParVector U2 = *tmp; U2_gf.GetTrueDofs(U2); - Domain domain = EntireDomain(mesh); - Domain boundary = EntireBoundary(mesh); + Domain domain = EntireDomain(mesh); + Domain boundary = EntireBoundary(mesh); Functional f({fespace1.get(), fespace2.get()}); f.AddDomainIntegral(Dimension{}, DependsOn<0, 1>{}, FourArgSineIntegrator{}, domain); @@ -457,7 +457,7 @@ TEST(QoI, UsingL2) // this tests a fix for the QoI constructor segfaulting when using L2 spaces Functional f({fespace_0.get(), fespace_1.get()}); - Domain whole_mesh = EntireDomain(mesh); + Domain whole_mesh = EntireDomain(mesh); Domain whole_boundary = EntireBoundary(mesh); f.AddVolumeIntegral(DependsOn<1>{}, TrivialVariadicIntegrator{}, whole_mesh); diff --git a/src/serac/numerics/functional/tests/functional_with_domain.cpp b/src/serac/numerics/functional/tests/functional_with_domain.cpp index 8e77b07b83..d05079a1be 100644 --- a/src/serac/numerics/functional/tests/functional_with_domain.cpp +++ b/src/serac/numerics/functional/tests/functional_with_domain.cpp @@ -215,7 +215,6 @@ void partial_mesh_comparison_test_impl(std::unique_ptr& mesh) Domain d = EntireDomain(*mesh); residual_comparison.AddDomainIntegral(Dimension{}, DependsOn<0>{}, TestThermalIntegratorOne{}, d); - Domain bdr = EntireBoundary(*mesh); residual_comparison.AddBoundaryIntegral(Dimension{}, DependsOn<0>{}, TestThermalIntegratorTwo{}, bdr); diff --git a/src/serac/numerics/functional/typedefs.hpp b/src/serac/numerics/functional/typedefs.hpp index 036b478bb1..c2cbe10c08 100644 --- a/src/serac/numerics/functional/typedefs.hpp +++ b/src/serac/numerics/functional/typedefs.hpp @@ -2,10 +2,10 @@ namespace serac { -// sam: this is a kludge-- it looks like the DG spaces need to use some interface defined ONLY on -// mfem::ParMesh / mfeme::ParFiniteElementSpace, but I'm not ready to pull the trigger on a big +// sam: this is a kludge-- it looks like the DG spaces need to use some interface defined ONLY on +// mfem::ParMesh / mfeme::ParFiniteElementSpace, but I'm not ready to pull the trigger on a big // interface change like that, so these typedefs mark the parts that would need to eventually change using mesh_t = mfem::Mesh; -using fes_t = mfem::FiniteElementSpace; +using fes_t = mfem::FiniteElementSpace; -} +} // namespace serac diff --git a/src/serac/physics/fit.hpp b/src/serac/physics/fit.hpp index 960411dcbd..88815b43c6 100644 --- a/src/serac/physics/fit.hpp +++ b/src/serac/physics/fit.hpp @@ -17,7 +17,7 @@ namespace detail { /// @overload template -FiniteElementState fit(std::integer_sequence, func f, mfem::ParMesh & pmesh, const T&... solution_fields) +FiniteElementState fit(std::integer_sequence, func f, mfem::ParMesh& pmesh, const T&... solution_fields) { // signature looks like return_type(arg0_type, arg1_type); // so this unpacks the return type @@ -27,7 +27,7 @@ FiniteElementState fit(std::integer_sequence, func f, mfem::ParMesh & fitted_field = 0.0; // mass term - Domain whole_domain = EntireDomain(pmesh); + Domain whole_domain = EntireDomain(pmesh); serac::Functional phi_phi(&fitted_field.space(), {&fitted_field.space()}); phi_phi.AddDomainIntegral( Dimension{}, DependsOn<0>{}, @@ -64,7 +64,7 @@ FiniteElementState fit(std::integer_sequence, func f, mfem::ParMesh & * @note: mesh is passed by non-const ref because mfem mutates the mesh when creating ParGridFunctions */ template -FiniteElementState fit(func f, mfem::ParMesh & pmesh, const T&... solution_fields) +FiniteElementState fit(func f, mfem::ParMesh& pmesh, const T&... solution_fields) { auto iseq = std::make_integer_sequence{}; return detail::fit(iseq, f, pmesh, solution_fields...); diff --git a/src/serac/physics/heat_transfer.hpp b/src/serac/physics/heat_transfer.hpp index 30fe301efc..a4c1adcb8c 100644 --- a/src/serac/physics/heat_transfer.hpp +++ b/src/serac/physics/heat_transfer.hpp @@ -430,7 +430,7 @@ class HeatTransfer, std::integer_sequ * @note This method must be called prior to completeSetup() */ template - void setMaterial(DependsOn, const MaterialType& material, Domain & domain) + void setMaterial(DependsOn, const MaterialType& material, Domain& domain) { residual_->AddDomainIntegral(Dimension{}, DependsOn<0, 1, NUM_STATE_VARS + active_parameters...>{}, ThermalMaterialIntegrand(material), domain); @@ -438,7 +438,7 @@ class HeatTransfer, std::integer_sequ /// @overload template - void setMaterial(const MaterialType& material, Domain & domain) + void setMaterial(const MaterialType& material, Domain& domain) { setMaterial(DependsOn<>{}, material, domain); } @@ -467,7 +467,7 @@ class HeatTransfer, std::integer_sequ * * @tparam SourceType The type of the source function * @param source_function A source function for a prescribed thermal load - * @param domain The domain over which the source is applied. + * @param domain The domain over which the source is applied. * * @pre source_function must be a object that can be called with the following arguments: * 1. `tensor x` the spatial coordinates for the quadrature point @@ -485,8 +485,7 @@ class HeatTransfer, std::integer_sequ * @note This method must be called prior to completeSetup() */ template - void setSource(DependsOn, SourceType source_function, - Domain & domain) + void setSource(DependsOn, SourceType source_function, Domain& domain) { residual_->AddDomainIntegral( Dimension{}, DependsOn<0, 1, active_parameters + NUM_STATE_VARS...>{}, @@ -504,7 +503,7 @@ class HeatTransfer, std::integer_sequ /// @overload template - void setSource(SourceType source_function, Domain & domain) + void setSource(SourceType source_function, Domain& domain) { setSource(DependsOn<>{}, source_function, domain); } @@ -532,8 +531,7 @@ class HeatTransfer, std::integer_sequ * @note This method must be called prior to completeSetup() */ template - void setFluxBCs(DependsOn, FluxType flux_function, - Domain & domain) + void setFluxBCs(DependsOn, FluxType flux_function, Domain& domain) { residual_->AddBoundaryIntegral( Dimension{}, DependsOn<0, 1, active_parameters + NUM_STATE_VARS...>{}, @@ -548,7 +546,7 @@ class HeatTransfer, std::integer_sequ /// @overload template - void setFluxBCs(FluxType flux_function, Domain & domain) + void setFluxBCs(FluxType flux_function, Domain& domain) { setFluxBCs(DependsOn<>{}, flux_function, domain); } @@ -665,8 +663,7 @@ class HeatTransfer, std::integer_sequ * @note This method must be called prior to completeSetup() */ template - void addCustomBoundaryIntegral(DependsOn, callable qfunction, - Domain & domain) + void addCustomBoundaryIntegral(DependsOn, callable qfunction, Domain& domain) { residual_->AddBoundaryIntegral(Dimension{}, DependsOn<0, 1, active_parameters + NUM_STATE_VARS...>{}, qfunction, domain); diff --git a/src/serac/physics/solid_mechanics.hpp b/src/serac/physics/solid_mechanics.hpp index 34d020fc45..b6b9b7c7af 100644 --- a/src/serac/physics/solid_mechanics.hpp +++ b/src/serac/physics/solid_mechanics.hpp @@ -840,7 +840,7 @@ class SolidMechanics, std::integer_se * @note This method must be called prior to completeSetup() */ template - void addCustomDomainIntegral(DependsOn, callable qfunction, Domain & domain, + void addCustomDomainIntegral(DependsOn, callable qfunction, Domain& domain, qdata_type qdata = NoQData) { residual_->AddDomainIntegral(Dimension{}, DependsOn<0, 1, active_parameters + NUM_STATE_VARS...>{}, qfunction, @@ -1005,7 +1005,7 @@ class SolidMechanics, std::integer_se * @tparam BodyForceType The type of the body force load * @param body_force A function describing the body force applied * @param domain which part of the mesh to apply the body force to - * + * * @pre body_force must be a object that can be called with the following arguments: * 1. `tensor x` the spatial coordinates for the quadrature point * 2. `double t` the time (note: time will be handled differently in the future) @@ -1019,8 +1019,7 @@ class SolidMechanics, std::integer_se * @note This method must be called prior to completeSetup() */ template - void addBodyForce(DependsOn, BodyForceType body_force, - Domain& domain) + void addBodyForce(DependsOn, BodyForceType body_force, Domain& domain) { residual_->AddDomainIntegral(Dimension{}, DependsOn<0, 1, active_parameters + NUM_STATE_VARS...>{}, BodyForceIntegrand(body_force), domain); @@ -1028,7 +1027,7 @@ class SolidMechanics, std::integer_se /// @overload template - void addBodyForce(BodyForceType body_force, Domain & domain) + void addBodyForce(BodyForceType body_force, Domain& domain) { addBodyForce(DependsOn<>{}, body_force, domain); } @@ -1038,8 +1037,8 @@ class SolidMechanics, std::integer_se * * @tparam TractionType The type of the traction load * @param traction_function A function describing the traction applied to a boundary - * @param domain The domain over which the traction is applied. - * + * @param domain The domain over which the traction is applied. + * * @pre TractionType must be a object that can be called with the following arguments: * 1. `tensor x` the spatial coordinates for the quadrature point * 2. `tensor n` the outward-facing unit normal for the quadrature point @@ -1057,8 +1056,7 @@ class SolidMechanics, std::integer_se * @note This method must be called prior to completeSetup() */ template - void setTraction(DependsOn, TractionType traction_function, - Domain & domain) + void setTraction(DependsOn, TractionType traction_function, Domain& domain) { residual_->AddBoundaryIntegral( Dimension{}, DependsOn<0, 1, active_parameters + NUM_STATE_VARS...>{}, @@ -1072,7 +1070,7 @@ class SolidMechanics, std::integer_se /// @overload template - void setTraction(TractionType traction_function, Domain & domain) + void setTraction(TractionType traction_function, Domain& domain) { setTraction(DependsOn<>{}, traction_function, domain); } @@ -1082,8 +1080,8 @@ class SolidMechanics, std::integer_se * * @tparam PressureType The type of the pressure load * @param pressure_function A function describing the pressure applied to a boundary - * @param domain The domain over which the pressure is applied. - * + * @param domain The domain over which the pressure is applied. + * * @pre PressureType must be a object that can be called with the following arguments: * 1. `tensor x` the reference configuration spatial coordinates for the quadrature point * 2. `double t` the time (note: time will be handled differently in the future) @@ -1101,7 +1099,7 @@ class SolidMechanics, std::integer_se * @note This method must be called prior to completeSetup() */ template - void setPressure(DependsOn, PressureType pressure_function, Domain &domain) + void setPressure(DependsOn, PressureType pressure_function, Domain& domain) { residual_->AddBoundaryIntegral( Dimension{}, DependsOn<0, 1, active_parameters + NUM_STATE_VARS...>{}, @@ -1130,7 +1128,7 @@ class SolidMechanics, std::integer_se /// @overload template - void setPressure(PressureType pressure_function, Domain & domain) + void setPressure(PressureType pressure_function, Domain& domain) { setPressure(DependsOn<>{}, pressure_function, domain); } diff --git a/src/serac/physics/state/finite_element_vector.hpp b/src/serac/physics/state/finite_element_vector.hpp index 6de60c5151..399b34ce5d 100644 --- a/src/serac/physics/state/finite_element_vector.hpp +++ b/src/serac/physics/state/finite_element_vector.hpp @@ -65,7 +65,8 @@ class FiniteElementVector : public mfem::HypreParVector { * @param name The name of the new finite element state field */ template - FiniteElementVector(mfem::ParMesh& mesh, [[maybe_unused]] FunctionSpace f, const std::string& name = "") : mesh_(mesh), name_(name) + FiniteElementVector(mfem::ParMesh& mesh, [[maybe_unused]] FunctionSpace f, const std::string& name = "") + : mesh_(mesh), name_(name) { std::tie(space_, coll_) = serac::generateParFiniteElementSpace(&mesh); diff --git a/src/serac/physics/tests/beam_bending.cpp b/src/serac/physics/tests/beam_bending.cpp index 78c366ec70..459fc7debe 100644 --- a/src/serac/physics/tests/beam_bending.cpp +++ b/src/serac/physics/tests/beam_bending.cpp @@ -40,7 +40,7 @@ TEST(BeamBending, TwoDimensional) std::string mesh_tag{"mesh"}; - auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); serac::LinearSolverOptions linear_options{.linear_solver = LinearSolver::GMRES, .preconditioner = Preconditioner::HypreAMG, @@ -69,7 +69,7 @@ TEST(BeamBending, TwoDimensional) double K = 1.91666666666667; double G = 1.0; solid_mechanics::StVenantKirchhoff mat{1.0, K, G}; - Domain material_block = EntireDomain(pmesh); + Domain material_block = EntireDomain(pmesh); solid_solver.setMaterial(mat, material_block); // Define the function for the initial displacement and boundary condition @@ -80,10 +80,8 @@ TEST(BeamBending, TwoDimensional) solid_solver.setDisplacementBCs(ess_bdr, bc); solid_solver.setDisplacement(bc); - - Domain top_face = Domain::ofBoundaryElements(pmesh, [](std::vector vertices, int/*attr*/) { - return (average(vertices)[1] > 0.99); - }); + Domain top_face = Domain::ofBoundaryElements( + pmesh, [](std::vector vertices, int /*attr*/) { return (average(vertices)[1] > 0.99); }); solid_solver.setTraction([](auto /*x*/, auto n, auto /*t*/) { return -0.01 * n; }, top_face); diff --git a/src/serac/physics/tests/contact_beam.cpp b/src/serac/physics/tests/contact_beam.cpp index de0143735b..daf0718840 100644 --- a/src/serac/physics/tests/contact_beam.cpp +++ b/src/serac/physics/tests/contact_beam.cpp @@ -39,7 +39,7 @@ TEST_P(ContactTest, beam) // Construct the appropriate dimension mesh and give it to the data store std::string filename = SERAC_REPO_DIR "/data/meshes/beam-hex-with-contact-block.mesh"; - auto mesh = mesh::refineAndDistribute(buildMeshFromFile(filename), 1, 0); + auto mesh = mesh::refineAndDistribute(buildMeshFromFile(filename), 1, 0); auto& pmesh = serac::StateManager::setMesh(std::move(mesh), "beam_mesh"); LinearSolverOptions linear_options{.linear_solver = LinearSolver::Strumpack, .print_level = 1}; @@ -71,7 +71,7 @@ TEST_P(ContactTest, beam) double K = 10.0; double G = 0.25; solid_mechanics::NeoHookean mat{1.0, K, G}; - Domain material_block = EntireDomain(pmesh); + Domain material_block = EntireDomain(pmesh); solid_solver.setMaterial(mat, material_block); // Pass the BC information to the solver object diff --git a/src/serac/physics/tests/contact_patch.cpp b/src/serac/physics/tests/contact_patch.cpp index a5f7dd4020..6cbdcfba0c 100644 --- a/src/serac/physics/tests/contact_patch.cpp +++ b/src/serac/physics/tests/contact_patch.cpp @@ -41,7 +41,7 @@ TEST_P(ContactTest, patch) // Construct the appropriate dimension mesh and give it to the data store std::string filename = SERAC_REPO_DIR "/data/meshes/twohex_for_contact.mesh"; - auto mesh = mesh::refineAndDistribute(buildMeshFromFile(filename), 2, 0); + auto mesh = mesh::refineAndDistribute(buildMeshFromFile(filename), 2, 0); auto& pmesh = serac::StateManager::setMesh(std::move(mesh), "patch_mesh"); #ifdef SERAC_USE_PETSC @@ -78,7 +78,7 @@ TEST_P(ContactTest, patch) double K = 10.0; double G = 0.25; solid_mechanics::NeoHookean mat{1.0, K, G}; - Domain material_block = EntireDomain(pmesh); + Domain material_block = EntireDomain(pmesh); solid_solver.setMaterial(mat, material_block); // Define the function for the initial displacement and boundary condition diff --git a/src/serac/physics/tests/contact_patch_tied.cpp b/src/serac/physics/tests/contact_patch_tied.cpp index 65dee67d1b..0eb767cf89 100644 --- a/src/serac/physics/tests/contact_patch_tied.cpp +++ b/src/serac/physics/tests/contact_patch_tied.cpp @@ -41,7 +41,7 @@ TEST_P(ContactPatchTied, patch) // Construct the appropriate dimension mesh and give it to the data store std::string filename = SERAC_REPO_DIR "/data/meshes/twohex_for_contact.mesh"; - auto mesh = mesh::refineAndDistribute(buildMeshFromFile(filename), 3, 0); + auto mesh = mesh::refineAndDistribute(buildMeshFromFile(filename), 3, 0); auto& pmesh = serac::StateManager::setMesh(std::move(mesh), "patch_mesh"); // TODO: investigate performance with Petsc @@ -79,7 +79,7 @@ TEST_P(ContactPatchTied, patch) double K = 10.0; double G = 0.25; solid_mechanics::NeoHookean mat{1.0, K, G}; - Domain material_block = EntireDomain(pmesh); + Domain material_block = EntireDomain(pmesh); solid_solver.setMaterial(mat, material_block); // Define the function for the initial displacement and boundary condition diff --git a/src/serac/physics/tests/dynamic_solid_adjoint.cpp b/src/serac/physics/tests/dynamic_solid_adjoint.cpp index 07c9803cc6..685cabeeb3 100644 --- a/src/serac/physics/tests/dynamic_solid_adjoint.cpp +++ b/src/serac/physics/tests/dynamic_solid_adjoint.cpp @@ -87,7 +87,8 @@ void applyInitialAndBoundaryConditions(SolidMechanics& solid_solver) } std::unique_ptr> createNonlinearSolidMechanicsSolver( - const NonlinearSolverOptions& nonlinear_opts, const TimesteppingOptions& dyn_opts, const SolidMaterial& mat, Domain & whole_domain) + const NonlinearSolverOptions& nonlinear_opts, const TimesteppingOptions& dyn_opts, const SolidMaterial& mat, + Domain& whole_domain) { static int iter = 0; const LinearSolverOptions linear_options = {.linear_solver = LinearSolver::CG, @@ -105,12 +106,14 @@ std::unique_ptr> createNonlinearSolidMechanicsSolver( solid->setDisplacementBCs( {1}, [](const mfem::Vector&, double t, mfem::Vector& disp) { disp = (1.0 + 10 * t) * boundary_disp; }); - solid->addBodyForce([](auto X, auto t) { - auto Y = X; - Y[0] = 0.1 + 0.1 * X[0] + 0.3 * X[1] - 0.2 * t; - Y[1] = -0.05 - 0.08 * X[0] + 0.15 * X[1] + 0.3 * t; - return 0.4 * X + Y; - }, whole_domain); + solid->addBodyForce( + [](auto X, auto t) { + auto Y = X; + Y[0] = 0.1 + 0.1 * X[0] + 0.3 * X[1] - 0.2 * t; + Y[1] = -0.05 - 0.08 * X[0] + 0.15 * X[1] + 0.3 * t; + return 0.4 * X + Y; + }, + whole_domain); solid->completeSetup(); applyInitialAndBoundaryConditions(*solid); @@ -274,7 +277,7 @@ struct SolidMechanicsSensitivityFixture : public ::testing::Test { TEST_F(SolidMechanicsSensitivityFixture, InitialDisplacementSensitivities) { Domain whole_domain = EntireDomain(*mesh); - auto solid_solver = createNonlinearSolidMechanicsSolver(nonlinear_opts, dyn_opts, mat, whole_domain); + auto solid_solver = createNonlinearSolidMechanicsSolver(nonlinear_opts, dyn_opts, mat, whole_domain); auto [qoi_base, init_disp_sensitivity, _, __] = computeSolidMechanicsQoiSensitivities(*solid_solver, tsInfo); solid_solver->resetStates(); @@ -292,7 +295,7 @@ TEST_F(SolidMechanicsSensitivityFixture, InitialDisplacementSensitivities) TEST_F(SolidMechanicsSensitivityFixture, InitialVelocitySensitivities) { Domain whole_domain = EntireDomain(*mesh); - auto solid_solver = createNonlinearSolidMechanicsSolver(nonlinear_opts, dyn_opts, mat, whole_domain); + auto solid_solver = createNonlinearSolidMechanicsSolver(nonlinear_opts, dyn_opts, mat, whole_domain); auto [qoi_base, _, init_velo_sensitivity, __] = computeSolidMechanicsQoiSensitivities(*solid_solver, tsInfo); solid_solver->resetStates(); @@ -309,7 +312,7 @@ TEST_F(SolidMechanicsSensitivityFixture, InitialVelocitySensitivities) TEST_F(SolidMechanicsSensitivityFixture, ShapeSensitivities) { Domain whole_domain = EntireDomain(*mesh); - auto solid_solver = createNonlinearSolidMechanicsSolver(nonlinear_opts, dyn_opts, mat, whole_domain); + auto solid_solver = createNonlinearSolidMechanicsSolver(nonlinear_opts, dyn_opts, mat, whole_domain); auto [qoi_base, _, __, shape_sensitivity] = computeSolidMechanicsQoiSensitivities(*solid_solver, tsInfo); solid_solver->resetStates(); @@ -325,9 +328,9 @@ TEST_F(SolidMechanicsSensitivityFixture, ShapeSensitivities) TEST_F(SolidMechanicsSensitivityFixture, QuasiStaticShapeSensitivities) { - Domain whole_domain = EntireDomain(*mesh); - dyn_opts.timestepper = TimestepMethod::QuasiStatic; - auto solid_solver = createNonlinearSolidMechanicsSolver(nonlinear_opts, dyn_opts, mat, whole_domain); + Domain whole_domain = EntireDomain(*mesh); + dyn_opts.timestepper = TimestepMethod::QuasiStatic; + auto solid_solver = createNonlinearSolidMechanicsSolver(nonlinear_opts, dyn_opts, mat, whole_domain); auto [qoi_base, _, __, shape_sensitivity] = computeSolidMechanicsQoiSensitivities(*solid_solver, tsInfo); solid_solver->resetStates(); @@ -344,7 +347,7 @@ TEST_F(SolidMechanicsSensitivityFixture, QuasiStaticShapeSensitivities) TEST_F(SolidMechanicsSensitivityFixture, WhenShapeSensitivitiesCalledTwice_GetSameObjectiveAndGradient) { Domain whole_domain = EntireDomain(*mesh); - auto solid_solver = createNonlinearSolidMechanicsSolver(nonlinear_opts, dyn_opts, mat, whole_domain); + auto solid_solver = createNonlinearSolidMechanicsSolver(nonlinear_opts, dyn_opts, mat, whole_domain); auto [qoi1, _, __, shape_sensitivity1] = computeSolidMechanicsQoiSensitivities(*solid_solver, tsInfo); solid_solver->resetStates(); diff --git a/src/serac/physics/tests/dynamic_thermal_adjoint.cpp b/src/serac/physics/tests/dynamic_thermal_adjoint.cpp index e23c85591f..463c787b97 100644 --- a/src/serac/physics/tests/dynamic_thermal_adjoint.cpp +++ b/src/serac/physics/tests/dynamic_thermal_adjoint.cpp @@ -52,8 +52,7 @@ static int iter = 0; std::unique_ptr> createNonlinearHeatTransfer( axom::sidre::DataStore& /*data_store*/, const NonlinearSolverOptions& nonlinear_opts, const TimesteppingOptions& dyn_opts, - const heat_transfer::IsotropicConductorWithLinearConductivityVsTemperature& mat, - Domain & whole_domain) + const heat_transfer::IsotropicConductorWithLinearConductivityVsTemperature& mat, Domain& whole_domain) { // Note that we are testing the non-default checkpoint to disk capability here auto thermal = std::make_unique>(nonlinear_opts, heat_transfer::direct_linear_options, dyn_opts, @@ -72,7 +71,8 @@ using ParametrizedHeatTransferT = HeatTransfer>, std::i std::unique_ptr createParameterizedHeatTransfer( axom::sidre::DataStore& /*data_store*/, const NonlinearSolverOptions& nonlinear_opts, - const TimesteppingOptions& dyn_opts, const heat_transfer::ParameterizedLinearIsotropicConductor& mat, Domain & whole_domain) + const TimesteppingOptions& dyn_opts, const heat_transfer::ParameterizedLinearIsotropicConductor& mat, + Domain& whole_domain) { std::vector names{"conductivity"}; @@ -84,8 +84,7 @@ std::unique_ptr createParameterizedHeatTransfer( user_defined_conductivity = 1.1; thermal->setParameter(0, user_defined_conductivity); thermal->setMaterial(DependsOn<0>{}, mat, whole_domain); - thermal->setSource([](auto /* X */, auto /* time */, auto /* u */, auto /* du_dx */) { return 1.0; }, - whole_domain); + thermal->setSource([](auto /* X */, auto /* time */, auto /* u */, auto /* du_dx */) { return 1.0; }, whole_domain); thermal->setTemperature([](const mfem::Vector&, double) { return 0.0; }); thermal->setTemperatureBCs({1}, [](const mfem::Vector&, double) { return 0.0; }); thermal->completeSetup(); @@ -95,8 +94,7 @@ std::unique_ptr createParameterizedHeatTransfer( std::unique_ptr createParameterizedNonlinearHeatTransfer( axom::sidre::DataStore& /*data_store*/, const NonlinearSolverOptions& nonlinear_opts, const TimesteppingOptions& dyn_opts, - const heat_transfer::ParameterizedIsotropicConductorWithLinearConductivityVsTemperature& mat, - Domain & whole_domain) + const heat_transfer::ParameterizedIsotropicConductorWithLinearConductivityVsTemperature& mat, Domain& whole_domain) { std::vector names{"conductivity"}; @@ -266,8 +264,8 @@ struct HeatTransferSensitivityFixture : public ::testing::Test { TEST_F(HeatTransferSensitivityFixture, InitialTemperatureSensitivities) { - Domain whole_domain = EntireDomain(*mesh); - auto thermal_solver = createNonlinearHeatTransfer(data_store, nonlinear_opts, dyn_opts, nonlinearMat, whole_domain); + Domain whole_domain = EntireDomain(*mesh); + auto thermal_solver = createNonlinearHeatTransfer(data_store, nonlinear_opts, dyn_opts, nonlinearMat, whole_domain); auto [qoi_base, temperature_sensitivity, _] = computeThermalQoiAndInitialTemperatureAndShapeSensitivity(*thermal_solver, tsInfo); @@ -285,8 +283,8 @@ TEST_F(HeatTransferSensitivityFixture, InitialTemperatureSensitivities) TEST_F(HeatTransferSensitivityFixture, ShapeSensitivities) { - Domain whole_domain = EntireDomain(*mesh); - auto thermal_solver = createNonlinearHeatTransfer(data_store, nonlinear_opts, dyn_opts, nonlinearMat, whole_domain); + Domain whole_domain = EntireDomain(*mesh); + auto thermal_solver = createNonlinearHeatTransfer(data_store, nonlinear_opts, dyn_opts, nonlinearMat, whole_domain); auto [qoi_base, _, shape_sensitivity] = computeThermalQoiAndInitialTemperatureAndShapeSensitivity(*thermal_solver, tsInfo); @@ -304,7 +302,8 @@ TEST_F(HeatTransferSensitivityFixture, ShapeSensitivities) TEST_F(HeatTransferSensitivityFixture, ConductivityParameterSensitivities) { Domain whole_domain = EntireDomain(*mesh); - auto thermal_solver = createParameterizedHeatTransfer(data_store, nonlinear_opts, dyn_opts, parameterizedMat, whole_domain); + auto thermal_solver = + createParameterizedHeatTransfer(data_store, nonlinear_opts, dyn_opts, parameterizedMat, whole_domain); auto [qoi_base, conductivity_sensitivity] = computeThermalConductivitySensitivity(*thermal_solver, tsInfo); thermal_solver->resetStates(); @@ -319,9 +318,9 @@ TEST_F(HeatTransferSensitivityFixture, ConductivityParameterSensitivities) TEST_F(HeatTransferSensitivityFixture, NonlinearConductivityParameterSensitivities) { - Domain whole_domain = EntireDomain(*mesh); - auto thermal_solver = - createParameterizedNonlinearHeatTransfer(data_store, nonlinear_opts, dyn_opts, parameterizedNonlinearMat, whole_domain); + Domain whole_domain = EntireDomain(*mesh); + auto thermal_solver = createParameterizedNonlinearHeatTransfer(data_store, nonlinear_opts, dyn_opts, + parameterizedNonlinearMat, whole_domain); auto [qoi_base, conductivity_sensitivity] = computeThermalConductivitySensitivity(*thermal_solver, tsInfo); thermal_solver->resetStates(); diff --git a/src/serac/physics/tests/lce_Bertoldi_lattice.cpp b/src/serac/physics/tests/lce_Bertoldi_lattice.cpp index 0a6d812bee..1edf2c056b 100644 --- a/src/serac/physics/tests/lce_Bertoldi_lattice.cpp +++ b/src/serac/physics/tests/lce_Bertoldi_lattice.cpp @@ -56,7 +56,7 @@ TEST(LiquidCrystalElastomer, Bertoldi) std::string mesh_tag{"mesh"}; - auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // Construct a functional-based solid mechanics solver LinearSolverOptions linear_options = {.linear_solver = LinearSolver::SuperLU}; diff --git a/src/serac/physics/tests/lce_Brighenti_tensile.cpp b/src/serac/physics/tests/lce_Brighenti_tensile.cpp index 7aebf74ecb..07dbcac8f6 100644 --- a/src/serac/physics/tests/lce_Brighenti_tensile.cpp +++ b/src/serac/physics/tests/lce_Brighenti_tensile.cpp @@ -117,8 +117,8 @@ TEST(LiquidCrystalElastomer, Brighenti) transition_temperature, Nb2); LiquidCrystElastomerBrighenti::State initial_state{}; - auto qdata = solid_solver.createQuadratureDataBuffer(initial_state); - Domain whole_mesh = EntireDomain(pmesh); + auto qdata = solid_solver.createQuadratureDataBuffer(initial_state); + Domain whole_mesh = EntireDomain(pmesh); solid_solver.setMaterial(DependsOn{}, mat, whole_mesh, qdata); // prescribe symmetry conditions @@ -134,16 +134,14 @@ TEST(LiquidCrystalElastomer, Brighenti) double maxLoadVal = 4 * 1.3e0 / lx / lz; double loadVal = iniLoadVal + 0.0 * maxLoadVal; - Domain front_face = Domain::ofBoundaryElements(pmesh, [ly](std::vector< vec3 > vertices, int /* attr */){ - return average(vertices)[1] > 0.99 * ly; - }); + Domain front_face = Domain::ofBoundaryElements( + pmesh, [ly](std::vector vertices, int /* attr */) { return average(vertices)[1] > 0.99 * ly; }); solid_solver.setTraction( - [&loadVal](auto /*x*/, auto /*n*/, auto /*t*/) { - return tensor{0, loadVal, 0}; - }, - front_face - ); + [&loadVal](auto /*x*/, auto /*n*/, auto /*t*/) { + return tensor{0, loadVal, 0}; + }, + front_face); solid_solver.setDisplacement(ini_displacement); @@ -164,15 +162,11 @@ TEST(LiquidCrystalElastomer, Brighenti) auto n = normalize(cross(dX_dxi)); return dot(u, n); }, - front_face - ); + front_face); Functional)> area({&solid_solver.displacement().space()}); area.AddSurfaceIntegral( - DependsOn<>{}, - [=](double /*t*/, auto /*position*/) { return 1.0; }, - front_face - ); + DependsOn<>{}, [=](double /*t*/, auto /*position*/) { return 1.0; }, front_face); double t = 0.0; double initial_area = area(t, solid_solver.displacement()); diff --git a/src/serac/physics/tests/parameterized_thermal.cpp b/src/serac/physics/tests/parameterized_thermal.cpp index 3049826a7b..42f769d74e 100644 --- a/src/serac/physics/tests/parameterized_thermal.cpp +++ b/src/serac/physics/tests/parameterized_thermal.cpp @@ -66,7 +66,7 @@ TEST(Thermal, ParameterizedMaterial) thermal_solver.setParameter(0, user_defined_conductivity); - Domain whole_domain = EntireDomain(pmesh); + Domain whole_domain = EntireDomain(pmesh); Domain whole_boundary = EntireBoundary(pmesh); // Construct a potentially user-defined parameterized material and send it to the thermal module diff --git a/src/serac/physics/tests/parameterized_thermomechanics_example.cpp b/src/serac/physics/tests/parameterized_thermomechanics_example.cpp index 085a6bb82a..def0989f4a 100644 --- a/src/serac/physics/tests/parameterized_thermomechanics_example.cpp +++ b/src/serac/physics/tests/parameterized_thermomechanics_example.cpp @@ -108,7 +108,7 @@ TEST(Thermomechanics, ParameterizedMaterial) double theta_ref = 0.0; ///< datum temperature for thermal expansion ParameterizedThermoelasticMaterial material{density, E, nu, theta_ref}; - Domain material_block = EntireDomain(pmesh); + Domain material_block = EntireDomain(pmesh); simulation.setMaterial(DependsOn<0, 1>{}, material, material_block); double deltaT = 1.0; @@ -166,8 +166,7 @@ TEST(Thermomechanics, ParameterizedMaterial) auto n = normalize(cross(dX_dxi)); return dot(u, n); }, - top_surface - ); + top_surface); double initial_qoi = qoi(time, simulation.displacement()); SLIC_INFO_ROOT(axom::fmt::format("vertical displacement integrated over the top surface: {}", initial_qoi)); @@ -175,10 +174,7 @@ TEST(Thermomechanics, ParameterizedMaterial) Functional)> area({&simulation.displacement().space()}); area.AddSurfaceIntegral( - DependsOn<>{}, - [=](double /*t*/, auto /*position*/) { return 1.0; }, - top_surface - ); + DependsOn<>{}, [=](double /*t*/, auto /*position*/) { return 1.0; }, top_surface); double top_area = area(time, simulation.displacement()); diff --git a/src/serac/physics/tests/solid.cpp b/src/serac/physics/tests/solid.cpp index 754cca35c9..9962471b18 100644 --- a/src/serac/physics/tests/solid.cpp +++ b/src/serac/physics/tests/solid.cpp @@ -45,7 +45,7 @@ void functional_solid_test_static_J2() std::string mesh_tag{"mesh"}; - auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // _solver_params_start serac::LinearSolverOptions linear_options{.linear_solver = LinearSolver::SuperLU}; @@ -136,7 +136,7 @@ void functional_solid_spatial_essential_bc() std::string mesh_tag{"mesh"}; - auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // Construct a functional-based solid mechanics solver SolidMechanics solid_solver(solid_mechanics::default_nonlinear_options, @@ -144,7 +144,7 @@ void functional_solid_spatial_essential_bc() solid_mechanics::default_quasistatic_options, "solid_mechanics", mesh_tag); solid_mechanics::LinearIsotropic mat{1.0, 1.0, 1.0}; - Domain whole_domain = EntireDomain(pmesh); + Domain whole_domain = EntireDomain(pmesh); solid_solver.setMaterial(mat, whole_domain); // Set up @@ -303,7 +303,7 @@ void functional_parameterized_solid_test(double expected_disp_norm) solid_solver.setParameter(0, user_defined_bulk_modulus); solid_solver.setParameter(1, user_defined_shear_modulus); - Domain whole_domain = EntireDomain(pmesh); + Domain whole_domain = EntireDomain(pmesh); Domain whole_boundary = EntireBoundary(pmesh); solid_mechanics::ParameterizedLinearIsotropicSolid mat{1.0, 0.0, 0.0}; @@ -340,9 +340,12 @@ void functional_parameterized_solid_test(double expected_disp_norm) // add some nonexistent body forces / tractions to check that // these parameterized versions compile and run without error - solid_solver.addBodyForce(DependsOn<0>{}, [](const auto& x, double /*t*/, auto /* bulk */) { return x * 0.0; }, whole_domain); - solid_solver.addBodyForce(DependsOn<1>{}, ParameterizedBodyForce{[](const auto& x) { return 0.0 * x; }}, whole_domain); - solid_solver.setTraction(DependsOn<1>{}, [](const auto& x, auto...) { return 0 * x; }, whole_boundary); + solid_solver.addBodyForce( + DependsOn<0>{}, [](const auto& x, double /*t*/, auto /* bulk */) { return x * 0.0; }, whole_domain); + solid_solver.addBodyForce(DependsOn<1>{}, ParameterizedBodyForce{[](const auto& x) { return 0.0 * x; }}, + whole_domain); + solid_solver.setTraction( + DependsOn<1>{}, [](const auto& x, auto...) { return 0 * x; }, whole_boundary); // Finalize the data structures solid_solver.completeSetup(); diff --git a/src/serac/physics/tests/solid_dynamics_patch.cpp b/src/serac/physics/tests/solid_dynamics_patch.cpp index 39d98d6a11..e50d92c21c 100644 --- a/src/serac/physics/tests/solid_dynamics_patch.cpp +++ b/src/serac/physics/tests/solid_dynamics_patch.cpp @@ -131,7 +131,8 @@ class AffineSolution { * @param essential_boundaries Boundary attributes on which essential boundary conditions are desired */ template - void applyLoads(const Material& material, SolidMechanics& solid, std::set essential_boundaries, Domain & bdr_domain) const + void applyLoads(const Material& material, SolidMechanics& solid, std::set essential_boundaries, + Domain& bdr_domain) const { // essential BCs auto ebc_func = [*this](const auto& X, double t, auto& u) { this->operator()(X, t, u); }; @@ -224,7 +225,8 @@ class ConstantAccelerationSolution { * @param essential_boundaries Boundary attributes on which essential boundary conditions are desired */ template - void applyLoads(const Material& material, SolidMechanics& solid, std::set essential_boundaries, Domain & domain) const + void applyLoads(const Material& material, SolidMechanics& solid, std::set essential_boundaries, + Domain& domain) const { // essential BCs auto ebc_func = [*this](const auto& X, double t, auto& u) { this->operator()(X, t, u); }; @@ -310,8 +312,8 @@ double solution_error(solution_type exact_solution, PatchBoundaryCondition bc) "solid_dynamics", mesh_tag); solid_mechanics::NeoHookean mat{.density = 1.0, .K = 1.0, .G = 1.0}; - Domain whole_domain = EntireDomain(pmesh); - Domain whole_boundary = EntireBoundary(pmesh); + Domain whole_domain = EntireDomain(pmesh); + Domain whole_boundary = EntireBoundary(pmesh); solid.setMaterial(mat, whole_domain); // initial conditions @@ -319,14 +321,12 @@ double solution_error(solution_type exact_solution, PatchBoundaryCondition bc) solid.setDisplacement([exact_solution](const mfem::Vector& x, mfem::Vector& u) { exact_solution(x, 0.0, u); }); - - // forcing terms - if constexpr (std::is_same< solution_type, ConstantAccelerationSolution >::value) { + if constexpr (std::is_same >::value) { exact_solution.applyLoads(mat, solid, essentialBoundaryAttributes(bc), whole_domain); } - if constexpr (std::is_same< solution_type, AffineSolution >::value) { + if constexpr (std::is_same >::value) { exact_solution.applyLoads(mat, solid, essentialBoundaryAttributes(bc), whole_boundary); } diff --git a/src/serac/physics/tests/solid_finite_diff.cpp b/src/serac/physics/tests/solid_finite_diff.cpp index ceae8872f2..5a6ea940c5 100644 --- a/src/serac/physics/tests/solid_finite_diff.cpp +++ b/src/serac/physics/tests/solid_finite_diff.cpp @@ -73,7 +73,7 @@ TEST(SolidMechanics, FiniteDifferenceParameter) constexpr int bulk_parameter_index = 0; solid_mechanics::ParameterizedNeoHookeanSolid mat{1.0, 0.0, 0.0}; - Domain whole_mesh = EntireDomain(pmesh); + Domain whole_mesh = EntireDomain(pmesh); solid_solver.setMaterial(DependsOn<0, 1>{}, mat, whole_mesh); // Define the function for the initial displacement and boundary condition @@ -222,7 +222,7 @@ void finite_difference_shape_test(LoadingType load) solid_mechanics::default_quasistatic_options, "solid_functional", mesh_tag); solid_mechanics::NeoHookean mat{1.0, 1.0, 1.0}; - Domain whole_mesh = EntireDomain(pmesh); + Domain whole_mesh = EntireDomain(pmesh); solid_solver.setMaterial(mat, whole_mesh); FiniteElementState shape_displacement(pmesh, H1{}); @@ -237,8 +237,8 @@ void finite_difference_shape_test(LoadingType load) solid_solver.setDisplacementBCs(ess_bdr, bc); solid_solver.setDisplacement(bc); - Domain top_face = Domain::ofBoundaryElements(pmesh, [](std::vector< vec2 > vertices, int /*attr*/){ - return average(vertices)[1] > 0.99; // select faces by y-coordinate + Domain top_face = Domain::ofBoundaryElements(pmesh, [](std::vector vertices, int /*attr*/) { + return average(vertices)[1] > 0.99; // select faces by y-coordinate }); if (load == LoadingType::BodyForce) { @@ -248,17 +248,9 @@ void finite_difference_shape_test(LoadingType load) solid_mechanics::ConstantBodyForce force{constant_force}; solid_solver.addBodyForce(force, whole_mesh); } else if (load == LoadingType::Pressure) { - solid_solver.setPressure( - [](auto /*X*/, double /*t*/) { - return 0.1; - }, - top_face); + solid_solver.setPressure([](auto /*X*/, double /*t*/) { return 0.1; }, top_face); } else if (load == LoadingType::Traction) { - solid_solver.setTraction( - [](auto /*X*/, auto /*n*/, double /*t*/) { - return vec2{0.01, 0.01}; - }, - top_face); + solid_solver.setTraction([](auto /*X*/, auto /*n*/, double /*t*/) { return vec2{0.01, 0.01}; }, top_face); } // Finalize the data structures diff --git a/src/serac/physics/tests/solid_reaction_adjoint.cpp b/src/serac/physics/tests/solid_reaction_adjoint.cpp index 75edbc6335..bdb1877363 100644 --- a/src/serac/physics/tests/solid_reaction_adjoint.cpp +++ b/src/serac/physics/tests/solid_reaction_adjoint.cpp @@ -37,7 +37,7 @@ constexpr double boundary_disp = 0.013; constexpr double shear_modulus_value = 1.0; constexpr double bulk_modulus_value = 1.0; -std::unique_ptr createNonlinearSolidMechanicsSolver(mfem::ParMesh& pmesh, +std::unique_ptr createNonlinearSolidMechanicsSolver(mfem::ParMesh& pmesh, const NonlinearSolverOptions& nonlinear_opts, const SolidMaterial& mat) { @@ -61,12 +61,14 @@ std::unique_ptr createNonlinearSolidMechanicsSolver(mfem::Pa solid->setMaterial(DependsOn<0, 1>{}, mat, whole_mesh); - solid->addBodyForce([](auto X, auto /* t */) { - auto Y = X; - Y[0] = 0.1 + 0.1 * X[0] + 0.3 * X[1]; - Y[1] = -0.05 - 0.2 * X[0] + 0.15 * X[1]; - return 0.1 * X + Y; - }, whole_mesh); + solid->addBodyForce( + [](auto X, auto /* t */) { + auto Y = X; + Y[0] = 0.1 + 0.1 * X[0] + 0.3 * X[1]; + Y[1] = -0.05 - 0.2 * X[0] + 0.15 * X[1]; + return 0.1 * X + Y; + }, + whole_mesh); solid->setDisplacementBCs({1}, [](const mfem::Vector&, mfem::Vector& disp) { disp = boundary_disp; }); diff --git a/src/serac/physics/tests/solid_robin_condition.cpp b/src/serac/physics/tests/solid_robin_condition.cpp index cb97c7a874..a5a9a12135 100644 --- a/src/serac/physics/tests/solid_robin_condition.cpp +++ b/src/serac/physics/tests/solid_robin_condition.cpp @@ -41,8 +41,8 @@ void functional_solid_test_robin_condition() std::string mesh_tag{"mesh"}; - auto mesh = mesh::refineAndDistribute(buildMeshFromFile(filename), serial_refinement, parallel_refinement); - auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto mesh = mesh::refineAndDistribute(buildMeshFromFile(filename), serial_refinement, parallel_refinement); + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // _solver_params_start serac::NonlinearSolverOptions nonlinear_options{.nonlin_solver = NonlinearSolver::Newton, diff --git a/src/serac/physics/tests/thermal_dynamics_patch.cpp b/src/serac/physics/tests/thermal_dynamics_patch.cpp index e496d46787..e6bada373d 100644 --- a/src/serac/physics/tests/thermal_dynamics_patch.cpp +++ b/src/serac/physics/tests/thermal_dynamics_patch.cpp @@ -102,7 +102,7 @@ double dynamic_solution_error(const ExactSolution& exact_solution, PatchBoundary std::string mesh_tag{"mesh"}; std::string filename = std::string(SERAC_REPO_DIR) + "/data/meshes/patch" + std::to_string(dim) + "D.mesh"; auto mesh = mesh::refineAndDistribute(buildMeshFromFile(filename)); - auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // Construct a heat transfer solver NonlinearSolverOptions nonlinear_opts{.relative_tol = 5.0e-13, .absolute_tol = 5.0e-13}; @@ -112,7 +112,7 @@ double dynamic_solution_error(const ExactSolution& exact_solution, PatchBoundary HeatTransfer thermal(nonlinear_opts, heat_transfer::direct_linear_options, dyn_opts, "thermal", mesh_tag); - Domain whole_domain = EntireDomain(pmesh); + Domain whole_domain = EntireDomain(pmesh); Domain whole_boundary = EntireBoundary(pmesh); heat_transfer::LinearIsotropicConductor mat(1.0, 1.0, 1.0); @@ -191,7 +191,8 @@ class LinearSolution { * @param essential_boundaries Boundary attributes on which essential boundary conditions are desired */ template - void applyLoads(const Material& material, HeatTransfer& thermal, Domain & dom, Domain & bdr, std::set essential_boundaries) const + void applyLoads(const Material& material, HeatTransfer& thermal, Domain& dom, Domain& bdr, + std::set essential_boundaries) const { // essential BCs auto ebc_func = [*this](const auto& X, double t) { return this->operator()(X, t); }; diff --git a/src/serac/physics/tests/thermal_finite_diff.cpp b/src/serac/physics/tests/thermal_finite_diff.cpp index 028a636232..e967f70749 100644 --- a/src/serac/physics/tests/thermal_finite_diff.cpp +++ b/src/serac/physics/tests/thermal_finite_diff.cpp @@ -65,7 +65,7 @@ TEST(Thermal, FiniteDifference) thermal_solver.setParameter(0, user_defined_conductivity); - Domain whole_domain = EntireDomain(pmesh); + Domain whole_domain = EntireDomain(pmesh); Domain whole_boundary = EntireBoundary(pmesh); // Construct a potentially user-defined parameterized material and send it to the thermal module diff --git a/src/serac/physics/tests/thermal_mechanics.cpp b/src/serac/physics/tests/thermal_mechanics.cpp index f2024ec3a2..27540ae45c 100644 --- a/src/serac/physics/tests/thermal_mechanics.cpp +++ b/src/serac/physics/tests/thermal_mechanics.cpp @@ -40,7 +40,7 @@ void functional_test_static_3D(double expected_norm) std::string mesh_tag{"mesh"}; - auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // Define a boundary attribute set std::set ess_bdr = {1}; @@ -124,7 +124,7 @@ void functional_test_shrinking_3D(double expected_norm) std::string mesh_tag{"mesh"}; - auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // Define a boundary attribute set std::set constraint_bdr = {1}; diff --git a/src/serac/physics/tests/thermal_nonlinear_solve.cpp b/src/serac/physics/tests/thermal_nonlinear_solve.cpp index 89f7321f6a..6f2742d7f3 100644 --- a/src/serac/physics/tests/thermal_nonlinear_solve.cpp +++ b/src/serac/physics/tests/thermal_nonlinear_solve.cpp @@ -40,8 +40,8 @@ void functional_thermal_test_nonlinear() std::string mesh_tag{"mesh"}; - auto mesh = mesh::refineAndDistribute(buildMeshFromFile(filename), serial_refinement, parallel_refinement); - auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto mesh = mesh::refineAndDistribute(buildMeshFromFile(filename), serial_refinement, parallel_refinement); + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // _solver_params_start serac::NonlinearSolverOptions nonlinear_options{.nonlin_solver = NonlinearSolver::NewtonLineSearch, @@ -60,7 +60,7 @@ void functional_thermal_test_nonlinear() 21.0 // isotropic thermal conductivity }; - Domain whole_domain = EntireDomain(pmesh); + Domain whole_domain = EntireDomain(pmesh); Domain whole_boundary = EntireBoundary(pmesh); thermal_solver.setMaterial(mat, whole_domain); diff --git a/src/serac/physics/tests/thermal_robin_condition.cpp b/src/serac/physics/tests/thermal_robin_condition.cpp index 27a757d3e9..6f07ee586e 100644 --- a/src/serac/physics/tests/thermal_robin_condition.cpp +++ b/src/serac/physics/tests/thermal_robin_condition.cpp @@ -40,8 +40,8 @@ void functional_thermal_test_robin_condition() std::string mesh_tag{"mesh"}; - auto mesh = mesh::refineAndDistribute(buildMeshFromFile(filename), serial_refinement, parallel_refinement); - auto & pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); + auto mesh = mesh::refineAndDistribute(buildMeshFromFile(filename), serial_refinement, parallel_refinement); + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), mesh_tag); // _solver_params_start serac::NonlinearSolverOptions nonlinear_options{.nonlin_solver = NonlinearSolver::Newton, @@ -60,7 +60,7 @@ void functional_thermal_test_robin_condition() 1.0 // isotropic thermal conductivity }; - Domain whole_domain = EntireDomain(pmesh); + Domain whole_domain = EntireDomain(pmesh); Domain whole_boundary = EntireBoundary(pmesh); thermal_solver.setMaterial(mat, whole_domain); diff --git a/src/serac/physics/thermomechanics.hpp b/src/serac/physics/thermomechanics.hpp index 752c8be41c..ed1833c25c 100644 --- a/src/serac/physics/thermomechanics.hpp +++ b/src/serac/physics/thermomechanics.hpp @@ -352,7 +352,7 @@ class Thermomechanics : public BasePhysics { * @tparam MaterialType The thermomechanical material type * @tparam StateType The type that contains the internal variables for MaterialType * @param material A material that provides a function to evaluate stress, heat flux, density, and heat capacity - * @param domain The domain over which the source is applied. + * @param domain The domain over which the source is applied. * @param qdata the buffer of material internal variables at each quadrature point * * @pre material must be a object that can be called with the following arguments: @@ -367,26 +367,29 @@ class Thermomechanics : public BasePhysics { * when doing direct evaluation. When differentiating with respect to one of the inputs, its stored * values will change to `dual` numbers rather than `double`. (e.g. `tensor` becomes * `tensor, 3>`) - * + * * @param domain which elements in the mesh are described by the specified material * * @pre MaterialType must return a serac::tuple of Cauchy stress, volumetric heat capacity, internal heat source, * and thermal flux when operator() is called with the arguments listed above. */ template - void setMaterial(DependsOn, const MaterialType& material, Domain & domain, + void setMaterial(DependsOn, const MaterialType& material, Domain& domain, std::shared_ptr> qdata) { // note: these parameter indices are offset by 1 since, internally, this module uses the first parameter // to communicate the temperature and displacement field information to the other physics module // - thermal_.setMaterial(DependsOn<0, active_parameters + 1 ...>{}, ThermalMaterialInterface{material}, domain); - solid_.setMaterial(DependsOn<0, active_parameters + 1 ...>{}, MechanicalMaterialInterface{material}, domain, qdata); + thermal_.setMaterial(DependsOn<0, active_parameters + 1 ...>{}, ThermalMaterialInterface{material}, + domain); + solid_.setMaterial(DependsOn<0, active_parameters + 1 ...>{}, MechanicalMaterialInterface{material}, + domain, qdata); } /// @overload template - void setMaterial(const MaterialType& material, Domain& domain, std::shared_ptr> qdata = EmptyQData) + void setMaterial(const MaterialType& material, Domain& domain, + std::shared_ptr> qdata = EmptyQData) { setMaterial(DependsOn<>{}, material, domain, qdata); } From d4d79ebc968d80719bae651dda76cdd220c12152 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Tue, 26 Nov 2024 15:12:45 -0800 Subject: [PATCH 63/92] fix bug when creating domains of boundary elements, delete vertex_ids, address doxygen warnings --- src/serac/numerics/functional/domain.cpp | 46 +------------- src/serac/numerics/functional/domain.hpp | 11 ++-- .../functional/tests/domain_tests.cpp | 60 ------------------- 3 files changed, 8 insertions(+), 109 deletions(-) diff --git a/src/serac/numerics/functional/domain.cpp b/src/serac/numerics/functional/domain.cpp index cfeb5f7b89..55dfe9f810 100644 --- a/src/serac/numerics/functional/domain.cpp +++ b/src/serac/numerics/functional/domain.cpp @@ -48,44 +48,6 @@ std::vector> gather(const mfem::Vector& coordinates, mfem::Arr return x; } -template -static Domain domain_of_vertices(const mfem::Mesh& mesh, std::function)> predicate) -{ - assert(mesh.SpaceDimension() == d); - - Domain output{mesh, 0 /* points are 0-dimensional */}; - - // layout is undocumented, but it seems to be - // [x1, x2, x3, ..., y1, y2, y3 ..., (z1, z2, z3, ...)] - mfem::Vector vertices; - mesh.GetVertices(vertices); - - // vertices that satisfy the predicate are added to the domain - int num_vertices = mesh.GetNV(); - for (int i = 0; i < num_vertices; i++) { - tensor x; - for (int j = 0; j < d; j++) { - x[j] = vertices[j * num_vertices + i]; - } - - if (predicate(x)) { - output.vertex_ids_.push_back(i); - } - } - - return output; -} - -Domain Domain::ofVertices(const mfem::Mesh& mesh, std::function func) -{ - return domain_of_vertices(mesh, func); -} - -Domain Domain::ofVertices(const mfem::Mesh& mesh, std::function func) -{ - return domain_of_vertices(mesh, func); -} - /////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////// @@ -378,13 +340,13 @@ static Domain domain_of_boundary_elems(const mfem::Mesh& break; case mfem::Geometry::TRIANGLE: if (add) { - output.addElement(edge_id, f, geom); + output.addElement(tri_id, f, geom); } tri_id++; break; case mfem::Geometry::SQUARE: if (add) { - output.addElement(edge_id, f, geom); + output.addElement(quad_id, f, geom); } quad_id++; break; @@ -542,10 +504,6 @@ Domain set_operation(set_op op, const Domain& a, const Domain& b) using Ids = std::vector; auto apply_set_op = [&op](const Ids& x, const Ids& y) { return set_operation(op, x, y); }; - if (output.dim_ == 0) { - output.vertex_ids_ = apply_set_op(a.vertex_ids_, b.vertex_ids_); - } - auto fill_output_lists = [apply_set_op, &output](const Ids& a_ids, const Ids& a_mfem_ids, const Ids& b_ids, const Ids& b_mfem_ids, mfem::Geometry::Type g) { auto output_ids = apply_set_op(a_ids, b_ids); diff --git a/src/serac/numerics/functional/domain.hpp b/src/serac/numerics/functional/domain.hpp index 66f6e8ca82..7ecfdcfae3 100644 --- a/src/serac/numerics/functional/domain.hpp +++ b/src/serac/numerics/functional/domain.hpp @@ -37,12 +37,10 @@ struct Domain { /// @brief whether the elements in this domain are on the boundary or not Type type_; - std::vector vertex_ids_; - ///@{ /// @name ElementIds /// Indices of elements contained in the domain. - /// The first set, (vertex_ids_, edge_ids_, ...) hold the index of an element in + /// The first set, (edge_ids_, tri_ids, ...) hold the index of an element in /// this Domain in the set of all elements of like geometry in the mesh. /// For example, if edge_ids_[0] = 5, then element 0 in this domain is element /// 5 in the grouping of all edges in the mesh. In other words, these lists @@ -68,6 +66,9 @@ struct Domain { /// methods to add new entities, as this requires you to add both entries and /// keep the corresponding lists in sync. You are discouraged from /// manipulating these lists directly. + ///@} + + /// @cond std::vector edge_ids_; std::vector tri_ids_; std::vector quad_ids_; @@ -79,8 +80,9 @@ struct Domain { std::vector mfem_quad_ids_; std::vector mfem_tet_ids_; std::vector mfem_hex_ids_; - ///@} + /// @endcond + /// @brief construct an "empty" domain, to later be populated later with addElement member functions Domain(const mfem::Mesh& m, int d, Type type = Domain::Type::Elements) : mesh_(m), dim_(d), type_(type) {} /** @@ -143,7 +145,6 @@ struct Domain { /// @brief get elements by geometry type const std::vector& get(mfem::Geometry::Type geom) const { - if (geom == mfem::Geometry::POINT) return vertex_ids_; if (geom == mfem::Geometry::SEGMENT) return edge_ids_; if (geom == mfem::Geometry::TRIANGLE) return tri_ids_; if (geom == mfem::Geometry::SQUARE) return quad_ids_; diff --git a/src/serac/numerics/functional/tests/domain_tests.cpp b/src/serac/numerics/functional/tests/domain_tests.cpp index 1fb197dc2f..7893107a91 100644 --- a/src/serac/numerics/functional/tests/domain_tests.cpp +++ b/src/serac/numerics/functional/tests/domain_tests.cpp @@ -31,66 +31,6 @@ tensor average(std::vector >& positions) return total / double(positions.size()); } -TEST(domain, of_vertices) -{ - { - auto mesh = import_mesh("onehex.mesh"); - Domain d0 = Domain::ofVertices(mesh, std::function([](vec3 x) { return x[0] < 0.5; })); - EXPECT_EQ(d0.vertex_ids_.size(), 4); - EXPECT_EQ(d0.dim_, 0); - - Domain d1 = Domain::ofVertices(mesh, std::function([](vec3 x) { return x[1] < 0.5; })); - EXPECT_EQ(d1.vertex_ids_.size(), 4); - EXPECT_EQ(d1.dim_, 0); - - Domain d2 = d0 | d1; - EXPECT_EQ(d2.vertex_ids_.size(), 6); - EXPECT_EQ(d2.dim_, 0); - - Domain d3 = d0 & d1; - EXPECT_EQ(d3.vertex_ids_.size(), 2); - EXPECT_EQ(d3.dim_, 0); - } - - { - auto mesh = import_mesh("onetet.mesh"); - Domain d0 = Domain::ofVertices(mesh, std::function([](vec3 x) { return x[0] < 0.5; })); - EXPECT_EQ(d0.vertex_ids_.size(), 3); - EXPECT_EQ(d0.dim_, 0); - - Domain d1 = Domain::ofVertices(mesh, std::function([](vec3 x) { return x[1] < 0.5; })); - EXPECT_EQ(d1.vertex_ids_.size(), 3); - EXPECT_EQ(d1.dim_, 0); - - Domain d2 = d0 | d1; - EXPECT_EQ(d2.vertex_ids_.size(), 4); - EXPECT_EQ(d2.dim_, 0); - - Domain d3 = d0 & d1; - EXPECT_EQ(d3.vertex_ids_.size(), 2); - EXPECT_EQ(d3.dim_, 0); - } - - { - auto mesh = import_mesh("beam-quad.mesh"); - Domain d0 = Domain::ofVertices(mesh, std::function([](vec2 x) { return x[0] < 0.5; })); - EXPECT_EQ(d0.vertex_ids_.size(), 2); - EXPECT_EQ(d0.dim_, 0); - - Domain d1 = Domain::ofVertices(mesh, std::function([](vec2 x) { return x[1] < 0.5; })); - EXPECT_EQ(d1.vertex_ids_.size(), 9); - EXPECT_EQ(d1.dim_, 0); - - Domain d2 = d0 | d1; - EXPECT_EQ(d2.vertex_ids_.size(), 10); - EXPECT_EQ(d2.dim_, 0); - - Domain d3 = d0 & d1; - EXPECT_EQ(d3.vertex_ids_.size(), 1); - EXPECT_EQ(d3.dim_, 0); - } -} - TEST(domain, of_edges) { { From ada95fbb202c430e294538fb6e7493c688bbcc76 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Mon, 2 Dec 2024 11:03:45 -0800 Subject: [PATCH 64/92] adjust some names, delete unused code --- src/serac/numerics/functional/domain.cpp | 24 +++++++++---------- src/serac/numerics/functional/domain.hpp | 5 ++-- .../functional/tests/domain_tests.cpp | 10 -------- 3 files changed, 14 insertions(+), 25 deletions(-) diff --git a/src/serac/numerics/functional/domain.cpp b/src/serac/numerics/functional/domain.cpp index 56ad86d61a..6321d4e222 100644 --- a/src/serac/numerics/functional/domain.cpp +++ b/src/serac/numerics/functional/domain.cpp @@ -590,25 +590,25 @@ Domain set_operation(SET_OPERATION op, const Domain& a, const Domain& b) using Ids = std::vector; auto apply_set_op = [&op](const Ids& x, const Ids& y) { return set_operation(op, x, y); }; - auto fill_output_lists = [apply_set_op, &output](const Ids& a_ids, const Ids& a_mfem_ids, const Ids& b_ids, + auto fill_combined_lists = [apply_set_op, &combined](const Ids& a_ids, const Ids& a_mfem_ids, const Ids& b_ids, const Ids& b_mfem_ids, mfem::Geometry::Type g) { - auto output_ids = apply_set_op(a_ids, b_ids); - auto output_mfem_ids = apply_set_op(a_mfem_ids, b_mfem_ids); - output.addElements(output_ids, output_mfem_ids, g); + auto combined_ids = apply_set_op(a_ids, b_ids); + auto combined_mfem_ids = apply_set_op(a_mfem_ids, b_mfem_ids); + combined.addElements(combined_ids, combined_mfem_ids, g); }; - if (output.dim_ == 1) { - fill_output_lists(a.edge_ids_, a.mfem_edge_ids_, b.edge_ids_, b.mfem_edge_ids_, mfem::Geometry::SEGMENT); + if (combined.dim_ == 1) { + fill_combined_lists(a.edge_ids_, a.mfem_edge_ids_, b.edge_ids_, b.mfem_edge_ids_, mfem::Geometry::SEGMENT); } - if (output.dim_ == 2) { - fill_output_lists(a.tri_ids_, a.mfem_tri_ids_, b.tri_ids_, b.mfem_tri_ids_, mfem::Geometry::TRIANGLE); - fill_output_lists(a.quad_ids_, a.mfem_quad_ids_, b.quad_ids_, b.mfem_quad_ids_, mfem::Geometry::SQUARE); + if (combined.dim_ == 2) { + fill_combined_lists(a.tri_ids_, a.mfem_tri_ids_, b.tri_ids_, b.mfem_tri_ids_, mfem::Geometry::TRIANGLE); + fill_combined_lists(a.quad_ids_, a.mfem_quad_ids_, b.quad_ids_, b.mfem_quad_ids_, mfem::Geometry::SQUARE); } - if (output.dim_ == 3) { - fill_output_lists(a.tet_ids_, a.mfem_tet_ids_, b.tet_ids_, b.mfem_tet_ids_, mfem::Geometry::TETRAHEDRON); - fill_output_lists(a.hex_ids_, a.mfem_hex_ids_, b.hex_ids_, b.mfem_hex_ids_, mfem::Geometry::CUBE); + if (combined.dim_ == 3) { + fill_combined_lists(a.tet_ids_, a.mfem_tet_ids_, b.tet_ids_, b.mfem_tet_ids_, mfem::Geometry::TETRAHEDRON); + fill_combined_lists(a.hex_ids_, a.mfem_hex_ids_, b.hex_ids_, b.mfem_hex_ids_, mfem::Geometry::CUBE); } return combined; diff --git a/src/serac/numerics/functional/domain.hpp b/src/serac/numerics/functional/domain.hpp index 474b29c73e..4c04b3d6ab 100644 --- a/src/serac/numerics/functional/domain.hpp +++ b/src/serac/numerics/functional/domain.hpp @@ -182,8 +182,7 @@ struct Domain { */ int total_elements() const { - return int(vertex_ids_.size() + edge_ids_.size() + tri_ids_.size() + quad_ids_.size() + tet_ids_.size() + - hex_ids_.size()); + return int(edge_ids_.size() + tri_ids_.size() + quad_ids_.size() + tet_ids_.size() + hex_ids_.size()); } /** @@ -196,7 +195,7 @@ struct Domain { int total = 0; offsets[mfem::Geometry::POINT] = total; - total += vertex_ids_.size(); + total += 0; // vertices; offsets[mfem::Geometry::SEGMENT] = total; total += edge_ids_.size(); offsets[mfem::Geometry::TRIANGLE] = total; diff --git a/src/serac/numerics/functional/tests/domain_tests.cpp b/src/serac/numerics/functional/tests/domain_tests.cpp index 7893107a91..40cba25aaf 100644 --- a/src/serac/numerics/functional/tests/domain_tests.cpp +++ b/src/serac/numerics/functional/tests/domain_tests.cpp @@ -21,16 +21,6 @@ mfem::Mesh import_mesh(std::string meshfile) return mesh; } -template -tensor average(std::vector >& positions) -{ - tensor total{}; - for (auto x : positions) { - total += x; - } - return total / double(positions.size()); -} - TEST(domain, of_edges) { { From e868e9c9189c793ba165edebf803eee151160733 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Mon, 2 Dec 2024 11:21:41 -0800 Subject: [PATCH 65/92] fix some integer conversion warnings on GCC --- src/serac/numerics/functional/domain.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/serac/numerics/functional/domain.hpp b/src/serac/numerics/functional/domain.hpp index 4c04b3d6ab..994ba8fc24 100644 --- a/src/serac/numerics/functional/domain.hpp +++ b/src/serac/numerics/functional/domain.hpp @@ -197,15 +197,15 @@ struct Domain { offsets[mfem::Geometry::POINT] = total; total += 0; // vertices; offsets[mfem::Geometry::SEGMENT] = total; - total += edge_ids_.size(); + total += int(edge_ids_.size()); offsets[mfem::Geometry::TRIANGLE] = total; - total += tri_ids_.size(); + total += int(tri_ids_.size()); offsets[mfem::Geometry::SQUARE] = total; - total += quad_ids_.size(); + total += int(quad_ids_.size()); offsets[mfem::Geometry::TETRAHEDRON] = total; - total += tet_ids_.size(); + total += int(tet_ids_.size()); offsets[mfem::Geometry::CUBE] = total; - total += hex_ids_.size(); + total += int(hex_ids_.size()); offsets[mfem::Geometry::PRISM] = total; offsets[mfem::Geometry::PYRAMID] = total; offsets[mfem::Geometry::NUM_GEOMETRIES] = total; From 76d1890178fc309b4f8e52c273b7fffe35cdb21f Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Mon, 2 Dec 2024 11:41:21 -0800 Subject: [PATCH 66/92] address some doxygen warnings --- src/serac/numerics/functional/domain.cpp | 2 -- src/serac/numerics/functional/domain.hpp | 12 ++++++++++-- src/serac/numerics/functional/functional.hpp | 6 ++++++ src/serac/numerics/functional/typedefs.hpp | 3 +++ src/serac/physics/thermomechanics.hpp | 4 +--- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/serac/numerics/functional/domain.cpp b/src/serac/numerics/functional/domain.cpp index 6321d4e222..b229c3942f 100644 --- a/src/serac/numerics/functional/domain.cpp +++ b/src/serac/numerics/functional/domain.cpp @@ -29,8 +29,6 @@ namespace serac { -using mesh_t = mfem::Mesh; - /** * @brief gather vertex coordinates for a list of vertices * diff --git a/src/serac/numerics/functional/domain.hpp b/src/serac/numerics/functional/domain.hpp index 994ba8fc24..fcfe3e0bb7 100644 --- a/src/serac/numerics/functional/domain.hpp +++ b/src/serac/numerics/functional/domain.hpp @@ -89,6 +89,11 @@ struct Domain { std::vector mfem_hex_ids_; /// @endcond + /** + * @brief a collection of restriction operators for the different test/trial spaces appearing in + * integrals evaluated over this Domain. These are stored on the Domain itself to avoid duplicating + * these restriction operators in each Integral over a given Domain. + */ std::map restriction_operators; /** @@ -216,10 +221,13 @@ struct Domain { /// @brief get mfem degree of freedom list for a given FiniteElementSpace mfem::Array dof_list(const fes_t* fes) const; - /// @brief TODO + /** + * @brief create a restriction operator over this domain, using its FunctionSpace as a key + * @note if a restriction for the given key (i.e. FunctionSpace) already exists, this function does nothing + */ void insert_restriction(const fes_t* fes, FunctionSpace space); - /// @brief TODO + /// @brief getter for accessing a restriction operator by its function space const BlockElementRestriction& get_restriction(FunctionSpace space); /// @brief Add an element to the domain diff --git a/src/serac/numerics/functional/functional.hpp b/src/serac/numerics/functional/functional.hpp index 768dfcfa75..73b53272cc 100644 --- a/src/serac/numerics/functional/functional.hpp +++ b/src/serac/numerics/functional/functional.hpp @@ -101,6 +101,12 @@ inline void check_for_unsupported_elements(const mfem::Mesh& mesh) } } +/** + * @brief function for verifying that DG spaces aren't used on interior face integrals over meshes that contain "shared" faces + * + * sam: I would like to support these "shared" faces, but apparently mfem handles them in a fundamentally different way than + * the "finite element operator decomposition" pattern used by everything else (see: "ExchangeFaceNbrData") + */ inline void check_interior_face_compatibility(const mfem::Mesh& mesh, const FunctionSpace space) { if (space.family == Family::L2) { diff --git a/src/serac/numerics/functional/typedefs.hpp b/src/serac/numerics/functional/typedefs.hpp index c2cbe10c08..7dbe070f9a 100644 --- a/src/serac/numerics/functional/typedefs.hpp +++ b/src/serac/numerics/functional/typedefs.hpp @@ -5,7 +5,10 @@ namespace serac { // sam: this is a kludge-- it looks like the DG spaces need to use some interface defined ONLY on // mfem::ParMesh / mfeme::ParFiniteElementSpace, but I'm not ready to pull the trigger on a big // interface change like that, so these typedefs mark the parts that would need to eventually change + +/// @cond using mesh_t = mfem::Mesh; using fes_t = mfem::FiniteElementSpace; +/// @endcond } // namespace serac diff --git a/src/serac/physics/thermomechanics.hpp b/src/serac/physics/thermomechanics.hpp index ed1833c25c..688be30af2 100644 --- a/src/serac/physics/thermomechanics.hpp +++ b/src/serac/physics/thermomechanics.hpp @@ -352,7 +352,7 @@ class Thermomechanics : public BasePhysics { * @tparam MaterialType The thermomechanical material type * @tparam StateType The type that contains the internal variables for MaterialType * @param material A material that provides a function to evaluate stress, heat flux, density, and heat capacity - * @param domain The domain over which the source is applied. + * @param domain which elements in the mesh are described by the specified material * @param qdata the buffer of material internal variables at each quadrature point * * @pre material must be a object that can be called with the following arguments: @@ -368,8 +368,6 @@ class Thermomechanics : public BasePhysics { * values will change to `dual` numbers rather than `double`. (e.g. `tensor` becomes * `tensor, 3>`) * - * @param domain which elements in the mesh are described by the specified material - * * @pre MaterialType must return a serac::tuple of Cauchy stress, volumetric heat capacity, internal heat source, * and thermal flux when operator() is called with the arguments listed above. */ From 11bed34f96801ddf692ab5b6b21617a658c589a9 Mon Sep 17 00:00:00 2001 From: Agent Style Date: Mon, 2 Dec 2024 11:43:16 -0800 Subject: [PATCH 67/92] Apply style updates --- src/serac/numerics/functional/domain.cpp | 2 +- src/serac/numerics/functional/domain.hpp | 4 ++-- src/serac/numerics/functional/functional.hpp | 9 +++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/serac/numerics/functional/domain.cpp b/src/serac/numerics/functional/domain.cpp index b229c3942f..49fbbb64b5 100644 --- a/src/serac/numerics/functional/domain.cpp +++ b/src/serac/numerics/functional/domain.cpp @@ -589,7 +589,7 @@ Domain set_operation(SET_OPERATION op, const Domain& a, const Domain& b) auto apply_set_op = [&op](const Ids& x, const Ids& y) { return set_operation(op, x, y); }; auto fill_combined_lists = [apply_set_op, &combined](const Ids& a_ids, const Ids& a_mfem_ids, const Ids& b_ids, - const Ids& b_mfem_ids, mfem::Geometry::Type g) { + const Ids& b_mfem_ids, mfem::Geometry::Type g) { auto combined_ids = apply_set_op(a_ids, b_ids); auto combined_mfem_ids = apply_set_op(a_mfem_ids, b_mfem_ids); combined.addElements(combined_ids, combined_mfem_ids, g); diff --git a/src/serac/numerics/functional/domain.hpp b/src/serac/numerics/functional/domain.hpp index fcfe3e0bb7..8e1fb6cea2 100644 --- a/src/serac/numerics/functional/domain.hpp +++ b/src/serac/numerics/functional/domain.hpp @@ -90,7 +90,7 @@ struct Domain { /// @endcond /** - * @brief a collection of restriction operators for the different test/trial spaces appearing in + * @brief a collection of restriction operators for the different test/trial spaces appearing in * integrals evaluated over this Domain. These are stored on the Domain itself to avoid duplicating * these restriction operators in each Integral over a given Domain. */ @@ -200,7 +200,7 @@ struct Domain { int total = 0; offsets[mfem::Geometry::POINT] = total; - total += 0; // vertices; + total += 0; // vertices; offsets[mfem::Geometry::SEGMENT] = total; total += int(edge_ids_.size()); offsets[mfem::Geometry::TRIANGLE] = total; diff --git a/src/serac/numerics/functional/functional.hpp b/src/serac/numerics/functional/functional.hpp index 73b53272cc..6f1778d6bf 100644 --- a/src/serac/numerics/functional/functional.hpp +++ b/src/serac/numerics/functional/functional.hpp @@ -102,10 +102,11 @@ inline void check_for_unsupported_elements(const mfem::Mesh& mesh) } /** - * @brief function for verifying that DG spaces aren't used on interior face integrals over meshes that contain "shared" faces - * - * sam: I would like to support these "shared" faces, but apparently mfem handles them in a fundamentally different way than - * the "finite element operator decomposition" pattern used by everything else (see: "ExchangeFaceNbrData") + * @brief function for verifying that DG spaces aren't used on interior face integrals over meshes that contain "shared" + * faces + * + * sam: I would like to support these "shared" faces, but apparently mfem handles them in a fundamentally different way + * than the "finite element operator decomposition" pattern used by everything else (see: "ExchangeFaceNbrData") */ inline void check_interior_face_compatibility(const mfem::Mesh& mesh, const FunctionSpace space) { From 22dc40b02d451d461dcf4dec4ee883d5c56bc1c6 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Mon, 2 Dec 2024 13:01:44 -0800 Subject: [PATCH 68/92] explicitly add some headers to CMakeLists.txt --- src/serac/numerics/functional/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/serac/numerics/functional/CMakeLists.txt b/src/serac/numerics/functional/CMakeLists.txt index 6f0e83094b..2dfcc43502 100644 --- a/src/serac/numerics/functional/CMakeLists.txt +++ b/src/serac/numerics/functional/CMakeLists.txt @@ -21,6 +21,7 @@ set(functional_headers function_signature.hpp functional_qoi.inl integral.hpp + interior_face_integral_kernels.hpp isotropic_tensor.hpp polynomials.hpp quadrature.hpp @@ -29,6 +30,7 @@ set(functional_headers tensor.hpp tuple.hpp tuple_tensor_dual_functions.hpp + typedefs.hpp ) set(functional_sources From 3c8b96e1b0590a7d84ffd96eea4804c6824408e6 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Mon, 2 Dec 2024 14:44:04 -0800 Subject: [PATCH 69/92] (blind) attempt to fix benchmark compilation errors --- .../physics_benchmark_functional.cpp | 4 +++- ...physics_benchmark_solid_nonlinear_solve.cpp | 8 ++++++-- .../benchmarks/physics_benchmark_thermal.cpp | 18 ++++++++++++------ 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/serac/physics/benchmarks/physics_benchmark_functional.cpp b/src/serac/physics/benchmarks/physics_benchmark_functional.cpp index 6645074b01..1c0e2cf4e3 100644 --- a/src/serac/physics/benchmarks/physics_benchmark_functional.cpp +++ b/src/serac/physics/benchmarks/physics_benchmark_functional.cpp @@ -36,6 +36,8 @@ void functional_test(int parallel_refinement) using space = serac::H1; auto [fespace, fec] = serac::generateParFiniteElementSpace(mesh.get()); + Domain whole_domain = EntireDomain(*mesh); + serac::Functional residual(fespace.get(), {fespace.get()}); // Add the total domain residual term to the functional @@ -46,7 +48,7 @@ void functional_test(int parallel_refinement) auto [u, du_dx] = phi; return serac::tuple{u, du_dx}; }, - *mesh); + whole_domain); // Set a random state to evaluate the residual mfem::ParGridFunction u_global(fespace.get()); diff --git a/src/serac/physics/benchmarks/physics_benchmark_solid_nonlinear_solve.cpp b/src/serac/physics/benchmarks/physics_benchmark_solid_nonlinear_solve.cpp index 7c789c0942..67c5b76523 100644 --- a/src/serac/physics/benchmarks/physics_benchmark_solid_nonlinear_solve.cpp +++ b/src/serac/physics/benchmarks/physics_benchmark_solid_nonlinear_solve.cpp @@ -242,8 +242,10 @@ void functional_solid_test_euler(NonlinSolve nonlinSolve, Prec prec) serac::solid_mechanics::default_quasistatic_options, "serac_solid", meshTag, std::vector{}); + Domain whole_domain = EntireDomain(*meshPtr); + serac::solid_mechanics::NeoHookean material{density, bulkMod, shearMod}; - seracSolid->setMaterial(serac::DependsOn<>{}, material); + seracSolid->setMaterial(material, whole_domain); serac::Domain backSurface = serac::Domain::ofBoundaryElements(*meshPtr, serac::by_attr(3)); // 4,5 with traction makes a twist @@ -320,8 +322,10 @@ void functional_solid_test_nonlinear_buckle(NonlinSolve nonlinSolve, Prec prec, serac::solid_mechanics::default_quasistatic_options, "serac_solid", meshTag, std::vector{}); + Domain whole_domain = EntireDomain(*meshPtr); + serac::solid_mechanics::NeoHookean material{density, bulkMod, shearMod}; - seracSolid->setMaterial(serac::DependsOn<>{}, material); + seracSolid->setMaterial(material, whole_domain); // fix displacement on side surface seracSolid->setDisplacementBCs({2, 3, 4, 5}, [](const mfem::Vector&, mfem::Vector& u) { u = 0.0; }); diff --git a/src/serac/physics/benchmarks/physics_benchmark_thermal.cpp b/src/serac/physics/benchmarks/physics_benchmark_thermal.cpp index cb31ad519f..ff2cc25930 100644 --- a/src/serac/physics/benchmarks/physics_benchmark_thermal.cpp +++ b/src/serac/physics/benchmarks/physics_benchmark_thermal.cpp @@ -36,7 +36,8 @@ void functional_test_static() auto mesh = serac::mesh::refineAndDistribute(serac::buildMeshFromFile(filename), serial_refinement, parallel_refinement); - serac::StateManager::setMesh(std::move(mesh), "default_mesh"); + + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), "default_mesh"); // Define a boundary attribute set std::set ess_bdr = {1}; @@ -63,8 +64,10 @@ void functional_test_static() cond = {{{1.5, 0.01, 0.0}, {0.01, 1.0, 0.0}, {0.0, 0.0, 1.0}}}; } + Domain whole_domain = EntireDomain(pmesh); + serac::heat_transfer::LinearConductor mat(1.0, 1.0, cond); - thermal_solver.setMaterial(mat); + thermal_solver.setMaterial(mat, whole_domain); // Define the function for the initial temperature and boundary condition auto one = [](const mfem::Vector&, double) -> double { return 1.0; }; @@ -75,7 +78,7 @@ void functional_test_static() // Define a constant source term serac::heat_transfer::ConstantSource source{1.0}; - thermal_solver.setSource(source); + thermal_solver.setSource(source, whole_domain); // Finalize the data structures thermal_solver.completeSetup(); @@ -108,7 +111,8 @@ void functional_test_dynamic() auto mesh = serac::mesh::refineAndDistribute(serac::buildMeshFromFile(filename), serial_refinement, parallel_refinement); - serac::StateManager::setMesh(std::move(mesh), "default_mesh"); + + auto & pmesh = serac::StateManager::setMesh(std::move(mesh), "default_mesh"); // Define a boundary attribute set std::set ess_bdr = {1}; @@ -118,10 +122,12 @@ void functional_test_dynamic() serac::heat_transfer::default_nonlinear_options, serac::heat_transfer::default_linear_options, serac::heat_transfer::default_timestepping_options, "thermal_functional", "default_mesh"); + Domain whole_domain = EntireDomain(pmesh); + // Define an isotropic conductor material model serac::heat_transfer::LinearIsotropicConductor mat(1.0, 1.0, 1.0); - thermal_solver.setMaterial(mat); + thermal_solver.setMaterial(mat, whole_domain); // Define the function for the initial temperature and boundary condition auto initial_temp = [](const mfem::Vector& x, double) -> double { @@ -137,7 +143,7 @@ void functional_test_dynamic() // Define a constant source term serac::heat_transfer::ConstantSource source{1.0}; - thermal_solver.setSource(source); + thermal_solver.setSource(source, whole_domain); // Finalize the data structures thermal_solver.completeSetup(); From 5938689334fb19aa9c76941f3297dceb723c3850 Mon Sep 17 00:00:00 2001 From: Alex Tyler Chapman Date: Mon, 2 Dec 2024 17:51:19 -0800 Subject: [PATCH 70/92] Fix smith ci: run build script in smith repo --- scripts/gitlab/build_and_test.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/gitlab/build_and_test.sh b/scripts/gitlab/build_and_test.sh index f1cf374c9d..349cf1c582 100755 --- a/scripts/gitlab/build_and_test.sh +++ b/scripts/gitlab/build_and_test.sh @@ -4,6 +4,7 @@ echo "DO_INTEGRATION_TESTS=${DO_INTEGRATION_TESTS}" echo "EXTRA_BUILD_OPTIONS=${EXTRA_BUILD_OPTIONS}" echo "EXTRA_CMAKE_OPTIONS=${EXTRA_CMAKE_OPTIONS}" echo "HOST_CONFIG=${HOST_CONFIG}" +echo "FULL_BUILD_ROOT=${FULL_BUILD_ROOT}" # EXTRA_CMAKE_OPTIONS needs quotes wrapped around it, since it may contain spaces, and we want them all to be a part of # one large string. (e.g. "-DSERAC_ENABLE_CODEVELOP=ON -DENABLE_DOCS=OFF") @@ -14,6 +15,7 @@ echo "HOST_CONFIG=${HOST_CONFIG}" python3 scripts/llnl/build_src.py -v \ --host-config=${HOST_CONFIG} \ --extra-cmake-options="${EXTRA_CMAKE_OPTIONS}" \ + --directory=${FULL_BUILD_ROOT} \ ${EXTRA_BUILD_OPTIONS} if [ $? -ne 0 ]; then { echo "ERROR: build_src.py failed." ; exit 1; } fi From 7d69bc119b23c7805465351ebed1bb3052d66c9b Mon Sep 17 00:00:00 2001 From: Alex Tyler Chapman Date: Mon, 2 Dec 2024 18:04:40 -0800 Subject: [PATCH 71/92] fix directory --- scripts/gitlab/build_and_test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/gitlab/build_and_test.sh b/scripts/gitlab/build_and_test.sh index 349cf1c582..cb68046f07 100755 --- a/scripts/gitlab/build_and_test.sh +++ b/scripts/gitlab/build_and_test.sh @@ -4,7 +4,7 @@ echo "DO_INTEGRATION_TESTS=${DO_INTEGRATION_TESTS}" echo "EXTRA_BUILD_OPTIONS=${EXTRA_BUILD_OPTIONS}" echo "EXTRA_CMAKE_OPTIONS=${EXTRA_CMAKE_OPTIONS}" echo "HOST_CONFIG=${HOST_CONFIG}" -echo "FULL_BUILD_ROOT=${FULL_BUILD_ROOT}" +echo "CI_PROJECT_DIR=${CI_PROJECT_DIR}" # EXTRA_CMAKE_OPTIONS needs quotes wrapped around it, since it may contain spaces, and we want them all to be a part of # one large string. (e.g. "-DSERAC_ENABLE_CODEVELOP=ON -DENABLE_DOCS=OFF") @@ -15,7 +15,7 @@ echo "FULL_BUILD_ROOT=${FULL_BUILD_ROOT}" python3 scripts/llnl/build_src.py -v \ --host-config=${HOST_CONFIG} \ --extra-cmake-options="${EXTRA_CMAKE_OPTIONS}" \ - --directory=${FULL_BUILD_ROOT} \ + --directory=${CI_PROJECT_DIR} \ ${EXTRA_BUILD_OPTIONS} if [ $? -ne 0 ]; then { echo "ERROR: build_src.py failed." ; exit 1; } fi From 4daf2874600d6135a33f88872513e95cd2c23500 Mon Sep 17 00:00:00 2001 From: Alex Tyler Chapman Date: Mon, 2 Dec 2024 18:05:36 -0800 Subject: [PATCH 72/92] use build root --- scripts/gitlab/build_and_test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/gitlab/build_and_test.sh b/scripts/gitlab/build_and_test.sh index cb68046f07..6ace965c36 100755 --- a/scripts/gitlab/build_and_test.sh +++ b/scripts/gitlab/build_and_test.sh @@ -4,7 +4,7 @@ echo "DO_INTEGRATION_TESTS=${DO_INTEGRATION_TESTS}" echo "EXTRA_BUILD_OPTIONS=${EXTRA_BUILD_OPTIONS}" echo "EXTRA_CMAKE_OPTIONS=${EXTRA_CMAKE_OPTIONS}" echo "HOST_CONFIG=${HOST_CONFIG}" -echo "CI_PROJECT_DIR=${CI_PROJECT_DIR}" +echo "BUILD_ROOT=${BUILD_ROOT}" # EXTRA_CMAKE_OPTIONS needs quotes wrapped around it, since it may contain spaces, and we want them all to be a part of # one large string. (e.g. "-DSERAC_ENABLE_CODEVELOP=ON -DENABLE_DOCS=OFF") @@ -15,7 +15,7 @@ echo "CI_PROJECT_DIR=${CI_PROJECT_DIR}" python3 scripts/llnl/build_src.py -v \ --host-config=${HOST_CONFIG} \ --extra-cmake-options="${EXTRA_CMAKE_OPTIONS}" \ - --directory=${CI_PROJECT_DIR} \ + --directory=${BUILD_ROOT} \ ${EXTRA_BUILD_OPTIONS} if [ $? -ne 0 ]; then { echo "ERROR: build_src.py failed." ; exit 1; } fi From d1e255eb0855ee118d296669d1c67ef55fdb42af Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Tue, 3 Dec 2024 11:59:02 -0800 Subject: [PATCH 73/92] enable building benchmarks by directly specifying SERAC_ENABLE_BENCHMARKS=ON --- cmake/SeracBasics.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/SeracBasics.cmake b/cmake/SeracBasics.cmake index 735554fb78..50fb02dd27 100644 --- a/cmake/SeracBasics.cmake +++ b/cmake/SeracBasics.cmake @@ -43,7 +43,9 @@ endif() option(SERAC_ENABLE_PROFILING "Enable profiling functionality" OFF) -cmake_dependent_option(SERAC_ENABLE_BENCHMARKS "Enable benchmark executables" ON "ENABLE_BENCHMARKS" OFF) +if (ENABLE_BENCHMARKS) + set(SERAC_ENABLE_BENCHMARKS ON) +endif() # User turned on benchmarking but explicitly turned off profiling. Error out. if ((ENABLE_BENCHMARKS OR SERAC_ENABLE_BENCHMARKS) AND NOT SERAC_ENABLE_PROFILING) From cae2d94d3986ff2fe16387e7f3188b9888a1e7d9 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Tue, 3 Dec 2024 11:59:43 -0800 Subject: [PATCH 74/92] fix compilation error from missing namespace prefix in benchmarks --- src/serac/physics/benchmarks/physics_benchmark_functional.cpp | 2 +- src/serac/physics/benchmarks/physics_benchmark_thermal.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/serac/physics/benchmarks/physics_benchmark_functional.cpp b/src/serac/physics/benchmarks/physics_benchmark_functional.cpp index 1c0e2cf4e3..ee67b9cb64 100644 --- a/src/serac/physics/benchmarks/physics_benchmark_functional.cpp +++ b/src/serac/physics/benchmarks/physics_benchmark_functional.cpp @@ -36,7 +36,7 @@ void functional_test(int parallel_refinement) using space = serac::H1; auto [fespace, fec] = serac::generateParFiniteElementSpace(mesh.get()); - Domain whole_domain = EntireDomain(*mesh); + serac::Domain whole_domain = serac::EntireDomain(*mesh); serac::Functional residual(fespace.get(), {fespace.get()}); diff --git a/src/serac/physics/benchmarks/physics_benchmark_thermal.cpp b/src/serac/physics/benchmarks/physics_benchmark_thermal.cpp index ff2cc25930..2d1e6c1614 100644 --- a/src/serac/physics/benchmarks/physics_benchmark_thermal.cpp +++ b/src/serac/physics/benchmarks/physics_benchmark_thermal.cpp @@ -64,7 +64,7 @@ void functional_test_static() cond = {{{1.5, 0.01, 0.0}, {0.01, 1.0, 0.0}, {0.0, 0.0, 1.0}}}; } - Domain whole_domain = EntireDomain(pmesh); + serac::Domain whole_domain = serac::EntireDomain(pmesh); serac::heat_transfer::LinearConductor mat(1.0, 1.0, cond); thermal_solver.setMaterial(mat, whole_domain); @@ -122,7 +122,7 @@ void functional_test_dynamic() serac::heat_transfer::default_nonlinear_options, serac::heat_transfer::default_linear_options, serac::heat_transfer::default_timestepping_options, "thermal_functional", "default_mesh"); - Domain whole_domain = EntireDomain(pmesh); + serac::Domain whole_domain = serac::EntireDomain(pmesh); // Define an isotropic conductor material model serac::heat_transfer::LinearIsotropicConductor mat(1.0, 1.0, 1.0); From afb499a8c5254a00e4e3e34922ba00383374fdca Mon Sep 17 00:00:00 2001 From: Agent Style Date: Tue, 3 Dec 2024 12:04:16 -0800 Subject: [PATCH 75/92] Apply style updates --- src/serac/physics/benchmarks/physics_benchmark_thermal.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/serac/physics/benchmarks/physics_benchmark_thermal.cpp b/src/serac/physics/benchmarks/physics_benchmark_thermal.cpp index 2d1e6c1614..a8ab5a8f64 100644 --- a/src/serac/physics/benchmarks/physics_benchmark_thermal.cpp +++ b/src/serac/physics/benchmarks/physics_benchmark_thermal.cpp @@ -36,7 +36,7 @@ void functional_test_static() auto mesh = serac::mesh::refineAndDistribute(serac::buildMeshFromFile(filename), serial_refinement, parallel_refinement); - + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), "default_mesh"); // Define a boundary attribute set @@ -111,8 +111,8 @@ void functional_test_dynamic() auto mesh = serac::mesh::refineAndDistribute(serac::buildMeshFromFile(filename), serial_refinement, parallel_refinement); - - auto & pmesh = serac::StateManager::setMesh(std::move(mesh), "default_mesh"); + + auto& pmesh = serac::StateManager::setMesh(std::move(mesh), "default_mesh"); // Define a boundary attribute set std::set ess_bdr = {1}; From 114fd55f579c9259f0ae92a4be2da8e295ec23c2 Mon Sep 17 00:00:00 2001 From: Alex Tyler Chapman Date: Tue, 3 Dec 2024 13:10:20 -0800 Subject: [PATCH 76/92] update get_repo_dir to find smith or serac repo --- scripts/gitlab/build_and_test.sh | 2 -- scripts/llnl/common_build_functions.py | 25 +++++++++++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/scripts/gitlab/build_and_test.sh b/scripts/gitlab/build_and_test.sh index 6ace965c36..f1cf374c9d 100755 --- a/scripts/gitlab/build_and_test.sh +++ b/scripts/gitlab/build_and_test.sh @@ -4,7 +4,6 @@ echo "DO_INTEGRATION_TESTS=${DO_INTEGRATION_TESTS}" echo "EXTRA_BUILD_OPTIONS=${EXTRA_BUILD_OPTIONS}" echo "EXTRA_CMAKE_OPTIONS=${EXTRA_CMAKE_OPTIONS}" echo "HOST_CONFIG=${HOST_CONFIG}" -echo "BUILD_ROOT=${BUILD_ROOT}" # EXTRA_CMAKE_OPTIONS needs quotes wrapped around it, since it may contain spaces, and we want them all to be a part of # one large string. (e.g. "-DSERAC_ENABLE_CODEVELOP=ON -DENABLE_DOCS=OFF") @@ -15,7 +14,6 @@ echo "BUILD_ROOT=${BUILD_ROOT}" python3 scripts/llnl/build_src.py -v \ --host-config=${HOST_CONFIG} \ --extra-cmake-options="${EXTRA_CMAKE_OPTIONS}" \ - --directory=${BUILD_ROOT} \ ${EXTRA_BUILD_OPTIONS} if [ $? -ne 0 ]; then { echo "ERROR: build_src.py failed." ; exit 1; } fi diff --git a/scripts/llnl/common_build_functions.py b/scripts/llnl/common_build_functions.py index 927aacf170..4a9eddfe0d 100755 --- a/scripts/llnl/common_build_functions.py +++ b/scripts/llnl/common_build_functions.py @@ -633,9 +633,26 @@ def get_build_dir(prefix, host_config): return pjoin(prefix, "build-" + host_config_root) +_repo_dir = "" def get_repo_dir(): - script_dir = os.path.dirname(os.path.realpath(__file__)) - return os.path.abspath(pjoin(script_dir, "../..")) + """ Return absolute path to Smith or Serac repo """ + # Only set var once + global _repo_dir + if not _repo_dir: + # Set repo dir to be relative to this script + _repo_dir = os.path.abspath(pjoin(get_script_dir(), "../..")) + + # Check the parent dir for an uberenv config with a package_name "smith". + # This means Serac is a submodule. Change the repo dir to be parent dir (i.e. + # Smith repo dir) + smith_uberenv_config_path = pjoin(_repo_dir, "../.uberenv_config.json") + if os.path.exists(smith_uberenv_config_path): + with open(smith_uberenv_config_path) as json_file: + data = json.load(json_file) + if "package_name" in data: + if data["package_name"] == "smith": + _repo_dir = pjoin(_repo_dir, "../") + return _repo_dir def get_build_and_test_root(prefix, timestamp): @@ -681,7 +698,7 @@ def get_shared_spot_dir(): def get_uberenv_path(): - return pjoin(get_script_dir(), "../uberenv/uberenv.py") + return pjoin(get_repo_dir(), "scripts/uberenv/uberenv.py") def on_rz(): @@ -699,7 +716,7 @@ def get_script_dir(): def get_project_name(): global _project_name if not _project_name: - uberenv_config_path = os.path.abspath(os.path.join(get_script_dir(), "../../.uberenv_config.json")) + uberenv_config_path = pjoin(get_repo_dir(), ".uberenv_config.json") _project_name = "UNKNOWN_PROJECT" if os.path.exists(uberenv_config_path): with open(uberenv_config_path) as json_file: From 9c440269036f6361b74ac04e07cc370fe6c83c68 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Wed, 4 Dec 2024 14:30:43 -0800 Subject: [PATCH 77/92] temporarily disable lua integration tests --- .gitlab/build_toss4.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/build_toss4.yml b/.gitlab/build_toss4.yml index 5ceeda03a6..d4f92a098c 100644 --- a/.gitlab/build_toss4.yml +++ b/.gitlab/build_toss4.yml @@ -42,7 +42,7 @@ toss4-clang_14_0_6-src: COMPILER: "clang@14.0.6" HOST_CONFIG: "ruby-toss_4_x86_64_ib-${COMPILER}.cmake" EXTRA_CMAKE_OPTIONS: "-DENABLE_BENCHMARKS=ON" - DO_INTEGRATION_TESTS: "yes" + #DO_INTEGRATION_TESTS: "yes" ALLOC_NODES: "2" ALLOC_TIME: "30" ALLOC_DEADLINE: "60" From 989bca6e87a9af81d22f9bd5e3e96e7d0c7d1d52 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Wed, 4 Dec 2024 14:35:15 -0800 Subject: [PATCH 78/92] delete print statements --- src/serac/numerics/functional/functional.hpp | 2 -- src/serac/numerics/functional/integral.hpp | 3 --- .../numerics/functional/interior_face_integral_kernels.hpp | 4 ---- 3 files changed, 9 deletions(-) diff --git a/src/serac/numerics/functional/functional.hpp b/src/serac/numerics/functional/functional.hpp index 6f1778d6bf..cfaca8c88b 100644 --- a/src/serac/numerics/functional/functional.hpp +++ b/src/serac/numerics/functional/functional.hpp @@ -499,12 +499,10 @@ class Functional { Domain& dom = integral.domain_; const serac::BlockElementRestriction& G_test = dom.get_restriction(test_function_space_); - mpi::out << "G_test.ESize() " << G_test.ESize() << std::endl; for (auto i : integral.active_trial_spaces_) { const serac::BlockElementRestriction& G_trial = dom.get_restriction(trial_function_spaces_[i]); input_E_buffer_[i].SetSize(int(G_trial.ESize())); - mpi::out << "G_trial.ESize() " << G_trial.ESize() << std::endl; input_E_[i].Update(input_E_buffer_[i], G_trial.bOffsets()); G_trial.Gather(input_L_[i], input_E_[i]); } diff --git a/src/serac/numerics/functional/integral.hpp b/src/serac/numerics/functional/integral.hpp index c36407b731..16ab1bb489 100644 --- a/src/serac/numerics/functional/integral.hpp +++ b/src/serac/numerics/functional/integral.hpp @@ -77,11 +77,8 @@ struct Integral { (with_AD) ? evaluation_with_AD_[functional_to_integral_index_.at(differentiation_index)] : evaluation_; for (auto& [geometry, func] : kernels) { std::vector inputs(active_trial_spaces_.size()); - mpi::out << geometry << std::endl; for (std::size_t i = 0; i < active_trial_spaces_.size(); i++) { inputs[i] = input_E[uint32_t(active_trial_spaces_[i])].GetBlock(geometry).Read(); - mpi::out << "input_E[" << i - << "].Size(): " << input_E[uint32_t(active_trial_spaces_[i])].GetBlock(geometry).Size() << std::endl; } func(t, inputs, output_E.GetBlock(geometry).ReadWrite(), update_state); } diff --git a/src/serac/numerics/functional/interior_face_integral_kernels.hpp b/src/serac/numerics/functional/interior_face_integral_kernels.hpp index 6d61808e51..269b583afe 100644 --- a/src/serac/numerics/functional/interior_face_integral_kernels.hpp +++ b/src/serac/numerics/functional/interior_face_integral_kernels.hpp @@ -167,10 +167,6 @@ void evaluation_kernel_impl(trial_element_type trial_elements, test_element, dou [[maybe_unused]] tuple u = { reinterpret_cast(trial_elements))::dof_type_if*>(inputs[indices])...}; - ((mpi::out << "sizeof(dof_type_if) / sizeof(double): " << sizeof(decltype(get(u)[0])) / sizeof(double) - << std::endl), - ...); - // for each element in the domain for (uint32_t e = 0; e < num_elements; e++) { mpi::out << "e: " << e << " / " << num_elements << std::endl; From 234d04afb124db8360a05aad6ae8009f205be9a9 Mon Sep 17 00:00:00 2001 From: Alex Tyler Chapman Date: Wed, 4 Dec 2024 15:02:40 -0800 Subject: [PATCH 79/92] attempt to revert changes to get_repo_dir --- scripts/llnl/common_build_functions.py | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/scripts/llnl/common_build_functions.py b/scripts/llnl/common_build_functions.py index 03a67eca9d..de2e620a3b 100755 --- a/scripts/llnl/common_build_functions.py +++ b/scripts/llnl/common_build_functions.py @@ -637,26 +637,8 @@ def get_build_dir(prefix, host_config): return pjoin(prefix, "build-" + host_config_root) -_repo_dir = "" def get_repo_dir(): - """ Return absolute path to Smith or Serac repo """ - # Only set var once - global _repo_dir - if not _repo_dir: - # Set repo dir to be relative to this script - _repo_dir = os.path.abspath(pjoin(get_script_dir(), "../..")) - - # Check the parent dir for an uberenv config with a package_name "smith". - # This means Serac is a submodule. Change the repo dir to be parent dir (i.e. - # Smith repo dir) - smith_uberenv_config_path = pjoin(_repo_dir, "../.uberenv_config.json") - if os.path.exists(smith_uberenv_config_path): - with open(smith_uberenv_config_path) as json_file: - data = json.load(json_file) - if "package_name" in data: - if data["package_name"] == "smith": - _repo_dir = pjoin(_repo_dir, "../") - return _repo_dir + return os.path.abspath(pjoin(get_script_dir(), "../..")) def get_build_and_test_root(prefix, timestamp): From f374a7a6b6db97987e9e51962cab1d09bbb5f8ed Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Wed, 4 Dec 2024 15:06:03 -0800 Subject: [PATCH 80/92] respond to Mike's PR feedback --- .../numerics/functional/detail/error_code | 175 +++++++++++++ src/serac/numerics/functional/functional.hpp | 103 -------- .../numerics/functional/geometric_factors.cpp | 84 ------ src/serac/numerics/functional/integral.hpp | 3 - .../numerics/functional/tests/CMakeLists.txt | 1 - .../functional/tests/bug_residual.cpp | 102 -------- .../numerics/functional/tests/cuda_sandbox.cu | 244 ------------------ .../tests/dg_restriction_operators.cpp | 6 - .../functional/tests/functional_basic_dg.cpp | 73 ------ 9 files changed, 175 insertions(+), 616 deletions(-) create mode 100644 src/serac/numerics/functional/detail/error_code delete mode 100644 src/serac/numerics/functional/tests/bug_residual.cpp delete mode 100644 src/serac/numerics/functional/tests/cuda_sandbox.cu diff --git a/src/serac/numerics/functional/detail/error_code b/src/serac/numerics/functional/detail/error_code new file mode 100644 index 0000000000..124839def1 --- /dev/null +++ b/src/serac/numerics/functional/detail/error_code @@ -0,0 +1,175 @@ +==558614==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6190000437d0 at pc 0x5cf949c2911f bp 0x7ffcb5ae08f0 sp 0x7ffcb5ae08e8 +READ of size 8 at 0x6190000437d0 thread T0 +================================================================= +==558615==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6190000437d0 at pc 0x64f5687e111f bp 0x7ffc48d1be30 sp 0x7ffc48d1be28 +READ of size 8 at 0x6190000437d0 thread T0 + #0 0x5cf949c2911e in auto serac::dot(serac::tensor const&, serac::tensor const&) /home/sam/code/serac/src/serac/infrastructure/../../serac/numerics/functional/tensor.hpp:765:23 + #1 0x5cf949c27cf4 in auto serac::finite_element<(mfem::Geometry::Type)1, serac::L2<1, 2> >::interpolate<2>(serac::tensor const&, serac::TensorProductQuadratureRule<2> const&) /home/sam/code/serac/src/serac/infrastructure/../../serac/numerics/functional/detail/segment_L2.inl:174:16 + #2 0x5cf949c276ca in void serac::interior_face_integral::evaluation_kernel_impl<2147483648u, 2, (mfem::Geometry::Type)1, serac::finite_element<(mfem::Geometry::Type)1, serac::L2<1, 2> >, serac::tuple > >, void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >)::'lambda'(double, auto, auto), serac::zero, 0>(serac::tuple > >, serac::finite_element<(mfem::Geometry::Type)1, serac::L2<1, 2> >, double, std::vector > const&, double*, double const*, double const*, void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >)::'lambda'(double, auto, auto), serac::zero*, unsigned int, camp::int_seq) /home/sam/code/serac/src/serac/infrastructure/../../serac/numerics/functional/interior_face_integral_kernels.hpp:175:9 + #3 0x5cf949c271fa in auto serac::interior_face_integral::evaluation_kernel<2147483648u, 2, (mfem::Geometry::Type)1, FunctionSignature (serac::L2<1, 2>)>, void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >)::'lambda'(double, auto, auto), serac::zero>(FunctionSignature (serac::L2<1, 2>)>, void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >)::'lambda'(double, auto, auto), double const*, double const*, std::shared_ptr, unsigned int)::'lambda'(double, std::vector > const&, double*, bool)::operator()(double, std::vector > const&, double*, bool) const /home/sam/code/serac/src/serac/infrastructure/../../serac/numerics/functional/interior_face_integral_kernels.hpp:347:5 + #4 0x5cf949c26f5a in auto std::__invoke_impl (serac::L2<1, 2>)>, void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >)::'lambda'(double, auto, auto), serac::zero>(FunctionSignature (serac::L2<1, 2>)>, void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >)::'lambda'(double, auto, auto), double const*, double const*, std::shared_ptr, unsigned int)::'lambda'(double, std::vector > const&, double*, bool)&, double, std::vector > const&, double*, bool>(std::__invoke_other, auto&&, double&&, std::vector > const&, double*&&, bool&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/invoke.h:61:14 + #5 0x5cf949c26de0 in std::enable_if > const&, double*, bool>, auto>::type std::__invoke_r (serac::L2<1, 2>)>, void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >)::'lambda'(double, auto, auto), serac::zero>(FunctionSignature (serac::L2<1, 2>)>, void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >)::'lambda'(double, auto, auto), double const*, double const*, std::shared_ptr, unsigned int)::'lambda'(double, std::vector > const&, double*, bool)&, double, std::vector > const&, double*, bool>(auto&&, double&&, std::vector > const&, double*&&, bool&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/invoke.h:111:2 + #6 0x5cf949c269a0 in std::_Function_handler > const&, double*, bool), auto serac::interior_face_integral::evaluation_kernel<2147483648u, 2, (mfem::Geometry::Type)1, FunctionSignature (serac::L2<1, 2>)>, void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >)::'lambda'(double, auto, auto), serac::zero>(FunctionSignature (serac::L2<1, 2>)>, void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >)::'lambda'(double, auto, auto), double const*, double const*, std::shared_ptr, unsigned int)::'lambda'(double, std::vector > const&, double*, bool)>::_M_invoke(std::_Any_data const&, double&&, std::vector > const&, double*&&, bool&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/std_function.h:290:9 + #7 0x5cf949c475d8 in std::function > const&, double*, bool)>::operator()(double, std::vector > const&, double*, bool) const /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/std_function.h:591:9 + #8 0x5cf949c46bee in serac::Integral::Mult(double, std::vector > const&, mfem::BlockVector&, unsigned int, bool) const /home/sam/code/serac/src/serac/infrastructure/../../serac/numerics/functional/integral.hpp:80:7 + #9 0x5cf949c4613c in serac::Functional (serac::L2<1, 2>), (serac::ExecutionSpace)0>::operator_paren_return<2147483648u>::type serac::Functional (serac::L2<1, 2>), (serac::ExecutionSpace)0>::operator()<2147483648u, mfem::Vector>(serac::DifferentiateWRT<2147483648u>, double, mfem::Vector const&) /home/sam/code/serac/src/serac/infrastructure/../../serac/numerics/functional/functional.hpp:466:16 + #10 0x5cf949bf3c73 in auto serac::Functional (serac::L2<1, 2>), (serac::ExecutionSpace)0>::operator()(double, mfem::Vector const&) /home/sam/code/serac/src/serac/infrastructure/../../serac/numerics/functional/functional.hpp:507:12 + #11 0x5cf949bf0ac2 in void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >) /home/sam/code/serac/src/serac/numerics/functional/tests/functional_basic_dg.cpp:72:16 + #12 0x5cf949b980b6 in basic_L2_test_tris_and_quads_linear_Test::TestBody() /home/sam/code/serac/src/serac/numerics/functional/tests/functional_basic_dg.cpp:77:46 + #13 0x5cf949d1fb9a in void testing::internal::HandleSehExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2621:10 + #14 0x5cf949d03689 in void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2657:14 + #15 0x5cf949ce27a2 in testing::Test::Run() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2696:5 + #16 0x5cf949ce339f in testing::TestInfo::Run() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2845:11 + #17 0x5cf949ce3c1e in testing::TestSuite::Run() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:3023:30 + #18 0x5cf949cf4960 in testing::internal::UnitTestImpl::RunAllTests() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:5926:44 + #19 0x5cf949d2401a in bool testing::internal::HandleSehExceptionsInMethodIfSupported(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2621:10 + #20 0x5cf949d05b59 in bool testing::internal::HandleExceptionsInMethodIfSupported(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2657:14 + #21 0x5cf949cf44ba in testing::UnitTest::Run() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:5492:10 + #22 0x5cf949ca8490 in RUN_ALL_TESTS() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/include/gtest/gtest.h:2314:73 + #23 0x5cf949b98f48 in main /home/sam/code/serac/src/serac/numerics/functional/tests/functional_basic_dg.cpp:209:16 + #24 0x70f059a29d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 + #25 0x70f059a29e3f in __libc_start_main csu/../csu/libc-start.c:392:3 + #26 0x5cf949ad7da4 in _start (/home/sam/code/serac/build-Debug/tests/functional_basic_dg+0x980da4) (BuildId: 24bdc502972e3564795b90b83e63bc92533fcdae) + +0x6190000437d0 is located 0 bytes to the right of 1104-byte region [0x619000043380,0x6190000437d0) +allocated by thread T0 here: + #0 0x5cf949b95acd in operator new[](unsigned long) (/home/sam/code/serac/build-Debug/tests/functional_basic_dg+0xa3eacd) (BuildId: 24bdc502972e3564795b90b83e63bc92533fcdae) + #1 0x5cf949bf7b1d in mfem::Memory::Alloc<16ul, true>::New(unsigned long) /home/sam/code/serac/mfem/linalg/../general/mem_manager.hpp:582:55 + #2 0x5cf949bf7ae4 in mfem::Memory::NewHOST(unsigned long) /home/sam/code/serac/mfem/linalg/../general/mem_manager.hpp:596:14 + #3 0x5cf949bf8f47 in mfem::Memory::New(int, mfem::MemoryType) /home/sam/code/serac/mfem/linalg/../general/mem_manager.hpp:955:44 + #4 0x5cf949c00064 in mfem::Vector::SetSize(int) /home/sam/code/serac/mfem/mesh/../linalg/vector.hpp:554:9 + #5 0x5cf949c45ece in serac::Functional (serac::L2<1, 2>), (serac::ExecutionSpace)0>::operator_paren_return<2147483648u>::type serac::Functional (serac::L2<1, 2>), (serac::ExecutionSpace)0>::operator()<2147483648u, mfem::Vector>(serac::DifferentiateWRT<2147483648u>, double, mfem::Vector const&) /home/sam/code/serac/src/serac/infrastructure/../../serac/numerics/functional/functional.hpp:459:28 + #6 0x5cf949bf3c73 in auto serac::Functional (serac::L2<1, 2>), (serac::ExecutionSpace)0>::operator()(double, mfem::Vector const&) /home/sam/code/serac/src/serac/infrastructure/../../serac/numerics/functional/functional.hpp:507:12 + #7 0x5cf949bf0ac2 in void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >) /home/sam/code/serac/src/serac/numerics/functional/tests/functional_basic_dg.cpp:72:16 + #8 0x5cf949b980b6 in basic_L2_test_tris_and_quads_linear_Test::TestBody() /home/sam/code/serac/src/serac/numerics/functional/tests/functional_basic_dg.cpp:77:46 + #9 0x5cf949d1fb9a in void testing::internal::HandleSehExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2621:10 + #10 0x5cf949d03689 in void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2657:14 + #11 0x5cf949ce27a2 in testing::Test::Run() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2696:5 + #12 0x5cf949ce339f in testing::TestInfo::Run() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2845:11 + #13 0x5cf949ce3c1e in testing::TestSuite::Run() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:3023:30 + #14 0x5cf949cf4960 in testing::internal::UnitTestImpl::RunAllTests() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:5926:44 + #15 0x5cf949d2401a in bool testing::internal::HandleSehExceptionsInMethodIfSupported(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2621:10 + #16 0x5cf949d05b59 in bool testing::internal::HandleExceptionsInMethodIfSupported(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2657:14 + #17 0x5cf949cf44ba in testing::UnitTest::Run() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:5492:10 + #18 0x5cf949ca8490 in RUN_ALL_TESTS() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/include/gtest/gtest.h:2314:73 + #19 0x5cf949b98f48 in main /home/sam/code/serac/src/serac/numerics/functional/tests/functional_basic_dg.cpp:209:16 + #20 0x70f059a29d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 + +SUMMARY: AddressSanitizer: heap-buffer-overflow /home/sam/code/serac/src/serac/infrastructure/../../serac/numerics/functional/tensor.hpp:765:23 in auto serac::dot(serac::tensor const&, serac::tensor const&) +Shadow bytes around the buggy address: + 0x0c32800006a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0c32800006b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0c32800006c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0c32800006d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0c32800006e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +=>0x0c32800006f0: 00 00 00 00 00 00 00 00 00 00[fa]fa fa fa fa fa + 0x0c3280000700: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa + 0x0c3280000710: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0c3280000720: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0c3280000730: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0c3280000740: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Shadow byte legend (one shadow byte represents 8 application bytes): + Addressable: 00 + Partially addressable: 01 02 03 04 05 06 07 + Heap left redzone: fa + Freed heap region: fd + Stack left redzone: f1 + Stack mid redzone: f2 + Stack right redzone: f3 + Stack after return: f5 + Stack use after scope: f8 + Global redzone: f9 + Global init order: f6 + Poisoned by user: f7 + Container overflow: fc + Array cookie: ac + Intra object redzone: bb + ASan internal: fe + Left alloca redzone: ca + Right alloca redzone: cb +==558614==ABORTING + #0 0x64f5687e111e in auto serac::dot(serac::tensor const&, serac::tensor const&) /home/sam/code/serac/src/serac/infrastructure/../../serac/numerics/functional/tensor.hpp:765:23 + #1 0x64f5687dfcf4 in auto serac::finite_element<(mfem::Geometry::Type)1, serac::L2<1, 2> >::interpolate<2>(serac::tensor const&, serac::TensorProductQuadratureRule<2> const&) /home/sam/code/serac/src/serac/infrastructure/../../serac/numerics/functional/detail/segment_L2.inl:174:16 + #2 0x64f5687df6ca in void serac::interior_face_integral::evaluation_kernel_impl<2147483648u, 2, (mfem::Geometry::Type)1, serac::finite_element<(mfem::Geometry::Type)1, serac::L2<1, 2> >, serac::tuple > >, void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >)::'lambda'(double, auto, auto), serac::zero, 0>(serac::tuple > >, serac::finite_element<(mfem::Geometry::Type)1, serac::L2<1, 2> >, double, std::vector > const&, double*, double const*, double const*, void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >)::'lambda'(double, auto, auto), serac::zero*, unsigned int, camp::int_seq) /home/sam/code/serac/src/serac/infrastructure/../../serac/numerics/functional/interior_face_integral_kernels.hpp:175:9 + #3 0x64f5687df1fa in auto serac::interior_face_integral::evaluation_kernel<2147483648u, 2, (mfem::Geometry::Type)1, FunctionSignature (serac::L2<1, 2>)>, void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >)::'lambda'(double, auto, auto), serac::zero>(FunctionSignature (serac::L2<1, 2>)>, void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >)::'lambda'(double, auto, auto), double const*, double const*, std::shared_ptr, unsigned int)::'lambda'(double, std::vector > const&, double*, bool)::operator()(double, std::vector > const&, double*, bool) const /home/sam/code/serac/src/serac/infrastructure/../../serac/numerics/functional/interior_face_integral_kernels.hpp:347:5 + #4 0x64f5687def5a in auto std::__invoke_impl (serac::L2<1, 2>)>, void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >)::'lambda'(double, auto, auto), serac::zero>(FunctionSignature (serac::L2<1, 2>)>, void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >)::'lambda'(double, auto, auto), double const*, double const*, std::shared_ptr, unsigned int)::'lambda'(double, std::vector > const&, double*, bool)&, double, std::vector > const&, double*, bool>(std::__invoke_other, auto&&, double&&, std::vector > const&, double*&&, bool&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/invoke.h:61:14 + #5 0x64f5687dede0 in std::enable_if > const&, double*, bool>, auto>::type std::__invoke_r (serac::L2<1, 2>)>, void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >)::'lambda'(double, auto, auto), serac::zero>(FunctionSignature (serac::L2<1, 2>)>, void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >)::'lambda'(double, auto, auto), double const*, double const*, std::shared_ptr, unsigned int)::'lambda'(double, std::vector > const&, double*, bool)&, double, std::vector > const&, double*, bool>(auto&&, double&&, std::vector > const&, double*&&, bool&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/invoke.h:111:2 + #6 0x64f5687de9a0 in std::_Function_handler > const&, double*, bool), auto serac::interior_face_integral::evaluation_kernel<2147483648u, 2, (mfem::Geometry::Type)1, FunctionSignature (serac::L2<1, 2>)>, void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >)::'lambda'(double, auto, auto), serac::zero>(FunctionSignature (serac::L2<1, 2>)>, void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >)::'lambda'(double, auto, auto), double const*, double const*, std::shared_ptr, unsigned int)::'lambda'(double, std::vector > const&, double*, bool)>::_M_invoke(std::_Any_data const&, double&&, std::vector > const&, double*&&, bool&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/std_function.h:290:9 + #7 0x64f5687ff5d8 in std::function > const&, double*, bool)>::operator()(double, std::vector > const&, double*, bool) const /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/std_function.h:591:9 + #8 0x64f5687febee in serac::Integral::Mult(double, std::vector > const&, mfem::BlockVector&, unsigned int, bool) const /home/sam/code/serac/src/serac/infrastructure/../../serac/numerics/functional/integral.hpp:80:7 + #9 0x64f5687fe13c in serac::Functional (serac::L2<1, 2>), (serac::ExecutionSpace)0>::operator_paren_return<2147483648u>::type serac::Functional (serac::L2<1, 2>), (serac::ExecutionSpace)0>::operator()<2147483648u, mfem::Vector>(serac::DifferentiateWRT<2147483648u>, double, mfem::Vector const&) /home/sam/code/serac/src/serac/infrastructure/../../serac/numerics/functional/functional.hpp:466:16 + #10 0x64f5687abc73 in auto serac::Functional (serac::L2<1, 2>), (serac::ExecutionSpace)0>::operator()(double, mfem::Vector const&) /home/sam/code/serac/src/serac/infrastructure/../../serac/numerics/functional/functional.hpp:507:12 + #11 0x64f5687a8ac2 in void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >) /home/sam/code/serac/src/serac/numerics/functional/tests/functional_basic_dg.cpp:72:16 + #12 0x64f5687500b6 in basic_L2_test_tris_and_quads_linear_Test::TestBody() /home/sam/code/serac/src/serac/numerics/functional/tests/functional_basic_dg.cpp:77:46 + #13 0x64f5688d7b9a in void testing::internal::HandleSehExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2621:10 + #14 0x64f5688bb689 in void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2657:14 + #15 0x64f56889a7a2 in testing::Test::Run() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2696:5 + #16 0x64f56889b39f in testing::TestInfo::Run() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2845:11 + #17 0x64f56889bc1e in testing::TestSuite::Run() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:3023:30 + #18 0x64f5688ac960 in testing::internal::UnitTestImpl::RunAllTests() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:5926:44 + #19 0x64f5688dc01a in bool testing::internal::HandleSehExceptionsInMethodIfSupported(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2621:10 + #20 0x64f5688bdb59 in bool testing::internal::HandleExceptionsInMethodIfSupported(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2657:14 + #21 0x64f5688ac4ba in testing::UnitTest::Run() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:5492:10 + #22 0x64f568860490 in RUN_ALL_TESTS() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/include/gtest/gtest.h:2314:73 + #23 0x64f568750f48 in main /home/sam/code/serac/src/serac/numerics/functional/tests/functional_basic_dg.cpp:209:16 + #24 0x721e2c829d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 + #25 0x721e2c829e3f in __libc_start_main csu/../csu/libc-start.c:392:3 + #26 0x64f56868fda4 in _start (/home/sam/code/serac/build-Debug/tests/functional_basic_dg+0x980da4) (BuildId: 24bdc502972e3564795b90b83e63bc92533fcdae) + +0x6190000437d0 is located 0 bytes to the right of 1104-byte region [0x619000043380,0x6190000437d0) +allocated by thread T0 here: + #0 0x64f56874dacd in operator new[](unsigned long) (/home/sam/code/serac/build-Debug/tests/functional_basic_dg+0xa3eacd) (BuildId: 24bdc502972e3564795b90b83e63bc92533fcdae) + #1 0x64f5687afb1d in mfem::Memory::Alloc<16ul, true>::New(unsigned long) /home/sam/code/serac/mfem/linalg/../general/mem_manager.hpp:582:55 + #2 0x64f5687afae4 in mfem::Memory::NewHOST(unsigned long) /home/sam/code/serac/mfem/linalg/../general/mem_manager.hpp:596:14 + #3 0x64f5687b0f47 in mfem::Memory::New(int, mfem::MemoryType) /home/sam/code/serac/mfem/linalg/../general/mem_manager.hpp:955:44 + #4 0x64f5687b8064 in mfem::Vector::SetSize(int) /home/sam/code/serac/mfem/mesh/../linalg/vector.hpp:554:9 + #5 0x64f5687fdece in serac::Functional (serac::L2<1, 2>), (serac::ExecutionSpace)0>::operator_paren_return<2147483648u>::type serac::Functional (serac::L2<1, 2>), (serac::ExecutionSpace)0>::operator()<2147483648u, mfem::Vector>(serac::DifferentiateWRT<2147483648u>, double, mfem::Vector const&) /home/sam/code/serac/src/serac/infrastructure/../../serac/numerics/functional/functional.hpp:459:28 + #6 0x64f5687abc73 in auto serac::Functional (serac::L2<1, 2>), (serac::ExecutionSpace)0>::operator()(double, mfem::Vector const&) /home/sam/code/serac/src/serac/infrastructure/../../serac/numerics/functional/functional.hpp:507:12 + #7 0x64f5687a8ac2 in void L2_test<2, 1>(std::__cxx11::basic_string, std::allocator >) /home/sam/code/serac/src/serac/numerics/functional/tests/functional_basic_dg.cpp:72:16 + #8 0x64f5687500b6 in basic_L2_test_tris_and_quads_linear_Test::TestBody() /home/sam/code/serac/src/serac/numerics/functional/tests/functional_basic_dg.cpp:77:46 + #9 0x64f5688d7b9a in void testing::internal::HandleSehExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2621:10 + #10 0x64f5688bb689 in void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*) /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2657:14 + #11 0x64f56889a7a2 in testing::Test::Run() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2696:5 + #12 0x64f56889b39f in testing::TestInfo::Run() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2845:11 + #13 0x64f56889bc1e in testing::TestSuite::Run() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:3023:30 + #14 0x64f5688ac960 in testing::internal::UnitTestImpl::RunAllTests() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:5926:44 + #15 0x64f5688dc01a in bool testing::internal::HandleSehExceptionsInMethodIfSupported(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2621:10 + #16 0x64f5688bdb59 in bool testing::internal::HandleExceptionsInMethodIfSupported(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:2657:14 + #17 0x64f5688ac4ba in testing::UnitTest::Run() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/src/gtest.cc:5492:10 + #18 0x64f568860490 in RUN_ALL_TESTS() /home/sam/code/serac/cmake/blt/thirdparty_builtin/googletest/googletest/include/gtest/gtest.h:2314:73 + #19 0x64f568750f48 in main /home/sam/code/serac/src/serac/numerics/functional/tests/functional_basic_dg.cpp:209:16 + #20 0x721e2c829d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16 + +SUMMARY: AddressSanitizer: heap-buffer-overflow /home/sam/code/serac/src/serac/infrastructure/../../serac/numerics/functional/tensor.hpp:765:23 in auto serac::dot(serac::tensor const&, serac::tensor const&) +Shadow bytes around the buggy address: + 0x0c32800006a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0c32800006b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0c32800006c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0c32800006d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0c32800006e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +=>0x0c32800006f0: 00 00 00 00 00 00 00 00 00 00[fa]fa fa fa fa fa + 0x0c3280000700: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa + 0x0c3280000710: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0c3280000720: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0c3280000730: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x0c3280000740: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Shadow byte legend (one shadow byte represents 8 application bytes): + Addressable: 00 + Partially addressable: 01 02 03 04 05 06 07 + Heap left redzone: fa + Freed heap region: fd + Stack left redzone: f1 + Stack mid redzone: f2 + Stack right redzone: f3 + Stack after return: f5 + Stack use after scope: f8 + Global redzone: f9 + Global init order: f6 + Poisoned by user: f7 + Container overflow: fc + Array cookie: ac + Intra object redzone: bb + ASan internal: fe + Left alloca redzone: ca + Right alloca redzone: cb +==558615==ABORTING diff --git a/src/serac/numerics/functional/functional.hpp b/src/serac/numerics/functional/functional.hpp index cfaca8c88b..e4af6a876c 100644 --- a/src/serac/numerics/functional/functional.hpp +++ b/src/serac/numerics/functional/functional.hpp @@ -606,7 +606,6 @@ class Functional { return df_; } -#if 1 void initialize_sparsity_pattern() { using row_col = std::tuple; @@ -778,108 +777,6 @@ class Functional { return A; }; -#else - /// @brief assemble element matrices and form an mfem::HypreParMatrix - std::unique_ptr assemble() - { - // the CSR graph (sparsity pattern) is reusable, so we cache - // that and ask mfem to not free that memory in ~SparseMatrix() - constexpr bool sparse_matrix_frees_graph_ptrs = false; - - // the CSR values are NOT reusable, so we pass ownership of - // them to the mfem::SparseMatrix, to be freed in ~SparseMatrix() - constexpr bool sparse_matrix_frees_values_ptr = true; - - constexpr bool col_ind_is_sorted = true; - - if (!lookup_tables.initialized) { - lookup_tables.init(form_.G_test_[Domain::Type::Elements], - form_.G_trial_[Domain::Type::Elements][which_argument]); - } - - double* values = new double[lookup_tables.nnz]{}; - - std::map> element_gradients[Domain::num_types]; - - for (auto& integral : form_.integrals_) { - auto& K_elem = element_gradients[integral.domain_.type_]; - auto& test_restrictions = form_.G_test_[integral.domain_.type_].restrictions; - auto& trial_restrictions = form_.G_trial_[integral.domain_.type_][which_argument].restrictions; - - if (K_elem.empty()) { - for (auto& [geom, test_restriction] : test_restrictions) { - auto& trial_restriction = trial_restrictions[geom]; - - K_elem[geom] = ExecArray(test_restriction.num_elements, - trial_restriction.nodes_per_elem * trial_restriction.components, - test_restriction.nodes_per_elem * test_restriction.components); - - detail::zero_out(K_elem[geom]); - } - } - - integral.ComputeElementGradients(K_elem, which_argument); - } - - for (auto type : {Domain::Type::Elements, Domain::Type::BoundaryElements}) { - auto& K_elem = element_gradients[type]; - auto& test_restrictions = form_.G_test_[type].restrictions; - auto& trial_restrictions = form_.G_trial_[type][which_argument].restrictions; - - if (!K_elem.empty()) { - for (auto [geom, elem_matrices] : K_elem) { - std::vector test_vdofs(test_restrictions[geom].nodes_per_elem * test_restrictions[geom].components); - std::vector trial_vdofs(trial_restrictions[geom].nodes_per_elem * trial_restrictions[geom].components); - - for (axom::IndexType e = 0; e < elem_matrices.shape()[0]; e++) { - test_restrictions[geom].GetElementVDofs(e, test_vdofs); - trial_restrictions[geom].GetElementVDofs(e, trial_vdofs); - - for (uint32_t i = 0; i < uint32_t(elem_matrices.shape()[1]); i++) { - int col = int(trial_vdofs[i].index()); - - for (uint32_t j = 0; j < uint32_t(elem_matrices.shape()[2]); j++) { - int row = int(test_vdofs[j].index()); - - int sign = test_vdofs[j].sign() * trial_vdofs[i].sign(); - - // note: col / row appear backwards here, because the element matrix kernel - // is actually transposed, as a result of being row-major storage. - // - // This is kind of confusing, and will be fixed in a future refactor - // of the element gradient kernel implementation - [[maybe_unused]] auto nz = lookup_tables(row, col); - values[lookup_tables(row, col)] += sign * elem_matrices(e, i, j); - } - } - } - } - } - } - - // Copy the column indices to an auxilliary array as MFEM can mutate these during HypreParMatrix construction - col_ind_copy_ = lookup_tables.col_ind; - - auto J_local = - mfem::SparseMatrix(lookup_tables.row_ptr.data(), col_ind_copy_.data(), values, form_.output_L_.Size(), - form_.input_L_[which_argument].Size(), sparse_matrix_frees_graph_ptrs, - sparse_matrix_frees_values_ptr, col_ind_is_sorted); - - auto* R = form_.test_space_->Dof_TrueDof_Matrix(); - - auto* A = - new mfem::HypreParMatrix(test_space_->GetComm(), test_space_->GlobalVSize(), trial_space_->GlobalVSize(), - test_space_->GetDofOffsets(), trial_space_->GetDofOffsets(), &J_local); - - auto* P = trial_space_->Dof_TrueDof_Matrix(); - - std::unique_ptr K(mfem::RAP(R, A, P)); - - delete A; - - return K; - }; -#endif friend auto assemble(Gradient& g) { return g.assemble(); } diff --git a/src/serac/numerics/functional/geometric_factors.cpp b/src/serac/numerics/functional/geometric_factors.cpp index f286783470..55c906c7f9 100644 --- a/src/serac/numerics/functional/geometric_factors.cpp +++ b/src/serac/numerics/functional/geometric_factors.cpp @@ -209,88 +209,4 @@ GeometricFactors::GeometricFactors(const Domain& domain, int q, mfem::Geometry:: std::cout << "should never be reached " << std::endl; } -#if 0 -GeometricFactors::GeometricFactors(const Domain& d, int q, mfem::Geometry::Type g, FaceType type) -{ - auto* nodes = d.mesh_.GetNodes(); - auto* fes = nodes->FESpace(); - - auto restriction = serac::ElementRestriction(fes, g, type); - mfem::Vector X_e(int(restriction.ESize())); - restriction.Gather(*nodes, X_e); - - // assumes all elements are the same order - int p = fes->GetElementOrder(0); - - int spatial_dim = d.mesh_.SpaceDimension(); - int geometry_dim = dimension_of(g); - int qpts_per_elem = num_quadrature_points(g, q); - - // NB: we only want the number of elements with the specified - // geometry, which is not the same as mesh->GetNE() in general - elements = d.get(g); - - num_elements = std::size_t(elements.size()); - - X = mfem::Vector(int(num_elements) * qpts_per_elem * spatial_dim); - J = mfem::Vector(int(num_elements) * qpts_per_elem * spatial_dim * geometry_dim); - -#define DISPATCH_KERNEL(GEOM, P, Q) \ - if (g == mfem::Geometry::GEOM && p == P && q == Q) { \ - compute_geometric_factors >(X, J, X_e, \ - elements); \ - return; \ - } - - DISPATCH_KERNEL(SEGMENT, 1, 1); - DISPATCH_KERNEL(SEGMENT, 1, 2); - DISPATCH_KERNEL(SEGMENT, 1, 3); - DISPATCH_KERNEL(SEGMENT, 1, 4); - - DISPATCH_KERNEL(SEGMENT, 2, 1); - DISPATCH_KERNEL(SEGMENT, 2, 2); - DISPATCH_KERNEL(SEGMENT, 2, 3); - DISPATCH_KERNEL(SEGMENT, 2, 4); - - DISPATCH_KERNEL(SEGMENT, 3, 1); - DISPATCH_KERNEL(SEGMENT, 3, 2); - DISPATCH_KERNEL(SEGMENT, 3, 3); - DISPATCH_KERNEL(SEGMENT, 3, 4); - - DISPATCH_KERNEL(TRIANGLE, 1, 1); - DISPATCH_KERNEL(TRIANGLE, 1, 2); - DISPATCH_KERNEL(TRIANGLE, 1, 3); - DISPATCH_KERNEL(TRIANGLE, 1, 4); - - DISPATCH_KERNEL(TRIANGLE, 2, 1); - DISPATCH_KERNEL(TRIANGLE, 2, 2); - DISPATCH_KERNEL(TRIANGLE, 2, 3); - DISPATCH_KERNEL(TRIANGLE, 2, 4); - - DISPATCH_KERNEL(TRIANGLE, 3, 1); - DISPATCH_KERNEL(TRIANGLE, 3, 2); - DISPATCH_KERNEL(TRIANGLE, 3, 3); - DISPATCH_KERNEL(TRIANGLE, 3, 4); - - DISPATCH_KERNEL(SQUARE, 1, 1); - DISPATCH_KERNEL(SQUARE, 1, 2); - DISPATCH_KERNEL(SQUARE, 1, 3); - DISPATCH_KERNEL(SQUARE, 1, 4); - - DISPATCH_KERNEL(SQUARE, 2, 1); - DISPATCH_KERNEL(SQUARE, 2, 2); - DISPATCH_KERNEL(SQUARE, 2, 3); - DISPATCH_KERNEL(SQUARE, 2, 4); - - DISPATCH_KERNEL(SQUARE, 3, 1); - DISPATCH_KERNEL(SQUARE, 3, 2); - DISPATCH_KERNEL(SQUARE, 3, 3); - DISPATCH_KERNEL(SQUARE, 3, 4); - -#undef DISPATCH_KERNEL - - std::cout << "should never be reached" << std::endl; -} -#endif - } // namespace serac diff --git a/src/serac/numerics/functional/integral.hpp b/src/serac/numerics/functional/integral.hpp index 16ab1bb489..9444ba2156 100644 --- a/src/serac/numerics/functional/integral.hpp +++ b/src/serac/numerics/functional/integral.hpp @@ -19,9 +19,6 @@ #include "serac/numerics/functional/interior_face_integral_kernels.hpp" #include "serac/numerics/functional/differentiate_wrt.hpp" -// TODO REMOVE AFTER DEBUGGING -#include "serac/infrastructure/mpi_fstream.hpp" - namespace serac { /// @brief a class for representing a Integral calculations and their derivatives diff --git a/src/serac/numerics/functional/tests/CMakeLists.txt b/src/serac/numerics/functional/tests/CMakeLists.txt index 14d1a5a744..62bf13048a 100644 --- a/src/serac/numerics/functional/tests/CMakeLists.txt +++ b/src/serac/numerics/functional/tests/CMakeLists.txt @@ -22,7 +22,6 @@ set(functional_serial_test_sources dg_restriction_operators.cpp functional_basic_dg.cpp bug_boundary_qoi.cpp - bug_residual.cpp domain_tests.cpp geometric_factors_tests.cpp hcurl_unit_tests.cpp diff --git a/src/serac/numerics/functional/tests/bug_residual.cpp b/src/serac/numerics/functional/tests/bug_residual.cpp deleted file mode 100644 index 4f393facd4..0000000000 --- a/src/serac/numerics/functional/tests/bug_residual.cpp +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2019-2023, Lawrence Livermore National Security, LLC and -// other Serac Project Developers. See the top-level LICENSE file for -// details. -// -// SPDX-License-Identifier: (BSD-3-Clause) - -#include -#include - -#include "axom/slic/core/SimpleLogger.hpp" -#include -#include "mfem.hpp" - -#include "serac/serac_config.hpp" -#include "serac/mesh/mesh_utils_base.hpp" -#include "serac/numerics/functional/functional.hpp" -#include "serac/numerics/functional/shape_aware_functional.hpp" -#include "serac/numerics/functional/tensor.hpp" -#include "serac/infrastructure/profiling.hpp" - -#include "serac/numerics/functional/tests/check_gradient.hpp" - -using namespace serac; -using namespace serac::profiling; - -double t = 0.0; - -int num_procs, myid; - -#define L2_SCALAR_SPACE - -// this is an attempt to reproduce an error message described by Mike -void test() -{ - constexpr int p = 1; - constexpr int dim = 2; - -#ifdef L2_SCALAR_SPACE - using scalar_space = serac::L2; -#else - using scalar_space = serac::H1; -#endif - using vector_space = serac::H1; - - std::string meshfile = SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"; - auto pmesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 1); - - auto L2fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); - mfem::ParFiniteElementSpace L2_fespace(pmesh.get(), &L2fec, 1, serac::ordering); - -#ifdef L2_SCALAR_SPACE - auto scalar_fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); -#else - auto scalar_fec = mfem::H1_FECollection(p, dim); -#endif - mfem::ParFiniteElementSpace scalar_fespace(pmesh.get(), &scalar_fec, 1, serac::ordering); - - auto vector_fec = mfem::H1_FECollection(p, dim); - mfem::ParFiniteElementSpace vector_fespace(pmesh.get(), &vector_fec, dim, serac::ordering); - - Domain whole_domain = EntireDomain(*pmesh); - - Functional residual( - &scalar_fespace, {&scalar_fespace, &scalar_fespace, &vector_fespace, &vector_fespace}); - - residual.AddDomainIntegral( - serac::Dimension{}, serac::DependsOn<0, 1, 2, 3>{}, - [=](double time, auto /*X*/, auto Rho, auto Rho_dot, auto U0, auto UF) { - auto U = UF * time + U0 * (1.0 - time); - auto dx_dX = get(U) + Identity(); - auto dX_dx = inv(dx_dX); - - auto v = get(UF) - get(U0); - auto v_X = get(UF) - get(U0); - auto v_dx = dot(v_X, dX_dx); - auto div_v = serac::tr(v_dx); - - auto rho_dot = get(Rho_dot); - auto rho = get(Rho); - - auto J = det(dx_dX); - auto JrhoV = J * dot(v, transpose(dX_dx)); - // std::string y = rho; - // std::string z = rho*JrhoV; - return serac::tuple{J * (rho_dot + rho * div_v), rho * JrhoV}; - }, - whole_domain); -} - -int main(int argc, char* argv[]) -{ - ::testing::InitGoogleTest(&argc, argv); - MPI_Init(&argc, &argv); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - MPI_Comm_rank(MPI_COMM_WORLD, &myid); - - axom::slic::SimpleLogger logger; - - int result = RUN_ALL_TESTS(); - MPI_Finalize(); - return result; -} diff --git a/src/serac/numerics/functional/tests/cuda_sandbox.cu b/src/serac/numerics/functional/tests/cuda_sandbox.cu deleted file mode 100644 index 7d6ddaf1c9..0000000000 --- a/src/serac/numerics/functional/tests/cuda_sandbox.cu +++ /dev/null @@ -1,244 +0,0 @@ -#include - -#include "serac/numerics/functional/tensor.hpp" - -namespace serac { - -enum class Family -{ - H1, - Hcurl, - Hdiv, - DG -}; - -template -struct FiniteElement; - -template <> -struct FiniteElement { - using source_type = double; - using flux_type = vec2; - - static constexpr int dim = 2; - - uint32_t num_nodes() const { return ((p + 1) * (p + 2)) / 2; } - - constexpr double shape_function(vec2 xi, uint32_t i) const - { - if (p == 1) { - if (i == 0) return 1.0 - xi[0] - xi[1]; - if (i == 1) return xi[0]; - if (i == 2) return xi[1]; - } - if (p == 2) { - if (i == 0) return (-1 + xi[0] + xi[1]) * (-1 + 2 * xi[0] + 2 * xi[1]); - if (i == 1) return -4 * xi[0] * (-1 + xi[0] + xi[1]); - if (i == 2) return xi[0] * (-1 + 2 * xi[0]); - if (i == 3) return -4 * xi[1] * (-1 + xi[0] + xi[1]); - if (i == 4) return 4 * xi[0] * xi[1]; - if (i == 5) return xi[1] * (-1 + 2 * xi[1]); - } - if (p == 3) { - double sqrt5 = 2.23606797749978981; - if (i == 0) - return -((-1 + xi[0] + xi[1]) * (1 + 5 * xi[0] * xi[0] + 5 * (-1 + xi[1]) * xi[1] + xi[0] * (-5 + 11 * xi[1]))); - if (i == 1) - return (5 * xi[0] * (-1 + xi[0] + xi[1]) * (-1 - sqrt5 + 2 * sqrt5 * xi[0] + (3 + sqrt5) * xi[1])) / 2.0; - if (i == 2) - return (-5 * xi[0] * (-1 + xi[0] + xi[1]) * (1 - sqrt5 + 2 * sqrt5 * xi[0] + (-3 + sqrt5) * xi[1])) / 2.0; - if (i == 3) return xi[0] * (1 + 5 * xi[0] * xi[0] + xi[1] - xi[1] * xi[1] - xi[0] * (5 + xi[1])); - if (i == 4) - return (5 * xi[1] * (-1 + xi[0] + xi[1]) * (-1 - sqrt5 + (3 + sqrt5) * xi[0] + 2 * sqrt5 * xi[1])) / 2.0; - if (i == 5) return -27 * xi[0] * xi[1] * (-1 + xi[0] + xi[1]); - if (i == 6) return (5 * xi[0] * xi[1] * (-2 + (3 + sqrt5) * xi[0] - (-3 + sqrt5) * xi[1])) / 2.; - if (i == 7) - return (5 * xi[1] * (-1 + xi[0] + xi[1]) * - (5 - 3 * sqrt5 + 2 * (-5 + 2 * sqrt5) * xi[0] + 5 * (-1 + sqrt5) * xi[1])) / - (-5 + sqrt5); - if (i == 8) return (-5 * xi[0] * xi[1] * (2 + (-3 + sqrt5) * xi[0] - (3 + sqrt5) * xi[1])) / 2.; - if (i == 9) return xi[1] * (1 + xi[0] - xi[0] * xi[0] - xi[0] * xi[1] + 5 * (-1 + xi[1]) * xi[1]); - } - - return -1.0; - } - - vec2 shape_function_gradient(vec2 xi, uint32_t i) const - { - // expressions generated symbolically by mathematica - if (p == 1) { - if (i == 0) return {-1.0, -1.0}; - if (i == 1) return {1.0, 0.0}; - if (i == 2) return {0.0, 1.0}; - } - if (p == 2) { - if (i == 0) return {-3 + 4 * xi[0] + 4 * xi[1], -3 + 4 * xi[0] + 4 * xi[1]}; - if (i == 1) return {-4 * (-1 + 2 * xi[0] + xi[1]), -4 * xi[0]}; - if (i == 2) return {-1 + 4 * xi[0], 0}; - if (i == 3) return {-4 * xi[1], -4 * (-1 + xi[0] + 2 * xi[1])}; - if (i == 4) return {4 * xi[1], 4 * xi[0]}; - if (i == 5) return {0, -1 + 4 * xi[1]}; - } - if (p == 3) { - double sqrt5 = 2.23606797749978981; - if (i == 0) - return {-6 - 15 * xi[0] * xi[0] + 4 * xi[0] * (5 - 8 * xi[1]) + (21 - 16 * xi[1]) * xi[1], - -6 - 16 * xi[0] * xi[0] + xi[0] * (21 - 32 * xi[1]) + 5 * (4 - 3 * xi[1]) * xi[1]}; - if (i == 1) - return {(5 * (6 * sqrt5 * xi[0] * xi[0] + xi[0] * (-2 - 6 * sqrt5 + 6 * (1 + sqrt5) * xi[1]) + - (-1 + xi[1]) * (-1 - sqrt5 + (3 + sqrt5) * xi[1]))) / - 2., - (5 * xi[0] * (-2 * (2 + sqrt5) + 3 * (1 + sqrt5) * xi[0] + 2 * (3 + sqrt5) * xi[1])) / 2.}; - if (i == 2) - return {(-5 * (6 * sqrt5 * xi[0] * xi[0] + (-1 + xi[1]) * (1 - sqrt5 + (-3 + sqrt5) * xi[1]) + - xi[0] * (2 - 6 * sqrt5 + 6 * (-1 + sqrt5) * xi[1]))) / - 2., - (-5 * xi[0] * (4 - 2 * sqrt5 + 3 * (-1 + sqrt5) * xi[0] + 2 * (-3 + sqrt5) * xi[1])) / 2.}; - if (i == 3) - return {1 + 15 * xi[0] * xi[0] + xi[1] - xi[1] * xi[1] - 2 * xi[0] * (5 + xi[1]), - -(xi[0] * (-1 + xi[0] + 2 * xi[1]))}; - if (i == 4) - return {(5 * xi[1] * (-2 * (2 + sqrt5) + 2 * (3 + sqrt5) * xi[0] + 3 * (1 + sqrt5) * xi[1])) / 2., - (5 * (1 + sqrt5 - 2 * (2 + sqrt5) * xi[0] + (3 + sqrt5) * xi[0] * xi[0] + - 6 * (1 + sqrt5) * xi[0] * xi[1] + 2 * xi[1] * (-1 - 3 * sqrt5 + 3 * sqrt5 * xi[1]))) / - 2.}; - if (i == 5) return {-27 * xi[1] * (-1 + 2 * xi[0] + xi[1]), -27 * xi[0] * (-1 + xi[0] + 2 * xi[1])}; - if (i == 6) - return {(-5 * xi[1] * (2 - 2 * (3 + sqrt5) * xi[0] + (-3 + sqrt5) * xi[1])) / 2., - (5 * xi[0] * (-2 + (3 + sqrt5) * xi[0] - 2 * (-3 + sqrt5) * xi[1])) / 2.}; - if (i == 7) - return {(-5 * xi[1] * (4 - 2 * sqrt5 + 2 * (-3 + sqrt5) * xi[0] + 3 * (-1 + sqrt5) * xi[1])) / 2., - (-5 * (-1 + sqrt5 + (-3 + sqrt5) * xi[0] * xi[0] + 2 * xi[1] * (1 - 3 * sqrt5 + 3 * sqrt5 * xi[1]) + - xi[0] * (4 - 2 * sqrt5 + 6 * (-1 + sqrt5) * xi[1]))) / - 2.}; - if (i == 8) - return {(5 * xi[1] * (-2 - 2 * (-3 + sqrt5) * xi[0] + (3 + sqrt5) * xi[1])) / 2., - (-5 * xi[0] * (2 + (-3 + sqrt5) * xi[0] - 2 * (3 + sqrt5) * xi[1])) / 2.}; - if (i == 9) - return {-(xi[1] * (-1 + 2 * xi[0] + xi[1])), - 1 + xi[0] - xi[0] * xi[0] - 2 * (5 + xi[0]) * xi[1] + 15 * xi[1] * xi[1]}; - } - - return {}; - } - -#if 0 - nd::array< double, 2 > evaluate_shape_functions(nd::view xi) const { - uint32_t q = xi.shape[0]; - nd::array shape_fns({q, num_nodes()}); - for (int i = 0; i < q; i++) { - GaussLobattoInterpolationTriangle(&xi(i, 0), p, &shape_fns(i, 0)); - } - return shape_fns; - } - - nd::array< double, 3 > evaluate_shape_function_gradients(nd::view xi) const { - uint32_t q = xi.shape[0]; - nd::array shape_fn_grads({q, num_nodes(), dim}); - for (int i = 0; i < q; i++) { - GaussLobattoInterpolationDerivativeTriangle(&xi(i, 0), p, &shape_fn_grads(i, 0, 0)); - } - return shape_fn_grads; - } - - void interpolate(nd::view values_q, nd::view values_e, nd::view shape_fns) const { - int nnodes = num_nodes(); - int nqpts = values_q.shape[0]; - - for (int q = 0; q < nqpts; q++) { - double sum = 0.0; - for (int i = 0; i < nnodes; i++) { - sum += shape_fns(q, i) * values_e(i); - } - values_q(q) = sum; - } - } - - void gradient(nd::view gradients_q, nd::view values_e, nd::view shape_fn_grads) const { - int nnodes = num_nodes(); - int nqpts = gradients_q.shape[0]; - for (int j = 0; j < nqpts; j++) { - double sum[2]{}; - for (int i = 0; i < nnodes; i++) { - sum[0] += values_e(i) * shape_fn_grads(j, i, 0); - sum[1] += values_e(i) * shape_fn_grads(j, i, 1); - } - gradients_q(j, 0) = sum[0]; - gradients_q(j, 1) = sum[1]; - - } - } - - nd::array< double, 2 > evaluate_weighted_shape_functions(nd::view xi, - nd::view weights) const { - uint32_t nnodes = num_nodes(); - uint32_t q = xi.shape[0]; - nd::array shape_fns({q, nnodes}); - for (uint32_t i = 0; i < q; i++) { - GaussLobattoInterpolationTriangle(&xi(i, 0), p, &shape_fns(i, 0)); - for (uint32_t j = 0; j < nnodes; j++) { - shape_fns(i, j) = shape_fns(i, j) * weights(i); - } - } - return shape_fns; - } - - void integrate_source(nd::view residual_e, nd::view source_q, nd::view shape_fn) const { - int nnodes = num_nodes(); - int nqpts = source_q.shape[0]; - - for (int i = 0; i < nnodes; i++) { - double sum = 0.0; - for (int q = 0; q < nqpts; q++) { - sum += shape_fn(q, i) * source_q(q); - } - residual_e(i) = sum; - } - } - - nd::array< double, 3 > evaluate_weighted_shape_function_gradients(nd::view xi, - nd::view weights) const { - uint32_t nnodes = num_nodes(); - uint32_t q = xi.shape[0]; - nd::array shape_fn_grads({q, nnodes, dim}); - for (uint32_t i = 0; i < q; i++) { - GaussLobattoInterpolationDerivativeTriangle(&xi(i, 0), p, &shape_fn_grads(i, 0, 0)); - for (uint32_t j = 0; j < nnodes; j++) { - shape_fn_grads(i, j, 0) = shape_fn_grads(i, j, 0) * weights(i); - shape_fn_grads(i, j, 1) = shape_fn_grads(i, j, 1) * weights(i); - } - } - - return shape_fn_grads; - } - - void integrate_flux(nd::view residual_e, nd::view flux_q, nd::view shape_fn_grads) const { - int nnodes = num_nodes(); - int nqpts = flux_q.shape[0]; - - for (int i = 0; i < nnodes; i++) { - double sum = 0.0; - for (int q = 0; q < nqpts; q++) { - for (int d = 0; d < dim; d++) { - sum += shape_fn_grads(q, i, d) * flux_q(q)[d]; - } - } - residual_e(i) = sum; - } - } -#endif - - int p; -}; - -} // namespace serac - -//#include "elements/h1_edge.hpp" -//#include "elements/h1_triangle.hpp" -//#include "elements/h1_quadrilateral.hpp" -//#include "elements/h1_tetrahedron.hpp" -//#include "elements/h1_hexahedron.hpp" - -__global__ void kernel() {} - -int main() { kernel<<<1, 1>>>(); } diff --git a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp index 67775dbeb9..74cadd1a1e 100644 --- a/src/serac/numerics/functional/tests/dg_restriction_operators.cpp +++ b/src/serac/numerics/functional/tests/dg_restriction_operators.cpp @@ -329,11 +329,7 @@ void parametrized_test(int permutation) auto H1_dofs = GetFaceDofs(H1_fes.get(), face_geom, FaceType::INTERIOR); auto Hcurl_dofs = GetFaceDofs(Hcurl_fes.get(), face_geom, FaceType::INTERIOR); -#if 0 - auto L2_dofs = GetFaceDofs(L2_fes.get(), face_geom, FaceType::INTERIOR); -#else auto L2_dofs = GetFaceDofs(L2_fes.get(), face_geom, face_ids); -#endif // verify that the dofs for the L2 faces are aligned properly int dofs_per_side = L2_dofs.shape()[1] / 2; @@ -387,7 +383,6 @@ void parametrized_test(int permutation) //////////////////////////////////////////////////////////////////////////////// TEST(DomainInterior, TriMesh10) { parametrized_test(0); } -#if 1 TEST(DomainInterior, TriMesh11) { parametrized_test(1); } TEST(DomainInterior, TriMesh12) { parametrized_test(2); } @@ -533,7 +528,6 @@ TEST(DomainInterior, HexMesh320) { parametrized_test(20 TEST(DomainInterior, HexMesh321) { parametrized_test(21); } TEST(DomainInterior, HexMesh322) { parametrized_test(22); } TEST(DomainInterior, HexMesh323) { parametrized_test(23); } -#endif //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// diff --git a/src/serac/numerics/functional/tests/functional_basic_dg.cpp b/src/serac/numerics/functional/tests/functional_basic_dg.cpp index 4bd174580d..54398503de 100644 --- a/src/serac/numerics/functional/tests/functional_basic_dg.cpp +++ b/src/serac/numerics/functional/tests/functional_basic_dg.cpp @@ -24,76 +24,6 @@ using namespace serac; using namespace serac::profiling; -// -// patch2D_tris_and_quads.mesh -// -// o---------------------o -// | * 0 * | -// | * * | -// | o ----- o | -// | 0 | 0 . ^ | | -// | | * 1 | 1 | -// | o ----- o | -// | * * | -// | * 1 * | -// o---------------------o -// - -// [ --- L --- | -- FND -- ] -// ^^ - -#if 0 -void ParNonlinearForm::Mult(const Vector &x, Vector &y) const -{ - NonlinearForm::Mult(x, y); // x --(P)--> aux1 --(A_local)--> aux2 - - if (fnfi.Size()) - { - MFEM_VERIFY(!NonlinearForm::ext, "Not implemented (extensions + faces"); - // Terms over shared interior faces in parallel. - ParFiniteElementSpace *pfes = ParFESpace(); - ParMesh *pmesh = pfes->GetParMesh(); - FaceElementTransformations *tr; - const FiniteElement *fe1, *fe2; - Array vdofs1, vdofs2; - Vector el_x, el_y; - - aux1.HostReadWrite(); - X.MakeRef(aux1, 0); // aux1 contains P.x - X.ExchangeFaceNbrData(); - const int n_shared_faces = pmesh->GetNSharedFaces(); - for (int i = 0; i < n_shared_faces; i++) - { - tr = pmesh->GetSharedFaceTransformations(i, true); - int Elem2NbrNo = tr->Elem2No - pmesh->GetNE(); - - fe1 = pfes->GetFE(tr->Elem1No); - fe2 = pfes->GetFaceNbrFE(Elem2NbrNo); - - pfes->GetElementVDofs(tr->Elem1No, vdofs1); - pfes->GetFaceNbrElementVDofs(Elem2NbrNo, vdofs2); - - el_x.SetSize(vdofs1.Size() + vdofs2.Size()); - X.GetSubVector(vdofs1, el_x.GetData()); - X.FaceNbrData().GetSubVector(vdofs2, el_x.GetData() + vdofs1.Size()); - - for (int k = 0; k < fnfi.Size(); k++) - { - fnfi[k]->AssembleFaceVector(*fe1, *fe2, *tr, el_x, el_y); - aux2.AddElementVector(vdofs1, el_y.GetData()); - } - } - } - - P->MultTranspose(aux2, y); - - const int N = ess_tdof_list.Size(); - const auto idx = ess_tdof_list.Read(); - auto Y_RW = y.ReadWrite(); - mfem::forall(N, [=] MFEM_HOST_DEVICE (int i) { Y_RW[idx[i]] = 0.0; }); -} -#endif - template void L2_test(std::string meshfile) { @@ -145,8 +75,6 @@ void L2_test(std::string meshfile) } TEST(basic, L2_test_tris_and_quads_linear) { L2_test<2, 1>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); } - -#if 0 TEST(basic, L2_test_tris_and_quads_quadratic) { L2_test<2, 2>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); } TEST(basic, L2_test_tets_linear) { L2_test<3, 1>(SERAC_REPO_DIR "/data/meshes/patch3D_tets.mesh"); } @@ -263,7 +191,6 @@ void L2_scalar_valued_test(std::string meshfile) TEST(basic, L2_mixed_scalar_test_tris_and_quads_linear) { L2_scalar_valued_test<2, 1>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); } TEST(basic, L2_mixed_scalar_test_tets_and_hexes_linear) { L2_scalar_valued_test<3, 1>(SERAC_REPO_DIR "/data/meshes/patch3D_tets_and_hexes.mesh"); } -#endif int main(int argc, char* argv[]) { From 586ede56582ca8b31b1ac6c0aca986eeec7be46a Mon Sep 17 00:00:00 2001 From: Alex Tyler Chapman Date: Wed, 4 Dec 2024 15:48:59 -0800 Subject: [PATCH 81/92] use project source dir --- src/drivers/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/drivers/CMakeLists.txt b/src/drivers/CMakeLists.txt index 5c213cb306..b29b48804f 100644 --- a/src/drivers/CMakeLists.txt +++ b/src/drivers/CMakeLists.txt @@ -11,7 +11,7 @@ blt_add_executable( NAME serac_driver ) if (SERAC_ENABLE_TESTS) - set(input_files_dir ${CMAKE_CURRENT_SOURCE_DIR}/../../data/input_files/tests) + set(input_files_dir ${PROJECT_SOURCE_DIR}/data/input_files/tests) # Run basic test for the Serac driver blt_add_test(NAME serac_driver_solid From 9db8634d825387c190d423f95f8a408bb90b0f3c Mon Sep 17 00:00:00 2001 From: Agent Style Date: Wed, 4 Dec 2024 16:07:45 -0800 Subject: [PATCH 82/92] Apply style updates --- .../functional/tests/functional_basic_dg.cpp | 70 +++++++++++-------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/src/serac/numerics/functional/tests/functional_basic_dg.cpp b/src/serac/numerics/functional/tests/functional_basic_dg.cpp index 54398503de..6f5c205ae6 100644 --- a/src/serac/numerics/functional/tests/functional_basic_dg.cpp +++ b/src/serac/numerics/functional/tests/functional_basic_dg.cpp @@ -75,7 +75,10 @@ void L2_test(std::string meshfile) } TEST(basic, L2_test_tris_and_quads_linear) { L2_test<2, 1>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); } -TEST(basic, L2_test_tris_and_quads_quadratic) { L2_test<2, 2>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); } +TEST(basic, L2_test_tris_and_quads_quadratic) +{ + L2_test<2, 2>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); +} TEST(basic, L2_test_tets_linear) { L2_test<3, 1>(SERAC_REPO_DIR "/data/meshes/patch3D_tets.mesh"); } TEST(basic, L2_test_tets_quadratic) { L2_test<3, 2>(SERAC_REPO_DIR "/data/meshes/patch3D_tets.mesh"); } @@ -90,11 +93,11 @@ void L2_qoi_test(std::string meshfile) auto mesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 1); - auto fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); + auto fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); mfem::ParFiniteElementSpace fespace(mesh.get(), &fec, dim, serac::ordering); - int seed = 0; - mfem::HypreParVector U = *fespace.NewTrueDofVector(); + int seed = 0; + mfem::HypreParVector U = *fespace.NewTrueDofVector(); U.Randomize(seed); // Construct the new functional object using the specified test and trial spaces @@ -105,31 +108,37 @@ void L2_qoi_test(std::string meshfile) Domain interior_faces = InteriorFaces(*mesh); qoi.AddInteriorFaceIntegral( - Dimension{}, DependsOn<0>{}, + Dimension{}, DependsOn<0>{}, [=](double /*t*/, auto X, auto velocity) { // compute the unit surface normal auto dX_dxi = get(X); - auto n = normalize(cross(dX_dxi)); + auto n = normalize(cross(dX_dxi)); // extract the velocity values from each side of the interface - // note: the orientation convention is such that the normal + // note: the orientation convention is such that the normal // computed as above will point from from side 1->2 - auto [u_1, u_2] = velocity; + auto [u_1, u_2] = velocity; auto a = dot(u_2 - u_1, n); auto f_1 = u_1 * a; auto f_2 = u_2 * a; return dot(f_1, f_2); - }, interior_faces); + }, + interior_faces); double t = 0.0; check_gradient(qoi, t, U); - } -TEST(basic, L2_qoi_test_tri_and_quads_linear) { L2_qoi_test<2, 1>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); } -TEST(basic, L2_qoi_test_tri_and_quads_quadratic) { L2_qoi_test<2, 2>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); } +TEST(basic, L2_qoi_test_tri_and_quads_linear) +{ + L2_qoi_test<2, 1>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); +} +TEST(basic, L2_qoi_test_tri_and_quads_quadratic) +{ + L2_qoi_test<2, 2>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); +} TEST(basic, L2_qoi_test_tets_linear) { L2_qoi_test<3, 1>(SERAC_REPO_DIR "/data/meshes/patch3D_tets.mesh"); } TEST(basic, L2_qoi_test_tets_quadratic) { L2_qoi_test<3, 2>(SERAC_REPO_DIR "/data/meshes/patch3D_tets.mesh"); } @@ -140,16 +149,16 @@ TEST(basic, L2_qoi_test_hexes_quadratic) { L2_qoi_test<3, 2>(SERAC_REPO_DIR "/da template void L2_scalar_valued_test(std::string meshfile) { - using test_space = L2

; + using test_space = L2

; using trial_space_0 = L2

; using trial_space_1 = H1; auto mesh = mesh::refineAndDistribute(buildMeshFromFile(meshfile), 1); - auto L2fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); + auto L2fec = mfem::L2_FECollection(p, dim, mfem::BasisType::GaussLobatto); mfem::ParFiniteElementSpace fespace_0(mesh.get(), &L2fec, 1, serac::ordering); - auto H1fec = mfem::H1_FECollection(p, dim); + auto H1fec = mfem::H1_FECollection(p, dim); mfem::ParFiniteElementSpace fespace_1(mesh.get(), &H1fec, dim, serac::ordering); mfem::Vector U0(fespace_0.TrueVSize()); @@ -161,36 +170,37 @@ void L2_scalar_valued_test(std::string meshfile) // Construct the new functional object using the specified test and trial spaces Functional residual(&fespace_0, {&fespace_0, &fespace_1}); - constexpr int VALUE = 0; + constexpr int VALUE = 0; constexpr int DERIVATIVE = 1; Domain interior_faces = InteriorFaces(*mesh); residual.AddInteriorFaceIntegral( - Dimension{}, DependsOn<0, 1>{}, + Dimension{}, DependsOn<0, 1>{}, [=](double /*t*/, auto X, auto rho, auto u) { - - auto n = normalize(cross(get(X))); + auto n = normalize(cross(get(X))); auto [rho0, rho1] = rho; - auto uTn = dot(get(u), n); - auto s = uTn > 0; + auto uTn = dot(get(u), n); + auto s = uTn > 0; - return serac::tuple{ - uTn * (( s) * rho0 + (1.0 - s) * rho1), - uTn * ((1.0 - s) * rho0 + ( s) * rho1) - }; - - }, interior_faces); + return serac::tuple{uTn * ((s)*rho0 + (1.0 - s) * rho1), uTn * ((1.0 - s) * rho0 + (s)*rho1)}; + }, + interior_faces); double t = 0.0; check_gradient(residual, t, U0, U1); - } -TEST(basic, L2_mixed_scalar_test_tris_and_quads_linear) { L2_scalar_valued_test<2, 1>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); } +TEST(basic, L2_mixed_scalar_test_tris_and_quads_linear) +{ + L2_scalar_valued_test<2, 1>(SERAC_REPO_DIR "/data/meshes/patch2D_tris_and_quads.mesh"); +} -TEST(basic, L2_mixed_scalar_test_tets_and_hexes_linear) { L2_scalar_valued_test<3, 1>(SERAC_REPO_DIR "/data/meshes/patch3D_tets_and_hexes.mesh"); } +TEST(basic, L2_mixed_scalar_test_tets_and_hexes_linear) +{ + L2_scalar_valued_test<3, 1>(SERAC_REPO_DIR "/data/meshes/patch3D_tets_and_hexes.mesh"); +} int main(int argc, char* argv[]) { From 344ea10032b99750b0aaef15c811dfa87286fac9 Mon Sep 17 00:00:00 2001 From: Alex Tyler Chapman Date: Wed, 4 Dec 2024 17:51:38 -0800 Subject: [PATCH 83/92] `chmod` shared caliper files --- scripts/llnl/run_benchmarks.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/llnl/run_benchmarks.py b/scripts/llnl/run_benchmarks.py index d2d388480b..627b900016 100755 --- a/scripts/llnl/run_benchmarks.py +++ b/scripts/llnl/run_benchmarks.py @@ -89,6 +89,9 @@ def main(): for cali_file in cali_files: if os.path.exists(cali_file): shutil.copy2(cali_file, spot_dir) + # Grant new caliper file user read/ write, group read/ write, and other read access + os.chmod(pjoin(spot_dir, cali_file), + stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH) # Print SPOT url if on_rz(): From b1b8c1d043cbeceaaff28862abf428a1eca024c5 Mon Sep 17 00:00:00 2001 From: Brandon Talamini Date: Wed, 4 Dec 2024 20:36:46 -0800 Subject: [PATCH 84/92] Write test that exposes bug --- .../functional/tests/domain_tests.cpp | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/serac/numerics/functional/tests/domain_tests.cpp b/src/serac/numerics/functional/tests/domain_tests.cpp index 7893107a91..eda03b0990 100644 --- a/src/serac/numerics/functional/tests/domain_tests.cpp +++ b/src/serac/numerics/functional/tests/domain_tests.cpp @@ -404,6 +404,75 @@ TEST(domain, of3dElementsFindsDofs) EXPECT_EQ(dof_indices.Size(), 113); } +TEST(domain, of2dBoundaryElementsFindsDofs) +{ + constexpr int dim = 2; + constexpr int p = 2; + auto mesh = import_mesh("patch2D_tris_and_quads.mesh"); + + auto find_right_boundary = [](std::vector vertices, int /* attr */) { + return std::all_of(vertices.begin(), vertices.end(), [](vec2 X) { return X[0] > 1.0 - 1e-2; }); + }; + + Domain d0 = Domain::ofBoundaryElements(mesh, find_right_boundary); + EXPECT_EQ(d0.edge_ids_.size(), 1); + + auto fec = mfem::H1_FECollection(p, dim); + auto fes = mfem::FiniteElementSpace(&mesh, &fec); + + mfem::Array dof_indices = d0.dof_list(&fes); + + EXPECT_EQ(dof_indices.Size(), 3); + + auto find_top_boundary = [](std::vector vertices, int /* attr */) { + return std::all_of(vertices.begin(), vertices.end(), [](vec2 X) { return X[1] > 1.0 - 1e-2; }); + }; + + Domain d1 = Domain::ofBoundaryElements(mesh, find_top_boundary); + EXPECT_EQ(d1.edge_ids_.size(), 1); + + Domain d2 = d0 | d1; + + dof_indices = d2.dof_list(&fes); + + EXPECT_EQ(dof_indices.Size(), 5); +} + + +TEST(domain, of3dBoundaryElementsFindsDofs) +{ + constexpr int dim = 3; + constexpr int p = 2; + auto mesh = import_mesh("patch3D_tets.mesh"); + + auto find_xmax_boundary = [](std::vector vertices, int /* attr */) { + return std::all_of(vertices.begin(), vertices.end(), [](vec3 X) { return X[0] > 1.0 - 1e-2; }); + }; + + Domain d0 = Domain::ofBoundaryElements(mesh, find_xmax_boundary); + EXPECT_EQ(d0.tri_ids_.size(), 2); + + auto fec = mfem::H1_FECollection(p, dim); + auto fes = mfem::FiniteElementSpace(&mesh, &fec); + + mfem::Array dof_indices = d0.dof_list(&fes); + + EXPECT_EQ(dof_indices.Size(), 9); + + auto find_ymax_boundary = [](std::vector vertices, int /* attr */) { + return std::all_of(vertices.begin(), vertices.end(), [](vec3 X) { return X[1] > 1.0 - 1e-2; }); + }; + + Domain d1 = Domain::ofBoundaryElements(mesh, find_ymax_boundary); + EXPECT_EQ(d1.tri_ids_.size(), 2); + + Domain d2 = d0 | d1; + + dof_indices = d2.dof_list(&fes); + + EXPECT_EQ(dof_indices.Size(), 15); +} + int main(int argc, char* argv[]) { int num_procs, myid; From 6321bf6cee31ea07fd12116dd382c3642ddc3e57 Mon Sep 17 00:00:00 2001 From: Brandon Talamini Date: Wed, 4 Dec 2024 20:48:09 -0800 Subject: [PATCH 85/92] Force type attribute to be set explicitly and fix incorrect type bug in boolean ops --- src/serac/numerics/functional/domain.cpp | 9 +++++---- src/serac/numerics/functional/domain.hpp | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/serac/numerics/functional/domain.cpp b/src/serac/numerics/functional/domain.cpp index 55dfe9f810..ab8fab1416 100644 --- a/src/serac/numerics/functional/domain.cpp +++ b/src/serac/numerics/functional/domain.cpp @@ -56,7 +56,7 @@ static Domain domain_of_edges(const mfem::Mesh& mesh, std::function predicate { assert(mesh.SpaceDimension() == d); - Domain output{mesh, 1 /* edges are 1-dimensional */}; + Domain output{mesh, 1 /* edges are 1-dimensional */, Domain::Type::Elements}; // layout is undocumented, but it seems to be // [x1, x2, x3, ..., y1, y2, y3 ..., (z1, z2, z3, ...)] @@ -110,7 +110,7 @@ static Domain domain_of_faces(const mfem::Mesh& { assert(mesh.SpaceDimension() == d); - Domain output{mesh, 2 /* faces are 2-dimensional */}; + Domain output{mesh, 2 /* faces are 2-dimensional */, Domain::Type::Elements}; // layout is undocumented, but it seems to be // [x1, x2, x3, ..., y1, y2, y3 ..., (z1, z2, z3, ...)] @@ -191,7 +191,7 @@ static Domain domain_of_elems(const mfem::Mesh& { assert(mesh.SpaceDimension() == d); - Domain output{mesh, mesh.SpaceDimension() /* elems can be 2 or 3 dimensional */}; + Domain output{mesh, mesh.SpaceDimension() /* elems can be 2 or 3 dimensional */, Domain::Type::Elements}; // layout is undocumented, but it seems to be // [x1, x2, x3, ..., y1, y2, y3 ..., (z1, z2, z3, ...)] @@ -498,8 +498,9 @@ Domain set_operation(set_op op, const Domain& a, const Domain& b) { assert(&a.mesh_ == &b.mesh_); assert(a.dim_ == b.dim_); + assert(a.type_ == b.type_); - Domain output{a.mesh_, a.dim_}; + Domain output{a.mesh_, a.dim_, a.type_}; using Ids = std::vector; auto apply_set_op = [&op](const Ids& x, const Ids& y) { return set_operation(op, x, y); }; diff --git a/src/serac/numerics/functional/domain.hpp b/src/serac/numerics/functional/domain.hpp index 7ecfdcfae3..6ef8633598 100644 --- a/src/serac/numerics/functional/domain.hpp +++ b/src/serac/numerics/functional/domain.hpp @@ -83,7 +83,7 @@ struct Domain { /// @endcond /// @brief construct an "empty" domain, to later be populated later with addElement member functions - Domain(const mfem::Mesh& m, int d, Type type = Domain::Type::Elements) : mesh_(m), dim_(d), type_(type) {} + Domain(const mfem::Mesh& m, int d, Type type) : mesh_(m), dim_(d), type_(type) {} /** * @brief create a domain from some subset of the vertices in an mfem::Mesh From bbff5d07b3db092f44c76100a838761c807872dd Mon Sep 17 00:00:00 2001 From: Agent Style Date: Thu, 5 Dec 2024 06:44:46 -0800 Subject: [PATCH 86/92] Apply style updates --- src/serac/numerics/functional/tests/domain_tests.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/serac/numerics/functional/tests/domain_tests.cpp b/src/serac/numerics/functional/tests/domain_tests.cpp index eda03b0990..c8cc1fd56a 100644 --- a/src/serac/numerics/functional/tests/domain_tests.cpp +++ b/src/serac/numerics/functional/tests/domain_tests.cpp @@ -427,7 +427,7 @@ TEST(domain, of2dBoundaryElementsFindsDofs) auto find_top_boundary = [](std::vector vertices, int /* attr */) { return std::all_of(vertices.begin(), vertices.end(), [](vec2 X) { return X[1] > 1.0 - 1e-2; }); }; - + Domain d1 = Domain::ofBoundaryElements(mesh, find_top_boundary); EXPECT_EQ(d1.edge_ids_.size(), 1); @@ -438,7 +438,6 @@ TEST(domain, of2dBoundaryElementsFindsDofs) EXPECT_EQ(dof_indices.Size(), 5); } - TEST(domain, of3dBoundaryElementsFindsDofs) { constexpr int dim = 3; @@ -462,7 +461,7 @@ TEST(domain, of3dBoundaryElementsFindsDofs) auto find_ymax_boundary = [](std::vector vertices, int /* attr */) { return std::all_of(vertices.begin(), vertices.end(), [](vec3 X) { return X[1] > 1.0 - 1e-2; }); }; - + Domain d1 = Domain::ofBoundaryElements(mesh, find_ymax_boundary); EXPECT_EQ(d1.tri_ids_.size(), 2); From c9219f30de9d2a42a377f5c45690c827c38f1189 Mon Sep 17 00:00:00 2001 From: Sam Mish Date: Thu, 5 Dec 2024 09:11:32 -0800 Subject: [PATCH 87/92] respond to Brandon's feedback --- src/serac/numerics/functional/element_restriction.cpp | 6 ++++-- src/serac/numerics/functional/element_restriction.hpp | 3 --- src/serac/numerics/functional/functional.hpp | 2 -- .../numerics/functional/interior_face_integral_kernels.hpp | 4 ---- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/serac/numerics/functional/element_restriction.cpp b/src/serac/numerics/functional/element_restriction.cpp index fca8af0351..e817a23628 100644 --- a/src/serac/numerics/functional/element_restriction.cpp +++ b/src/serac/numerics/functional/element_restriction.cpp @@ -472,6 +472,10 @@ axom::Array GetFaceDofs(const serac::fes_t* fes std::vector > local_face_dofs = geom_local_face_dofs(p); std::vector > lex_perm = lexicographic_permutations(p); + // sam: this function contains several comments that relate to an investigation into adopting + // mfem's FaceNbrData pattern (which ended up being too invasive to implement initially). + // I leave some of the commented code here so Julian's recommendations are not lost. + // // int components_per_node = fes->GetVDim(); // bool by_vdim = fes->GetOrdering() == mfem::Ordering::byVDIM; // fes->ExchangeFaceNbrData(); @@ -639,8 +643,6 @@ ElementRestriction::ElementRestriction(const fes_t* fes, mfem::Geometry::Type el ordering = fes->GetOrdering(); - element_ids = elem_ids; - lsize = uint64_t(fes->GetVSize()); components = uint64_t(fes->GetVDim()); num_nodes = lsize / components; diff --git a/src/serac/numerics/functional/element_restriction.hpp b/src/serac/numerics/functional/element_restriction.hpp index c77ced11c5..dc2e1c3360 100644 --- a/src/serac/numerics/functional/element_restriction.hpp +++ b/src/serac/numerics/functional/element_restriction.hpp @@ -196,9 +196,6 @@ struct ElementRestriction { /// the number of nodes in each element uint64_t nodes_per_elem; - /// an array mapping from domain element ids [0, num_elements) to - std::vector element_ids; - /// a 2D array (num_elements-by-nodes_per_elem) holding the dof info extracted from the finite element space axom::Array dof_info; diff --git a/src/serac/numerics/functional/functional.hpp b/src/serac/numerics/functional/functional.hpp index e4af6a876c..bb9fa8a5f0 100644 --- a/src/serac/numerics/functional/functional.hpp +++ b/src/serac/numerics/functional/functional.hpp @@ -26,8 +26,6 @@ #include "serac/numerics/functional/domain.hpp" -#include "serac/infrastructure/mpi_fstream.hpp" - #include #include diff --git a/src/serac/numerics/functional/interior_face_integral_kernels.hpp b/src/serac/numerics/functional/interior_face_integral_kernels.hpp index 269b583afe..408e03b956 100644 --- a/src/serac/numerics/functional/interior_face_integral_kernels.hpp +++ b/src/serac/numerics/functional/interior_face_integral_kernels.hpp @@ -11,9 +11,6 @@ #include "serac/numerics/functional/quadrature_data.hpp" #include "serac/numerics/functional/differentiate_wrt.hpp" -// TODO REMOVE AFTER DEBUGGING -#include "serac/infrastructure/mpi_fstream.hpp" - namespace serac { namespace interior_face_integral { @@ -169,7 +166,6 @@ void evaluation_kernel_impl(trial_element_type trial_elements, test_element, dou // for each element in the domain for (uint32_t e = 0; e < num_elements; e++) { - mpi::out << "e: " << e << " / " << num_elements << std::endl; // load the jacobians and positions for each quadrature point in this element auto J_e = J[e]; From 7249be17a474b491840c071f169a8ec615b6d650 Mon Sep 17 00:00:00 2001 From: Agent Style Date: Thu, 5 Dec 2024 09:13:54 -0800 Subject: [PATCH 88/92] Apply style updates --- src/serac/numerics/functional/element_restriction.cpp | 2 +- .../numerics/functional/interior_face_integral_kernels.hpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/serac/numerics/functional/element_restriction.cpp b/src/serac/numerics/functional/element_restriction.cpp index e817a23628..d5e7213121 100644 --- a/src/serac/numerics/functional/element_restriction.cpp +++ b/src/serac/numerics/functional/element_restriction.cpp @@ -472,7 +472,7 @@ axom::Array GetFaceDofs(const serac::fes_t* fes std::vector > local_face_dofs = geom_local_face_dofs(p); std::vector > lex_perm = lexicographic_permutations(p); - // sam: this function contains several comments that relate to an investigation into adopting + // sam: this function contains several comments that relate to an investigation into adopting // mfem's FaceNbrData pattern (which ended up being too invasive to implement initially). // I leave some of the commented code here so Julian's recommendations are not lost. // diff --git a/src/serac/numerics/functional/interior_face_integral_kernels.hpp b/src/serac/numerics/functional/interior_face_integral_kernels.hpp index 408e03b956..83842deee4 100644 --- a/src/serac/numerics/functional/interior_face_integral_kernels.hpp +++ b/src/serac/numerics/functional/interior_face_integral_kernels.hpp @@ -166,7 +166,6 @@ void evaluation_kernel_impl(trial_element_type trial_elements, test_element, dou // for each element in the domain for (uint32_t e = 0; e < num_elements; e++) { - // load the jacobians and positions for each quadrature point in this element auto J_e = J[e]; auto x_e = x[e]; From f80fab5513d7cd6518a759a13c1fc4f6c53d5125 Mon Sep 17 00:00:00 2001 From: Brandon Talamini Date: Thu, 5 Dec 2024 15:08:41 -0800 Subject: [PATCH 89/92] Fix logical conflict not picked up by merge algorithm --- src/serac/numerics/functional/domain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serac/numerics/functional/domain.cpp b/src/serac/numerics/functional/domain.cpp index 9e1900c5d4..c39bef78c5 100644 --- a/src/serac/numerics/functional/domain.cpp +++ b/src/serac/numerics/functional/domain.cpp @@ -584,7 +584,7 @@ Domain set_operation(SET_OPERATION op, const Domain& a, const Domain& b) assert(a.dim_ == b.dim_); assert(a.type_ == b.type_); - Domain output{a.mesh_, a.dim_, a.type_}; + Domain combined{a.mesh_, a.dim_, a.type_}; using Ids = std::vector; auto apply_set_op = [&op](const Ids& x, const Ids& y) { return set_operation(op, x, y); }; From 3a1baf8461d2b4d1f756a23e1e6b841aa61c7475 Mon Sep 17 00:00:00 2001 From: Alex Tyler Chapman Date: Fri, 6 Dec 2024 11:26:54 -0800 Subject: [PATCH 90/92] fix --- scripts/llnl/run_benchmarks.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/llnl/run_benchmarks.py b/scripts/llnl/run_benchmarks.py index 627b900016..136850d4e8 100755 --- a/scripts/llnl/run_benchmarks.py +++ b/scripts/llnl/run_benchmarks.py @@ -89,9 +89,12 @@ def main(): for cali_file in cali_files: if os.path.exists(cali_file): shutil.copy2(cali_file, spot_dir) - # Grant new caliper file user read/ write, group read/ write, and other read access - os.chmod(pjoin(spot_dir, cali_file), - stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH) + # Update group and permissions of new caliper file + cali_file = os.path.join(spot_dir, os.path.basename(cali_file)) + group_info = grp.getgrnam("smithdev") + os.chown(cali_file, -1, group_info.gr_gid) + os.chmod(cali_file, + stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP) # Print SPOT url if on_rz(): From 92096f2d94632b91f378e5b125dbbadaa24f0d4c Mon Sep 17 00:00:00 2001 From: Alex Chapman Date: Tue, 10 Dec 2024 14:21:32 -0800 Subject: [PATCH 91/92] correct small docs mistake --- src/docs/sphinx/quickstart.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/docs/sphinx/quickstart.rst b/src/docs/sphinx/quickstart.rst index f9fe3b4fd3..fb142c75fa 100644 --- a/src/docs/sphinx/quickstart.rst +++ b/src/docs/sphinx/quickstart.rst @@ -212,8 +212,7 @@ Some build options frequently used by Serac include: * ``ENABLE_ASAN``: Enables the Address Sanitizer for memory safety inspections, defaults to ``OFF`` * ``SERAC_ENABLE_TESTS``: Enables Serac unit tests, defaults to ``ON`` * ``SERAC_ENABLE_CODEVELOP``: Enables local development build of MFEM/Axom, see :ref:`codevelop-label`, defaults to ``OFF`` -* ``SERAC_USE_VDIM_ORDERING``: Sets the vector ordering to be ``byVDIM``, which is significantly faster for algebraic multigrid, - but may conflict with other packages if Serac is being used as a dependency, defaults to ``OFF``. +* ``SERAC_USE_VDIM_ORDERING``: Sets the vector ordering to be ``byVDIM``, which is significantly faster for algebraic multigrid, defaults to ``ON``. Once the build has been configured, Serac can be built with the following commands: From 39358404bd68742f86249c347cbcffead7c4b225 Mon Sep 17 00:00:00 2001 From: Alex Chapman Date: Tue, 10 Dec 2024 16:58:47 -0800 Subject: [PATCH 92/92] re-run azure --- src/docs/sphinx/quickstart.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/docs/sphinx/quickstart.rst b/src/docs/sphinx/quickstart.rst index fb142c75fa..5cc629e2f2 100644 --- a/src/docs/sphinx/quickstart.rst +++ b/src/docs/sphinx/quickstart.rst @@ -12,7 +12,8 @@ Quickstart Guide Getting Serac ------------- -Serac is hosted on `GitHub `_. Serac uses git submodules, so the project must be cloned recursively. Use either of the following commands to pull Serac's repository: +Serac is hosted on `GitHub `_. Serac uses git submodules, so the project must be cloned +recursively. Use either of the following commands to pull Serac's repository: .. code-block:: bash