diff --git a/CMakeLists.txt b/CMakeLists.txt index b287e40..99ff8b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -166,6 +166,7 @@ meshfields_add_exe(SerializationTests test/testSerialize.cpp) meshfields_add_exe(ElementTests test/testElement.cpp) meshfields_add_exe(ElementJacobian1d test/testElementJacobian1d.cpp) meshfields_add_exe(ElementJacobian2d test/testElementJacobian2d.cpp) +meshfields_add_exe(ElementJacobian3d test/testElementJacobian3d.cpp) meshfields_add_exe(CountIntegrator test/testCountIntegrator.cpp) meshfields_add_exe(OmegahTriTests test/testOmegahTri.cpp) meshfields_add_exe(ExceptionTest test/testExceptions.cpp) @@ -184,6 +185,7 @@ test_func(SerializationTests ./SerializationTests) test_func(ElementTests ./ElementTests) test_func(ElementJacobian1d ./ElementJacobian1d) test_func(ElementJacobian2d ./ElementJacobian2d) +test_func(ElementJacobian3d ./ElementJacobian3d) test_func(CountIntegrator ./CountIntegrator) test_func(OmegahTriTests ./OmegahTriTests) test_func(PointMapping ./PointMapping) diff --git a/src/MeshField.hpp b/src/MeshField.hpp index 63b51da..78757b6 100644 --- a/src/MeshField.hpp +++ b/src/MeshField.hpp @@ -71,8 +71,8 @@ struct LinearTriangleToVertexField { } } - KOKKOS_FUNCTION Kokkos::Array - getTopology() const { + static constexpr KOKKOS_FUNCTION Kokkos::Array + getTopology() { return {MeshField::Triangle}; } @@ -102,8 +102,8 @@ struct LinearTetrahedronToVertexField { __func__); } } - KOKKOS_FUNCTION Kokkos::Array - getTopology() const { + static constexpr KOKKOS_FUNCTION Kokkos::Array + getTopology() { return {MeshField::Tetrahedron}; } @@ -136,8 +136,8 @@ struct QuadraticTriangleToField { } } - KOKKOS_FUNCTION Kokkos::Array - getTopology() const { + static constexpr KOKKOS_FUNCTION Kokkos::Array + getTopology() { return {MeshField::Triangle}; } @@ -198,8 +198,8 @@ struct QuadraticTetrahedronToField { } } - KOKKOS_FUNCTION Kokkos::Array - getTopology() const { + static constexpr KOKKOS_FUNCTION Kokkos::Array + getTopology() { return {MeshField::Tetrahedron}; } diff --git a/src/MeshField_Element.hpp b/src/MeshField_Element.hpp index b43c500..f29c7d8 100644 --- a/src/MeshField_Element.hpp +++ b/src/MeshField_Element.hpp @@ -256,9 +256,17 @@ struct FieldElement { "tangent vectors" in 3D, the volume of their parallelpiped, which is the differential volume of the coordinate field */ - fail("getJacobianDeterminant doesn't yet support 3d. " - "The given matrices have dimension %lu x %lu \n", - J.extent(0), J.extent(1)); + Kokkos::View determinants("3dJacobianDeterminants", J.extent(0)); + Kokkos::parallel_for( + J.extent(0), KOKKOS_LAMBDA(const int i) { + const auto Ji = Kokkos::subview(J, i, Kokkos::ALL(), Kokkos::ALL()); + const auto cofactorSum = + Ji(0, 0) * (Ji(1, 1) * Ji(2, 2) - Ji(1, 2) * Ji(2, 1)) - + Ji(0, 1) * (Ji(1, 0) * Ji(2, 2) - Ji(1, 2) * Ji(2, 0)) + + Ji(0, 2) * (Ji(1, 0) * Ji(2, 1) - Ji(1, 1) * Ji(2, 0)); + determinants(i) = cofactorSum; + }); + return determinants; } if (dimension == 2) { if (J.extent(1) != 2) { @@ -349,8 +357,9 @@ struct FieldElement { fail("The input array of offsets must have size = %zu\n", numMeshEnts + 1); } - if (MeshEntDim != 1 && MeshEntDim != 2) { - fail("getJacobians only currently supports 1d and 2d meshes. Input mesh " + if (MeshEntDim != 1 && MeshEntDim != 2 && MeshEntDim != 3) { + fail("getJacobians only currently supports 1d, 2d, and 3d meshes. Input " + "mesh " "has %zu dimensions.\n", numMeshEnts); } @@ -366,7 +375,7 @@ struct FieldElement { } }); return res; - } else if constexpr (MeshEntDim == 2) { + } else if constexpr (MeshEntDim == 2 || MeshEntDim == 3) { const auto numPts = MeshFieldUtil::getLastValue(offsets); // one matrix per point Kokkos::View res("result", numPts, MeshEntDim, MeshEntDim); diff --git a/src/MeshField_Integrate.hpp b/src/MeshField_Integrate.hpp index 15fa8bc..0c16d68 100644 --- a/src/MeshField_Integrate.hpp +++ b/src/MeshField_Integrate.hpp @@ -10,50 +10,51 @@ namespace MeshField { // directly copied from SCOREC/core @ 7cd76473 apf/apfIntegrate.[h|cc] -struct IntegrationPoint { - IntegrationPoint(Vector3 const &p, double w) : param(p), weight(w) {} - Vector3 param; +template struct IntegrationPoint { + // template parameter pointSize specifies the length of the integration point + // array for one point + IntegrationPoint(Kokkos::Array const &p, double w) + : param(p), weight(w) {} + Kokkos::Array param; double weight; }; - -class Integration { +template class Integration { public: virtual ~Integration() {} virtual int countPoints() const = 0; - virtual std::vector getPoints() const = 0; + virtual std::vector> getPoints() const = 0; virtual int getAccuracy() const = 0; }; - -class EntityIntegration { +template class EntityIntegration { public: virtual ~EntityIntegration() {} - Integration const *getAccurate(int minimumAccuracy) const { + Integration const *getAccurate(int minimumAccuracy) const { int n = countIntegrations(); for (int i = 0; i < n; ++i) { - Integration const *integration = getIntegration(i); + Integration const *integration = getIntegration(i); if (integration->getAccuracy() >= minimumAccuracy) return integration; } return NULL; } virtual int countIntegrations() const = 0; - virtual Integration const *getIntegration(int i) const = 0; + virtual Integration const *getIntegration(int i) const = 0; }; -class TriangleIntegration : public EntityIntegration { +class TriangleIntegration : public EntityIntegration<3> { public: - class N1 : public Integration { + class N1 : public Integration<3> { public: virtual int countPoints() const { return 1; } - virtual std::vector getPoints() const { + virtual std::vector> getPoints() const { return {IntegrationPoint(Vector3{1. / 3., 1. / 3., 1. / 3.}, 1.0 / 2.0)}; } virtual int getAccuracy() const { return 1; } }; // end N1 - class N2 : public Integration { + class N2 : public Integration<3> { public: virtual int countPoints() const { return 3; } - virtual std::vector getPoints() const { + virtual std::vector> getPoints() const { return {IntegrationPoint(Vector3{0.666666666666667, 0.166666666666667, 0.166666666666667}, 1. / 3. / 2.0), @@ -67,31 +68,69 @@ class TriangleIntegration : public EntityIntegration { virtual int getAccuracy() const { return 2; } }; // end N2 virtual int countIntegrations() const { return 2; } - virtual Integration const *getIntegration(int i) const { + virtual Integration<3> const *getIntegration(int i) const { static N1 i1; static N2 i2; - static Integration *integrations[2] = {&i1, &i2}; + static Integration<3> *integrations[2] = {&i1, &i2}; return integrations[i]; } }; -std::shared_ptr const getIntegration(Mesh_Topology topo) { - if (topo != Triangle) { - fail("getIntegration only supports triangles (Mesh_Topology::Triangle)\n"); +class TetrahedronIntegration : public EntityIntegration<4> { +public: + class N1 : public Integration<4> { + public: + virtual int countPoints() const { return 1; } + virtual std::vector> getPoints() const { + return {IntegrationPoint(Vector4{0.25, 0.25, 0.25, 0.25}, 1.0 / 6.0)}; + } + virtual int getAccuracy() const { return 1; } + }; + class N2 : public Integration<4> { + public: + virtual int countPoints() const { return 4; } + virtual std::vector> getPoints() const { + + return {IntegrationPoint(Vector4{0.138196601125011, 0.138196601125011, + 0.138196601125011, 0.585410196624969}, + 0.25 / 6.0), + IntegrationPoint(Vector4{0.585410196624969, 0.138196601125011, + 0.138196601125011, 0.138196601125011}, + 0.25 / 6.0), + IntegrationPoint(Vector4{0.138196601125011, 0.585410196624969, + 0.138196601125011, 0.138196601125011}, + 0.25 / 6.0), + IntegrationPoint(Vector4{0.138196601125011, 0.138196601125011, + 0.585410196624969, 0.138196601125011}, + 0.25 / 6.0)}; + } + virtual int getAccuracy() const { return 2; } + }; + virtual int countIntegrations() const { return 2; } + virtual Integration<4> const *getIntegration(int i) const { + static N1 i1; + static N2 i2; + static Integration<4> *integrations[2] = {&i1, &i2}; + return integrations[i]; } - return std::make_shared(); +}; +template auto const getIntegration() { + if constexpr (topo == Triangle) { + return std::make_shared(); + } else if constexpr (topo == Tetrahedron) { + return std::make_shared(); + } + fail("getIntegration does not support given topology\n"); } - -std::vector getIntegrationPoints(Mesh_Topology topo, - int order) { - auto ip = getIntegration(topo)->getAccurate(order)->getPoints(); +template auto getIntegrationPoints(int order) { + auto ip = getIntegration()->getAccurate(order)->getPoints(); return ip; } template -Kokkos::View -getIntegrationPointLocalCoords(FieldElement &fes, - std::vector ip) { +Kokkos::View getIntegrationPointLocalCoords( + FieldElement &fes, + std::vector> ip) { const auto numPtsPerElm = ip.size(); const auto numMeshEnts = fes.numMeshEnts; const auto meshEntDim = fes.MeshEntDim; @@ -115,9 +154,9 @@ getIntegrationPointLocalCoords(FieldElement &fes, } template -Kokkos::View -getIntegrationPointWeights(FieldElement &fes, - std::vector ip) { +Kokkos::View getIntegrationPointWeights( + FieldElement &fes, + std::vector> ip) { const auto numPtsPerElm = ip.size(); const auto numMeshEnts = fes.numMeshEnts; const auto meshEntDim = fes.MeshEntDim; @@ -194,13 +233,9 @@ class Integrator { * FIXME make the sensible * */ template void process(FieldElement &fes) { - const auto topo = fes.elm2dof.getTopology(); - if (topo[0] != MeshField::Triangle) { - throw std::invalid_argument( - "Integrator::process only supports triangles."); - } + constexpr auto topo = decltype(FieldElement::elm2dof)::getTopology(); pre(); - auto ip = getIntegrationPoints(topo[0], order); + auto ip = getIntegrationPoints(order); auto localCoords = getIntegrationPointLocalCoords(fes, ip); auto weights = getIntegrationPointWeights(fes, ip); auto dV = getJacobianDeterminants(fes, localCoords, ip.size()); diff --git a/test/testCountIntegrator.cpp b/test/testCountIntegrator.cpp index 68aab23..c6361b0 100644 --- a/test/testCountIntegrator.cpp +++ b/test/testCountIntegrator.cpp @@ -14,12 +14,12 @@ using ExecutionSpace = Kokkos::DefaultExecutionSpace; using MemorySpace = Kokkos::DefaultExecutionSpace::memory_space; - -Omega_h::Mesh createMeshTri18(Omega_h::Library &lib) { +template Omega_h::Mesh createMesh(Omega_h::Library &lib) { auto world = lib.world(); const auto family = OMEGA_H_SIMPLEX; auto len = 1.0; - return Omega_h::build_box(world, family, len, len, 0.0, 3, 3, 0); + const auto numEnts3d = (dim == 3 ? 3 : 0); + return Omega_h::build_box(world, family, len, len, len, 3, 3, numEnts3d); } template @@ -57,13 +57,19 @@ class CountIntegrator : public MeshField::Integrator { } }; -template