From 75405e2950fc60fda8f937ae2841713f6e952a6f Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Mon, 10 Aug 2026 17:07:19 +0200 Subject: [PATCH 01/37] [FEM] Introduce higher order quadrature rule switch for Triangle --- .../src/sofa/fem/FiniteElement[Triangle].h | 40 +++++++++++++++++-- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Triangle].h b/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Triangle].h index 0d9659040de..7495e802b26 100644 --- a/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Triangle].h +++ b/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Triangle].h @@ -21,6 +21,8 @@ ******************************************************************************/ #pragma once #include +#include +#include namespace sofa::fem { @@ -64,11 +66,41 @@ struct FiniteElement }; } - static constexpr std::array quadraturePoints() + template + static constexpr auto quadraturePoints() { - return { - std::make_pair(sofa::type::Vec(1./3., 1./3.), 1./2.) - }; + if constexpr (Degree <= 1) + { + // Degree 1: 1-point centroid rule (default). + return std::array{ + std::make_pair(ReferenceCoord(1./3., 1./3.), Real(1./2.)) + }; + } + else if constexpr (Degree <= 2) + { + // Degree 2: 3-point interior rule. + return std::array{ + std::make_pair(ReferenceCoord(1./6., 1./6.), Real(1./6.)), + std::make_pair(ReferenceCoord(2./3., 1./6.), Real(1./6.)), + std::make_pair(ReferenceCoord(1./6., 2./3.), Real(1./6.)) + }; + } + else + { + static_assert(Degree <= 2, "FiniteElement: no quadrature rule for the requested degree"); + } + } + + // Quadrature rule selector by degree; view of the compile-time table. + static std::span quadratureRule(int degree) + { + switch (degree) + { + case 1: { static constexpr auto rule = quadraturePoints<1>(); return rule; } + case 2: { static constexpr auto rule = quadraturePoints<2>(); return rule; } + default: + throw std::invalid_argument("FiniteElement::quadratureRule: unsupported degree"); + } } }; From e76a64abf0b56024fac686185043121d20f34b0c Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Mon, 10 Aug 2026 17:17:19 +0200 Subject: [PATCH 02/37] Edge --- .../FEM/src/sofa/fem/FiniteElement[Edge].h | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Edge].h b/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Edge].h index fbffb5dbfed..492caa7a380 100644 --- a/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Edge].h +++ b/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Edge].h @@ -21,6 +21,8 @@ ******************************************************************************/ #pragma once #include +#include +#include #if !defined(SOFA_FEM_FINITE_ELEMENT_EDGE_CPP) #include @@ -55,12 +57,43 @@ struct FiniteElement return {{-static_cast(0.5)}, {static_cast(0.5)}}; } - static constexpr std::array quadraturePoints() + template + static constexpr auto quadraturePoints() { - constexpr sofa::type::Vec q0(static_cast(0)); - return { - std::make_pair(q0, static_cast(2)) - }; + if constexpr (Degree <= 1) + { + // Degree 1: 1-point midpoint rule (default). + return std::array{ + std::make_pair(ReferenceCoord(static_cast(0)), static_cast(2)) + }; + } + else if constexpr (Degree <= 3) + { + // Degrees 2-3: 2-point Gauss-Legendre rule. + constexpr Real sqrt3 = 1.73205080757; + constexpr Real g = static_cast(1) / sqrt3; + return std::array{ + std::make_pair(ReferenceCoord(-g), static_cast(1)), + std::make_pair(ReferenceCoord( g), static_cast(1)) + }; + } + else + { + static_assert(Degree <= 3, "FiniteElement: no quadrature rule for the requested degree"); + } + } + + // Quadrature rule selector by degree; view of the compile-time table. + static std::span quadratureRule(int degree) + { + switch (degree) + { + case 1: { static constexpr auto rule = quadraturePoints<1>(); return rule; } + case 2: + case 3: { static constexpr auto rule = quadraturePoints<3>(); return rule; } + default: + throw std::invalid_argument("FiniteElement::quadratureRule: unsupported degree"); + } } }; From c5b5c7b3c0077ef141e98c3fbcc4ecd008814ea0 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Mon, 10 Aug 2026 17:23:24 +0200 Subject: [PATCH 03/37] Tetrahedra --- .../src/sofa/fem/FiniteElement[Tetrahedron].h | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Tetrahedron].h b/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Tetrahedron].h index ac14bb9c59b..4ab10ae3ed5 100644 --- a/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Tetrahedron].h +++ b/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Tetrahedron].h @@ -21,6 +21,8 @@ ******************************************************************************/ #pragma once #include +#include +#include #if !defined(SOFA_FEM_FINITE_ELEMENT_TETAHEDRON_CPP) #include @@ -68,11 +70,45 @@ struct FiniteElement }; } - static constexpr std::array quadraturePoints() + template + static constexpr auto quadraturePoints() { - constexpr sofa::type::Vec q0(1./4., 1./4., 1./4.); - constexpr std::array q { std::make_pair(q0, 1./6.) }; - return q; + if constexpr (Degree <= 1) + { + // Degree 1: 1-point centroid rule (default). + return std::array{ + std::make_pair(ReferenceCoord(1./4., 1./4., 1./4.), Real(1./6.)) + }; + } + else if constexpr (Degree <= 2) + { + // Degree 2: 4-point rule. + constexpr Real sqrt5 = 2.2360679774997896; + constexpr Real a = (5. - sqrt5) / 20.; + constexpr Real b = (5. + 3. * sqrt5) / 20.; + return std::array{ + std::make_pair(ReferenceCoord(a, a, a), Real(1./24.)), + std::make_pair(ReferenceCoord(b, a, a), Real(1./24.)), + std::make_pair(ReferenceCoord(a, b, a), Real(1./24.)), + std::make_pair(ReferenceCoord(a, a, b), Real(1./24.)) + }; + } + else + { + static_assert(Degree <= 2, "FiniteElement: no quadrature rule for the requested degree"); + } + } + + // Quadrature rule selector by degree; view of the compile-time table. + static std::span quadratureRule(int degree) + { + switch (degree) + { + case 1: { static constexpr auto rule = quadraturePoints<1>(); return rule; } + case 2: { static constexpr auto rule = quadraturePoints<2>(); return rule; } + default: + throw std::invalid_argument("FiniteElement::quadratureRule: unsupported degree"); + } } }; From bc9ec3cb77e1b40a01fa6e9651eedb0e05c065da Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Mon, 10 Aug 2026 17:41:34 +0200 Subject: [PATCH 04/37] Quad --- .../FEM/src/sofa/fem/FiniteElement[Quad].h | 67 +++++++++++++++---- 1 file changed, 55 insertions(+), 12 deletions(-) diff --git a/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Quad].h b/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Quad].h index 4e37585eace..1485a881001 100644 --- a/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Quad].h +++ b/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Quad].h @@ -21,6 +21,8 @@ ******************************************************************************/ #pragma once #include +#include +#include #if !defined(SOFA_FEM_FINITE_ELEMENT_QUAD_CPP) #include @@ -67,21 +69,62 @@ struct FiniteElement }; } - static constexpr std::array quadraturePoints() + template + static constexpr auto quadraturePoints() { - constexpr Real sqrt2_3 = 0.816496580928; //sqrt(2./3.) - constexpr Real sqrt6 = 2.44948974278; //sqrt(6.) - constexpr Real sqrt2 = 1.41421356237; //sqrt(2.) + if constexpr (Degree <= 1) + { + // Degree 1: 1-point centroid rule. + return std::array{ + std::make_pair(ReferenceCoord(static_cast(0), static_cast(0)), static_cast(4)) + }; + } + else if constexpr (Degree <= 2) + { + // Degree 2: 3-point rule (default). + constexpr Real sqrt2_3 = 0.816496580928; //sqrt(2./3.) + constexpr Real sqrt6 = 2.44948974278; //sqrt(6.) + constexpr Real sqrt2 = 1.41421356237; //sqrt(2.) - constexpr sofa::type::Vec q0(sqrt2_3, 0.); - constexpr sofa::type::Vec q1(-1/sqrt6, -1./sqrt2); - constexpr sofa::type::Vec q2(-1/sqrt6, 1./sqrt2); + constexpr ReferenceCoord q0(sqrt2_3, 0.); + constexpr ReferenceCoord q1(-1/sqrt6, -1./sqrt2); + constexpr ReferenceCoord q2(-1/sqrt6, 1./sqrt2); - return { - std::make_pair(q0, 4./3.), - std::make_pair(q1, 4./3.), - std::make_pair(q2, 4./3.), - }; + return std::array{ + std::make_pair(q0, 4./3.), + std::make_pair(q1, 4./3.), + std::make_pair(q2, 4./3.) + }; + } + else if constexpr (Degree <= 3) + { + // Degree 3: 2x2 Gauss-Legendre rule. + constexpr Real sqrt3 = 1.73205080757; //sqrt(3.) + constexpr Real g = static_cast(1) / sqrt3; + return std::array{ + std::make_pair(ReferenceCoord(-g, -g), static_cast(1)), + std::make_pair(ReferenceCoord( g, -g), static_cast(1)), + std::make_pair(ReferenceCoord( g, g), static_cast(1)), + std::make_pair(ReferenceCoord(-g, g), static_cast(1)) + }; + } + else + { + static_assert(Degree <= 3, "FiniteElement: no quadrature rule for the requested degree"); + } + } + + // Quadrature rule selector by degree; view of the compile-time table. + static std::span quadratureRule(int degree) + { + switch (degree) + { + case 1: { static constexpr auto rule = quadraturePoints<1>(); return rule; } + case 2: { static constexpr auto rule = quadraturePoints<2>(); return rule; } + case 3: { static constexpr auto rule = quadraturePoints<3>(); return rule; } + default: + throw std::invalid_argument("FiniteElement::quadratureRule: unsupported degree"); + } } }; From 15ee0f42fc061c1d4f72ed29ea1c54411d28942d Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Mon, 10 Aug 2026 17:41:41 +0200 Subject: [PATCH 05/37] Hexa --- .../src/sofa/fem/FiniteElement[Hexahedron].h | 74 +++++++++++++++---- 1 file changed, 60 insertions(+), 14 deletions(-) diff --git a/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Hexahedron].h b/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Hexahedron].h index 38339624e47..e3ab476e0f8 100644 --- a/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Hexahedron].h +++ b/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Hexahedron].h @@ -21,6 +21,8 @@ ******************************************************************************/ #pragma once #include +#include +#include #if !defined(SOFA_FEM_FINITE_ELEMENT_HEXAHEDRON_CPP) #include @@ -96,24 +98,68 @@ struct FiniteElement return gradient; } + template static constexpr auto quadraturePoints() { - constexpr Real sqrt3 = 1.73205080757; //sqrt(3.) - constexpr Real sqrt3_1 = static_cast(1) / sqrt3; - constexpr Real one = static_cast(1); + if constexpr (Degree <= 1) + { + // Degree 1: 1-point centroid rule. + return std::array{ + std::make_pair(ReferenceCoord(static_cast(0), static_cast(0), static_cast(0)), static_cast(8)) + }; + } + else if constexpr (Degree <= 3) + { + // Degrees 2-3: 2x2x2 Gauss-Legendre rule (default). + constexpr Real sqrt3 = 1.73205080757; //sqrt(3.) + constexpr Real sqrt3_1 = static_cast(1) / sqrt3; + constexpr Real one = static_cast(1); - constexpr std::array q { - std::pair{referenceElementNodes[0] * sqrt3_1, one}, - std::pair{referenceElementNodes[1] * sqrt3_1, one}, - std::pair{referenceElementNodes[2] * sqrt3_1, one}, - std::pair{referenceElementNodes[3] * sqrt3_1, one}, - std::pair{referenceElementNodes[4] * sqrt3_1, one}, - std::pair{referenceElementNodes[5] * sqrt3_1, one}, - std::pair{referenceElementNodes[6] * sqrt3_1, one}, - std::pair{referenceElementNodes[7] * sqrt3_1, one}, - }; + return std::array { + std::pair{referenceElementNodes[0] * sqrt3_1, one}, + std::pair{referenceElementNodes[1] * sqrt3_1, one}, + std::pair{referenceElementNodes[2] * sqrt3_1, one}, + std::pair{referenceElementNodes[3] * sqrt3_1, one}, + std::pair{referenceElementNodes[4] * sqrt3_1, one}, + std::pair{referenceElementNodes[5] * sqrt3_1, one}, + std::pair{referenceElementNodes[6] * sqrt3_1, one}, + std::pair{referenceElementNodes[7] * sqrt3_1, one}, + }; + } + else if constexpr (Degree <= 5) + { + // Degrees 4-5: 3x3x3 Gauss-Legendre rule. + constexpr Real g = 0.77459666924; //sqrt(3./5.) + constexpr std::array node{ -g, static_cast(0), g }; + constexpr std::array weight{ static_cast(5./9.), static_cast(8./9.), static_cast(5./9.) }; + + std::array q{}; + sofa::Size k = 0; + for (sofa::Size i = 0; i < 3; ++i) + for (sofa::Size j = 0; j < 3; ++j) + for (sofa::Size l = 0; l < 3; ++l) + q[k++] = std::make_pair(ReferenceCoord(node[i], node[j], node[l]), weight[i] * weight[j] * weight[l]); + return q; + } + else + { + static_assert(Degree <= 5, "FiniteElement: no quadrature rule for the requested degree"); + } + } - return q; + // Quadrature rule selector by degree; view of the compile-time table. + static std::span quadratureRule(int degree) + { + switch (degree) + { + case 1: { static constexpr auto rule = quadraturePoints<1>(); return rule; } + case 2: + case 3: { static constexpr auto rule = quadraturePoints<3>(); return rule; } + case 4: + case 5: { static constexpr auto rule = quadraturePoints<5>(); return rule; } + default: + throw std::invalid_argument("FiniteElement::quadratureRule: unsupported degree"); + } } }; From 07ec7bc3f6d594809b765e58456d0d0beeaf7e78 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Tue, 11 Aug 2026 15:28:18 +0200 Subject: [PATCH 06/37] Use uint for degree --- Sofa/framework/FEM/src/sofa/fem/FiniteElement[Edge].h | 4 ++-- Sofa/framework/FEM/src/sofa/fem/FiniteElement[Hexahedron].h | 4 ++-- Sofa/framework/FEM/src/sofa/fem/FiniteElement[Quad].h | 4 ++-- Sofa/framework/FEM/src/sofa/fem/FiniteElement[Tetrahedron].h | 4 ++-- Sofa/framework/FEM/src/sofa/fem/FiniteElement[Triangle].h | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Edge].h b/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Edge].h index 492caa7a380..53570c30d05 100644 --- a/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Edge].h +++ b/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Edge].h @@ -57,7 +57,7 @@ struct FiniteElement return {{-static_cast(0.5)}, {static_cast(0.5)}}; } - template + template static constexpr auto quadraturePoints() { if constexpr (Degree <= 1) @@ -84,7 +84,7 @@ struct FiniteElement } // Quadrature rule selector by degree; view of the compile-time table. - static std::span quadratureRule(int degree) + static std::span quadratureRule(sofa::Size degree) { switch (degree) { diff --git a/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Hexahedron].h b/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Hexahedron].h index e3ab476e0f8..d855c9f8c72 100644 --- a/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Hexahedron].h +++ b/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Hexahedron].h @@ -98,7 +98,7 @@ struct FiniteElement return gradient; } - template + template static constexpr auto quadraturePoints() { if constexpr (Degree <= 1) @@ -148,7 +148,7 @@ struct FiniteElement } // Quadrature rule selector by degree; view of the compile-time table. - static std::span quadratureRule(int degree) + static std::span quadratureRule(sofa::Size degree) { switch (degree) { diff --git a/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Quad].h b/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Quad].h index 1485a881001..261eeae7036 100644 --- a/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Quad].h +++ b/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Quad].h @@ -69,7 +69,7 @@ struct FiniteElement }; } - template + template static constexpr auto quadraturePoints() { if constexpr (Degree <= 1) @@ -115,7 +115,7 @@ struct FiniteElement } // Quadrature rule selector by degree; view of the compile-time table. - static std::span quadratureRule(int degree) + static std::span quadratureRule(sofa::Size degree) { switch (degree) { diff --git a/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Tetrahedron].h b/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Tetrahedron].h index 4ab10ae3ed5..bca61671e0a 100644 --- a/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Tetrahedron].h +++ b/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Tetrahedron].h @@ -70,7 +70,7 @@ struct FiniteElement }; } - template + template static constexpr auto quadraturePoints() { if constexpr (Degree <= 1) @@ -100,7 +100,7 @@ struct FiniteElement } // Quadrature rule selector by degree; view of the compile-time table. - static std::span quadratureRule(int degree) + static std::span quadratureRule(sofa::Size degree) { switch (degree) { diff --git a/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Triangle].h b/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Triangle].h index 7495e802b26..84c9ec19a74 100644 --- a/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Triangle].h +++ b/Sofa/framework/FEM/src/sofa/fem/FiniteElement[Triangle].h @@ -66,7 +66,7 @@ struct FiniteElement }; } - template + template static constexpr auto quadraturePoints() { if constexpr (Degree <= 1) @@ -92,7 +92,7 @@ struct FiniteElement } // Quadrature rule selector by degree; view of the compile-time table. - static std::span quadratureRule(int degree) + static std::span quadratureRule(sofa::Size degree) { switch (degree) { From 5e78d66c1f5855ea3302f047c45225b940ee1e15 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Tue, 21 Jul 2026 14:41:34 +0200 Subject: [PATCH 07/37] [SolidMechanics] Add FEMSourceTerm component --- .../SolidMechanics/FEM/Elastic/CMakeLists.txt | 3 + .../fem/elastic/FEMSourceTerm.cpp | 58 ++++++ .../fem/elastic/FEMSourceTerm.h | 158 ++++++++++++++++ .../fem/elastic/FEMSourceTerm.inl | 169 ++++++++++++++++++ .../solidmechanics/fem/elastic/init.cpp | 2 + 5 files changed, 390 insertions(+) create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.cpp create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.h create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.inl diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt index 85447e27cdc..6f33b995089 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt @@ -16,6 +16,8 @@ set(HEADER_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CauchyStressEvaluator.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.inl + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTerm.h + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTerm.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FastTetrahedralCorotationalForceField.h @@ -72,6 +74,7 @@ set(SOURCE_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseLinearElasticityFEMForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BeamFEMForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.cpp + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTerm.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FastTetrahedralCorotationalForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMForceField.cpp diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.cpp new file mode 100644 index 00000000000..31935713335 --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.cpp @@ -0,0 +1,58 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#define SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_FEM_SOURCE_TERM_CPP + +#include + +#include +#include +#include + +namespace sofa::component::solidmechanics::fem::elastic +{ + +void registerFEMSourceTerm(sofa::core::ObjectFactory* factory) +{ + factory->registerObjects(sofa::core::ObjectRegistrationData("Consistent nodal load of a source term, integrated with the finite-element quadrature from a nodal source field") + .add< FEMSourceTerm >() + .add< FEMSourceTerm >() + .add< FEMSourceTerm >() + .add< FEMSourceTerm >() + .add< FEMSourceTerm >() + .add< FEMSourceTerm >() + .add< FEMSourceTerm >() + .add< FEMSourceTerm >() + .add< FEMSourceTerm >() + ); +} + +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.h new file mode 100644 index 00000000000..b99a9c42173 --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.h @@ -0,0 +1,158 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once + +#include +#include +#include +#include +#include + +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_FEM_SOURCE_TERM_CPP) +#include +#endif + +namespace sofa::component::solidmechanics::fem::elastic +{ + +/** + * @class FEMSourceTerm + * @brief Computes nodal source terms by integrating a given source density field stored at the nodes. + * + * This class assembles and stores a geometric matrix M using a quadrature rule over the element domain. + * The matrix is then used to multiply the source density b to compute a nodal source term F that + * contributes to the RHS of the weak form through the addForce function. + * + * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). + * @tparam TElementType The type of finite element (e.g., sofa::geometry::Tetrahedron). + */ +template +class FEMSourceTerm : + public sofa::core::behavior::ForceField, + public virtual sofa::core::behavior::TopologyAccessor +{ +public: + using DataTypes = TDataTypes; + using ElementType = TElementType; + SOFA_CLASS2(SOFA_TEMPLATE2(FEMSourceTerm, DataTypes, ElementType), + sofa::core::behavior::ForceField, + sofa::core::behavior::TopologyAccessor); + +protected: + using FiniteElement = sofa::fem::FiniteElement; + + static constexpr sofa::Size NumberOfNodesInElement = ElementType::NumberOfNodes; + + using GlobalMatrix = sofa::linearalgebra::CompressedRowSparseMatrixMechanical>; + +public: + + /** + * @brief Initializes the component. + * + * This method performs several initialization steps: + * 1. Initializes the base force field. + * 2. Initializes the topology accessor. + * 3. Resizes the nodal source density. + * 4. Assembles the global matrix M. + */ + void init() override; + + /** + * @brief Adds the force (f = M * b) to the RHS vector. + * + * This method computes the product of the geometric matrix and the source density vector, + * adding the result to the force vector `f`. + * + * @param mparams Mechanical parameters for the computation. + * @param f The force vector to which the source term will be added. + * @param x The current positions (unused in this implementation). + * @param v The current velocities (unused in this implementation). + */ + void addForce( + const sofa::core::MechanicalParams* mparams, + sofa::DataVecDeriv_t& f, + const sofa::DataVecCoord_t& x, + const sofa::DataVecDeriv_t& v) override; + + /** + * @brief A no-op as the load is prescribed on the rest configuration + */ + void addDForce(const sofa::core::MechanicalParams* mparams, + sofa::DataVecDeriv_t& df, + const sofa::DataVecDeriv_t& dx) override; + + /** + * @brief A no-op as the load is prescribed on the rest configuration + */ + void buildStiffnessMatrix(sofa::core::behavior::StiffnessMatrix* matrix) override; + + using sofa::core::behavior::ForceField::getPotentialEnergy; + /** + * @brief Not implemented, returns 0. + */ + SReal getPotentialEnergy(const sofa::core::MechanicalParams* mparams, + const sofa::DataVecCoord_t& x) const override; + + /** + * @brief Source term (per unit volume) sampled at each node. + */ + sofa::Data > d_nodalSourceDensity; + +protected: + + /** + * @brief Default constructor. + */ + FEMSourceTerm(); + + /** + * @brief Resizes the source density to the size of the mechanical state + */ + void resizeNodalSourceDensity(const std::size_t size); + + /** + * @brief Assembles and stores the geometry-only matrix \f$ M_{ij} = \int_{\Omega} N_i N_j \, d\Omega \f$ over each element on the rest configuration. + */ + void assembleGlobalMatrix(); + + /** + * @brief Geometry-only matrix \f$ M_{ij} = \int_{\Omega} N_i N_j \, d\Omega \f$ of the system. + * + * Stored in compressed sparse row format. Assembled once in init on the rest configuration. + */ + GlobalMatrix m_globalMatrix; +}; + +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_FEM_SOURCE_TERM_CPP) +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; +#endif + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.inl new file mode 100644 index 00000000000..d55cf8bdf7e --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.inl @@ -0,0 +1,169 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once +#include +#include + +namespace sofa::component::solidmechanics::fem::elastic +{ + +template +FEMSourceTerm::FEMSourceTerm() + : d_nodalSourceDensity(initData(&d_nodalSourceDensity, "nodalSourceDensity", + "Source term (per unit volume) sampled at each node. Interpolated inside the " + "element with the shape functions and integrated on the reference configuration.")) +{ +} + +template +void FEMSourceTerm::init() +{ + sofa::core::behavior::ForceField::init(); + + if (!this->isComponentStateInvalid()) + { + sofa::core::behavior::TopologyAccessor::init(); + } + + if (!this->isComponentStateInvalid() && this->mstate) + { + this->resizeNodalSourceDensity(this->mstate->getSize()); + } + + if (!this->isComponentStateInvalid() && this->l_topology && this->mstate) + { + this->assembleGlobalMatrix(); + } + + if (!this->isComponentStateInvalid()) + { + this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid); + } +} + +template +void FEMSourceTerm::resizeNodalSourceDensity(const std::size_t size) +{ + sofa::helper::WriteAccessor nodalSourceDensity = sofa::helper::getWriteAccessor(d_nodalSourceDensity); + + if (nodalSourceDensity.size() < size) + { + nodalSourceDensity.resize(size, sofa::Deriv_t{}); + } +} + +template +void FEMSourceTerm::assembleGlobalMatrix() +{ + // M_ij = integral of N_i N_j dV, evaluated on the rest configuration (geometry only). + const auto restPositionsAccessor = this->mstate->readRestPositions(); + const auto& elements = FiniteElement::getElementSequence(*this->l_topology); + + m_globalMatrix.clear(); + const auto size = this->mstate->getSize(); + m_globalMatrix.resize(size, size); + + for (const auto& element : elements) + { + const std::array, NumberOfNodesInElement> elementNodesRestCoordinates = + extractNodesVectorFromGlobalVector(element, restPositionsAccessor.ref()); + + sofa::type::Mat> elementMatrix; + + for (const auto& [quadraturePoint, weight] : FiniteElement::quadraturePoints()) + { + const auto N = FiniteElement::shapeFunctions(quadraturePoint); + const auto dN_dq_ref = FiniteElement::gradientShapeFunctions(quadraturePoint); + + const auto jacobian = FiniteElement::Helper::jacobianFromReferenceToPhysical( + elementNodesRestCoordinates, dN_dq_ref); + const auto detJ = sofa::type::absGeneralizedDeterminant(jacobian); + + elementMatrix += (static_cast>(weight) * static_cast>(detJ)) * sofa::type::dyad(N, N); + } + + for (sofa::Size i = 0; i < NumberOfNodesInElement; ++i) + { + for (sofa::Size j = 0; j < NumberOfNodesInElement; ++j) + { + m_globalMatrix.add(element[i], element[j], elementMatrix(i, j)); + } + } + } + + m_globalMatrix.compress(); +} + +template +void FEMSourceTerm::addForce(const sofa::core::MechanicalParams* mparams, + sofa::DataVecDeriv_t& f, + const sofa::DataVecCoord_t& x, + const sofa::DataVecDeriv_t& v) +{ + SOFA_UNUSED(mparams); + SOFA_UNUSED(x); + SOFA_UNUSED(v); + + const sofa::helper::ReadAccessor nodalSourceDensity = sofa::helper::getReadAccessor(d_nodalSourceDensity); + auto forceAccessor = sofa::helper::getWriteAccessor(f); + + // f_i = sum_j M_ij b_j : apply the cached matrix to the nodal source density. + for (std::size_t xi = 0; xi < m_globalMatrix.rowIndex.size(); ++xi) + { + const auto rowId = m_globalMatrix.rowIndex[xi]; + typename GlobalMatrix::Range rowRange(m_globalMatrix.rowBegin[xi], m_globalMatrix.rowBegin[xi + 1]); + for (typename GlobalMatrix::Index xj = rowRange.begin(); xj < rowRange.end(); ++xj) + { + const auto columnId = m_globalMatrix.colsIndex[xj]; + const auto& value = m_globalMatrix.colsValue[xj]; + + forceAccessor[rowId] += nodalSourceDensity[columnId] * value; + } + } +} + +template +void FEMSourceTerm::addDForce(const sofa::core::MechanicalParams* mparams, + sofa::DataVecDeriv_t& df, + const sofa::DataVecDeriv_t& dx) +{ + SOFA_UNUSED(mparams); + SOFA_UNUSED(df); + SOFA_UNUSED(dx); +} + +template +void FEMSourceTerm::buildStiffnessMatrix(sofa::core::behavior::StiffnessMatrix* matrix) +{ + SOFA_UNUSED(matrix); +} + +template +SReal FEMSourceTerm::getPotentialEnergy(const sofa::core::MechanicalParams* mparams, + const sofa::DataVecCoord_t& x) const +{ + SOFA_UNUSED(mparams); + SOFA_UNUSED(x); + return 0.0; +} + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp index 87d14c740ff..8022759d7f1 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp @@ -28,6 +28,7 @@ namespace sofa::component::solidmechanics::fem::elastic extern void registerBeamFEMForceField(sofa::core::ObjectFactory* factory); extern void registerCorotationalFEMForceField(sofa::core::ObjectFactory* factory); +extern void registerFEMSourceTerm(sofa::core::ObjectFactory* factory); extern void registerFastTetrahedralCorotationalForceField(sofa::core::ObjectFactory* factory); extern void registerHexahedralFEMForceField(sofa::core::ObjectFactory* factory); extern void registerHexahedralFEMForceFieldAndMass(sofa::core::ObjectFactory* factory); @@ -69,6 +70,7 @@ void registerObjects(sofa::core::ObjectFactory* factory) { registerBeamFEMForceField(factory); registerCorotationalFEMForceField(factory); + registerFEMSourceTerm(factory); registerFastTetrahedralCorotationalForceField(factory); registerHexahedralFEMForceField(factory); registerHexahedralFEMForceFieldAndMass(factory); From 9381375de6d64e75d70b08790f23050b27bb314f Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Tue, 21 Jul 2026 18:19:01 +0200 Subject: [PATCH 08/37] Split assembly in two steps --- .../fem/elastic/FEMSourceTerm.h | 11 +++++ .../fem/elastic/FEMSourceTerm.inl | 48 +++++++++++++++---- 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.h index b99a9c42173..724881a4bd2 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.h @@ -62,6 +62,7 @@ class FEMSourceTerm : static constexpr sofa::Size NumberOfNodesInElement = ElementType::NumberOfNodes; + using ElementMatrix = sofa::type::Mat>; using GlobalMatrix = sofa::linearalgebra::CompressedRowSparseMatrixMechanical>; public: @@ -135,6 +136,16 @@ class FEMSourceTerm : */ void assembleGlobalMatrix(); + /** + * @brief Computes the geometry-only matrix of each element. + */ + void calculateElementMatrix(const auto& elements, sofa::type::vector& elementMatrices); + + /** + * @brief Scatters the element matrices into the global matrix. + */ + void initializeGlobalMatrix(const auto& elements, const sofa::type::vector& elementMatrices); + /** * @brief Geometry-only matrix \f$ M_{ij} = \int_{\Omega} N_i N_j \, d\Omega \f$ of the system. * diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.inl index d55cf8bdf7e..528e0079750 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.inl +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.inl @@ -74,21 +74,32 @@ void FEMSourceTerm::resizeNodalSourceDensity(const std:: template void FEMSourceTerm::assembleGlobalMatrix() { - // M_ij = integral of N_i N_j dV, evaluated on the rest configuration (geometry only). - const auto restPositionsAccessor = this->mstate->readRestPositions(); const auto& elements = FiniteElement::getElementSequence(*this->l_topology); + sofa::type::vector elementMatrices; - m_globalMatrix.clear(); - const auto size = this->mstate->getSize(); - m_globalMatrix.resize(size, size); + // 1. compute the geometry-only matrix of each element + calculateElementMatrix(elements, elementMatrices); + + // 2. scatter the element matrices into the global matrix + initializeGlobalMatrix(elements, elementMatrices); +} - for (const auto& element : elements) +template +void FEMSourceTerm::calculateElementMatrix( + const auto& elements, sofa::type::vector& elementMatrices) +{ + const auto restPositionsAccessor = this->mstate->readRestPositions(); + elementMatrices.resize(elements.size()); + + for (std::size_t elementId = 0; elementId < elements.size(); ++elementId) { + const auto& element = elements[elementId]; + auto& elementMatrix = elementMatrices[elementId]; + const std::array, NumberOfNodesInElement> elementNodesRestCoordinates = extractNodesVectorFromGlobalVector(element, restPositionsAccessor.ref()); - sofa::type::Mat> elementMatrix; - + // M_ij = integral of N_i N_j dV, evaluated on the rest configuration (geometry only). for (const auto& [quadraturePoint, weight] : FiniteElement::quadraturePoints()) { const auto N = FiniteElement::shapeFunctions(quadraturePoint); @@ -98,8 +109,25 @@ void FEMSourceTerm::assembleGlobalMatrix() elementNodesRestCoordinates, dN_dq_ref); const auto detJ = sofa::type::absGeneralizedDeterminant(jacobian); - elementMatrix += (static_cast>(weight) * static_cast>(detJ)) * sofa::type::dyad(N, N); + const auto NT_N = sofa::type::dyad(N, N); + + elementMatrix += (weight * detJ) * NT_N; } + } +} + +template +void FEMSourceTerm::initializeGlobalMatrix( + const auto& elements, const sofa::type::vector& elementMatrices) +{ + m_globalMatrix.clear(); + const auto size = this->mstate->getSize(); + m_globalMatrix.resize(size, size); + + for (std::size_t elementId = 0; elementId < elements.size(); ++elementId) + { + const auto& element = elements[elementId]; + const auto& elementMatrix = elementMatrices[elementId]; for (sofa::Size i = 0; i < NumberOfNodesInElement; ++i) { @@ -126,7 +154,7 @@ void FEMSourceTerm::addForce(const sofa::core::Mechanica const sofa::helper::ReadAccessor nodalSourceDensity = sofa::helper::getReadAccessor(d_nodalSourceDensity); auto forceAccessor = sofa::helper::getWriteAccessor(f); - // f_i = sum_j M_ij b_j : apply the cached matrix to the nodal source density. + // f_i = sum_j M_ij b_j : apply the global matrix to the nodal source density. for (std::size_t xi = 0; xi < m_globalMatrix.rowIndex.size(); ++xi) { const auto rowId = m_globalMatrix.rowIndex[xi]; From bb7586fc1831cebae46646224025da3cd513d554 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Thu, 13 Aug 2026 11:17:32 +0200 Subject: [PATCH 09/37] [SolidMechanics] Enable choosing quadrature order in FEMSourceTerm --- .../solidmechanics/fem/elastic/FEMSourceTerm.h | 5 +++++ .../solidmechanics/fem/elastic/FEMSourceTerm.inl | 16 ++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.h index 724881a4bd2..9b4e8f358d6 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.h @@ -119,6 +119,11 @@ class FEMSourceTerm : */ sofa::Data > d_nodalSourceDensity; + /** + * @brief Degree of the quadrature rule integrating the element matrix M. + */ + sofa::Data d_quadratureDegree; + protected: /** diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.inl index 528e0079750..6e029f0bb98 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.inl +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.inl @@ -28,10 +28,20 @@ namespace sofa::component::solidmechanics::fem::elastic template FEMSourceTerm::FEMSourceTerm() - : d_nodalSourceDensity(initData(&d_nodalSourceDensity, "nodalSourceDensity", + : d_nodalSourceDensity(initData(&d_nodalSourceDensity, "nodalSourceDensity", "Source term (per unit volume) sampled at each node. Interpolated inside the " "element with the shape functions and integrated on the reference configuration.")) + , d_quadratureDegree(initData(&d_quadratureDegree, static_cast(1), "quadratureDegree", + "Degree of the quadrature rule integrating the element matrix M.")) { + this->addUpdateCallback("reassembleSourceMatrix", {&d_quadratureDegree}, + [this](const sofa::core::DataTracker&) + { + if (!this->isComponentStateInvalid() && this->l_topology && this->mstate) + assembleGlobalMatrix(); + + return this->getComponentState(); + }, {}); } template @@ -91,6 +101,8 @@ void FEMSourceTerm::calculateElementMatrix( const auto restPositionsAccessor = this->mstate->readRestPositions(); elementMatrices.resize(elements.size()); + const auto quadratureRule = FiniteElement::quadratureRule(d_quadratureDegree.getValue()); + for (std::size_t elementId = 0; elementId < elements.size(); ++elementId) { const auto& element = elements[elementId]; @@ -100,7 +112,7 @@ void FEMSourceTerm::calculateElementMatrix( extractNodesVectorFromGlobalVector(element, restPositionsAccessor.ref()); // M_ij = integral of N_i N_j dV, evaluated on the rest configuration (geometry only). - for (const auto& [quadraturePoint, weight] : FiniteElement::quadraturePoints()) + for (const auto& [quadraturePoint, weight] : quadratureRule) { const auto N = FiniteElement::shapeFunctions(quadraturePoint); const auto dN_dq_ref = FiniteElement::gradientShapeFunctions(quadraturePoint); From 49cf3c8e3729a0049694c5d8aa6b8d108a7a64ff Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Tue, 1 Sep 2026 13:33:47 +0200 Subject: [PATCH 10/37] [FEM] Add constant source term integration --- .../SolidMechanics/FEM/Elastic/CMakeLists.txt | 8 +- .../fem/elastic/ConstantSourceTerm.cpp | 45 ++++++ .../fem/elastic/ConstantSourceTerm.h | 66 +++++++++ ...ceTerm.cpp => FEMSourceTermIntegrator.cpp} | 44 +++--- ...SourceTerm.h => FEMSourceTermIntegrator.h} | 102 +++++++++----- ...ceTerm.inl => FEMSourceTermIntegrator.inl} | 131 +++++++++++++----- .../solidmechanics/fem/elastic/init.cpp | 6 +- .../FEM/FEMSourceTermIntegrator.scn | 110 +++++++++++++++ 8 files changed, 413 insertions(+), 99 deletions(-) create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ConstantSourceTerm.cpp create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ConstantSourceTerm.h rename Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/{FEMSourceTerm.cpp => FEMSourceTermIntegrator.cpp} (61%) rename Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/{FEMSourceTerm.h => FEMSourceTermIntegrator.h} (63%) rename Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/{FEMSourceTerm.inl => FEMSourceTermIntegrator.inl} (65%) create mode 100644 examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt index 6f33b995089..6cbf65b8f00 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt @@ -14,10 +14,11 @@ set(HEADER_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BeamFEMForceField.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BeamFEMForceField.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CauchyStressEvaluator.h + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/ConstantSourceTerm.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.inl - ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTerm.h - ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTerm.inl + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.h + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FastTetrahedralCorotationalForceField.h @@ -73,8 +74,9 @@ set(SOURCE_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseElementLinearFEMForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseLinearElasticityFEMForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BeamFEMForceField.cpp + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/ConstantSourceTerm.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.cpp - ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTerm.cpp + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FastTetrahedralCorotationalForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMForceField.cpp diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ConstantSourceTerm.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ConstantSourceTerm.cpp new file mode 100644 index 00000000000..569b3d7e86f --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ConstantSourceTerm.cpp @@ -0,0 +1,45 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#define SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_CONSTANT_SOURCE_TERM_CPP + +#include + +#include +#include + +namespace sofa::component::solidmechanics::fem::elastic +{ + +void registerConstantSourceTerm(sofa::core::ObjectFactory* factory) +{ + factory->registerObjects(sofa::core::ObjectRegistrationData("Constant source term (per unit volume) prescribed at the nodes") + .add< ConstantSourceTerm >() + .add< ConstantSourceTerm >() + .add< ConstantSourceTerm >() + ); +} + +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API ConstantSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API ConstantSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API ConstantSourceTerm; + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ConstantSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ConstantSourceTerm.h new file mode 100644 index 00000000000..e03bf47622a --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ConstantSourceTerm.h @@ -0,0 +1,66 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once + +#include +#include +#include + +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_CONSTANT_SOURCE_TERM_CPP) +#include +#endif + +namespace sofa::component::solidmechanics::fem::elastic +{ + +/** + * @class ConstantSourceTerm + * @brief A source density prescribed at the nodes, independent of the current displacement. + * + * The density is the inherited "property" Data (see BaseNodalProperty): a vector shorter than the + * mechanical state broadcasts its last value to the remaining nodes, so a uniform density is + * written with a single value. Link it to a FEMSourceTermIntegrator through l_constantSources. + * + * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). + */ +template +class ConstantSourceTerm : public sofa::core::BaseNodalProperty> +{ +public: + using DataTypes = TDataTypes; + using Deriv = sofa::Deriv_t; + + SOFA_CLASS(SOFA_TEMPLATE(ConstantSourceTerm, DataTypes), + SOFA_TEMPLATE(sofa::core::BaseNodalProperty, Deriv)); + +protected: + + ConstantSourceTerm() : sofa::core::BaseNodalProperty(Deriv{}) {} +}; + +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_CONSTANT_SOURCE_TERM_CPP) +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API ConstantSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API ConstantSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API ConstantSourceTerm; +#endif + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.cpp similarity index 61% rename from Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.cpp rename to Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.cpp index 31935713335..f7a908ed703 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.cpp @@ -19,9 +19,9 @@ * * * Contact information: contact@sofa-framework.org * ******************************************************************************/ -#define SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_FEM_SOURCE_TERM_CPP +#define SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_FEM_SOURCE_TERM_INTEGRATOR_CPP -#include +#include #include #include @@ -30,29 +30,29 @@ namespace sofa::component::solidmechanics::fem::elastic { -void registerFEMSourceTerm(sofa::core::ObjectFactory* factory) +void registerFEMSourceTermIntegrator(sofa::core::ObjectFactory* factory) { - factory->registerObjects(sofa::core::ObjectRegistrationData("Consistent nodal load of a source term, integrated with the finite-element quadrature from a nodal source field") - .add< FEMSourceTerm >() - .add< FEMSourceTerm >() - .add< FEMSourceTerm >() - .add< FEMSourceTerm >() - .add< FEMSourceTerm >() - .add< FEMSourceTerm >() - .add< FEMSourceTerm >() - .add< FEMSourceTerm >() - .add< FEMSourceTerm >() + factory->registerObjects(sofa::core::ObjectRegistrationData("Consistent nodal load of a source term, integrated with the finite-element quadrature from a nodal source density field") + .add< FEMSourceTermIntegrator >() + .add< FEMSourceTermIntegrator >() + .add< FEMSourceTermIntegrator >() + .add< FEMSourceTermIntegrator >() + .add< FEMSourceTermIntegrator >() + .add< FEMSourceTermIntegrator >() + .add< FEMSourceTermIntegrator >() + .add< FEMSourceTermIntegrator >() + .add< FEMSourceTermIntegrator >() ); } -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTermIntegrator; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTermIntegrator; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTermIntegrator; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTermIntegrator; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTermIntegrator; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTermIntegrator; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTermIntegrator; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTermIntegrator; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTermIntegrator; } // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h similarity index 63% rename from Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.h rename to Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h index 9b4e8f358d6..7873aec5cdf 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h @@ -22,12 +22,14 @@ #pragma once #include +#include #include #include +#include #include #include -#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_FEM_SOURCE_TERM_CPP) +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_FEM_SOURCE_TERM_INTEGRATOR_CPP) #include #endif @@ -35,58 +37,69 @@ namespace sofa::component::solidmechanics::fem::elastic { /** - * @class FEMSourceTerm - * @brief Computes nodal source terms by integrating a given source density field stored at the nodes. + * @class FEMSourceTermIntegrator + * @brief Integrates source terms into consistent nodal loads. * - * This class assembles and stores a geometric matrix M using a quadrature rule over the element domain. - * The matrix is then used to multiply the source density b to compute a nodal source term F that - * contributes to the RHS of the weak form through the addForce function. + * A source term contributes \f$ \int_{\Omega} N_a \, r \, d\Omega \f$ to the right-hand side, where + * r is the per-node density carried by a linked ConstantSourceTerm (through l_constantSources) and + * does not depend on the displacement. Every term is thus summed and integrated once in init(); + * addForce merely accumulates the result. * * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). * @tparam TElementType The type of finite element (e.g., sofa::geometry::Tetrahedron). */ template -class FEMSourceTerm : +class FEMSourceTermIntegrator : public sofa::core::behavior::ForceField, public virtual sofa::core::behavior::TopologyAccessor { public: using DataTypes = TDataTypes; using ElementType = TElementType; - SOFA_CLASS2(SOFA_TEMPLATE2(FEMSourceTerm, DataTypes, ElementType), + SOFA_CLASS2(SOFA_TEMPLATE2(FEMSourceTermIntegrator, DataTypes, ElementType), sofa::core::behavior::ForceField, sofa::core::behavior::TopologyAccessor); protected: using FiniteElement = sofa::fem::FiniteElement; + using Real = sofa::Real_t; static constexpr sofa::Size NumberOfNodesInElement = ElementType::NumberOfNodes; + static constexpr sofa::Size spatial_dimensions = DataTypes::spatial_dimensions; using ElementMatrix = sofa::type::Mat>; using GlobalMatrix = sofa::linearalgebra::CompressedRowSparseMatrixMechanical>; public: + /** + * @brief Source terms integrated by this component. + * + * If left empty, the ConstantSourceTerm components found in the current context are used. + */ + sofa::MultiLink, ConstantSourceTerm, + sofa::BaseLink::FLAG_STOREPATH | sofa::BaseLink::FLAG_STRONGLINK> l_constantSources; + /** * @brief Initializes the component. * * This method performs several initialization steps: * 1. Initializes the base force field. * 2. Initializes the topology accessor. - * 3. Resizes the nodal source density. + * 3. Validates the linked source terms. * 4. Assembles the global matrix M. + * 5. Integrates the source terms into the constant nodal force. */ void init() override; /** - * @brief Adds the force (f = M * b) to the RHS vector. + * @brief Adds the nodal source term to the RHS vector. * - * This method computes the product of the geometric matrix and the source density vector, - * adding the result to the force vector `f`. + * The source terms were integrated once in init and are only accumulated here. * * @param mparams Mechanical parameters for the computation. * @param f The force vector to which the source term will be added. - * @param x The current positions (unused in this implementation). + * @param x The current positions (unused: the load is prescribed on the rest configuration). * @param v The current velocities (unused in this implementation). */ void addForce( @@ -96,29 +109,24 @@ class FEMSourceTerm : const sofa::DataVecDeriv_t& v) override; /** - * @brief A no-op as the load is prescribed on the rest configuration - */ + * @brief No-op: TODO for non-const source terms + */ void addDForce(const sofa::core::MechanicalParams* mparams, sofa::DataVecDeriv_t& df, const sofa::DataVecDeriv_t& dx) override; /** - * @brief A no-op as the load is prescribed on the rest configuration - */ + * @brief No-op: TODO for non-const source terms + */ void buildStiffnessMatrix(sofa::core::behavior::StiffnessMatrix* matrix) override; using sofa::core::behavior::ForceField::getPotentialEnergy; /** - * @brief Not implemented, returns 0. + * @brief Potential energy of the constant nodal load, \f$ V = -\sum_a F_a \cdot (x_a - x_{0,a}) \f$. */ SReal getPotentialEnergy(const sofa::core::MechanicalParams* mparams, const sofa::DataVecCoord_t& x) const override; - /** - * @brief Source term (per unit volume) sampled at each node. - */ - sofa::Data > d_nodalSourceDensity; - /** * @brief Degree of the quadrature rule integrating the element matrix M. */ @@ -129,18 +137,32 @@ class FEMSourceTerm : /** * @brief Default constructor. */ - FEMSourceTerm(); + FEMSourceTermIntegrator(); /** - * @brief Resizes the source density to the size of the mechanical state + * @brief Ensures that valid source terms are linked, falling back to the current context. */ - void resizeNodalSourceDensity(const std::size_t size); + void validateSources(); /** * @brief Assembles and stores the geometry-only matrix \f$ M_{ij} = \int_{\Omega} N_i N_j \, d\Omega \f$ over each element on the rest configuration. */ void assembleGlobalMatrix(); + /** + * @brief Sums every displacement-independent source density and integrates it once into m_constantForce. + * + * Integration is linear, so the sum of the terms integrates to the sum of their contributions: + * a single matrix-vector product covers all of them. + */ + void assembleConstantForce(); + + /** + * @brief Applies the geometry-only matrix M to a nodal source term. + */ + void applyGlobalMatrix(const sofa::VecDeriv_t& nodalSourceTerm, + sofa::VecDeriv_t& result) const; + /** * @brief Computes the geometry-only matrix of each element. */ @@ -157,18 +179,26 @@ class FEMSourceTerm : * Stored in compressed sparse row format. Assembled once in init on the rest configuration. */ GlobalMatrix m_globalMatrix; + + /** + * @brief Nodal load of every term in l_constantSources, integrated once in init. + * + * @note Their contribution is integrated once, so editing the property of a linked term at run + * time has no effect until the scene is reinitialised. + */ + sofa::VecDeriv_t m_constantForce; }; -#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_FEM_SOURCE_TERM_CPP) -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTerm; +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_FEM_SOURCE_TERM_INTEGRATOR_CPP) +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTermIntegrator; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTermIntegrator; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTermIntegrator; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTermIntegrator; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTermIntegrator; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTermIntegrator; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTermIntegrator; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTermIntegrator; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API FEMSourceTermIntegrator; #endif } // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl similarity index 65% rename from Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.inl rename to Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl index 6e029f0bb98..6e69510ed09 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTerm.inl +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl @@ -20,32 +20,35 @@ * Contact information: contact@sofa-framework.org * ******************************************************************************/ #pragma once -#include +#include #include namespace sofa::component::solidmechanics::fem::elastic { template -FEMSourceTerm::FEMSourceTerm() - : d_nodalSourceDensity(initData(&d_nodalSourceDensity, "nodalSourceDensity", - "Source term (per unit volume) sampled at each node. Interpolated inside the " - "element with the shape functions and integrated on the reference configuration.")) +FEMSourceTermIntegrator::FEMSourceTermIntegrator() + : l_constantSources(initLink("constantSources", "Source terms of the weak form integrated by " + "this component. If empty, the ones found in the current context are used.")) , d_quadratureDegree(initData(&d_quadratureDegree, static_cast(1), "quadratureDegree", "Degree of the quadrature rule integrating the element matrix M.")) { + // Re-compute global matrix and constant forces in case of quadrature degree change this->addUpdateCallback("reassembleSourceMatrix", {&d_quadratureDegree}, [this](const sofa::core::DataTracker&) { if (!this->isComponentStateInvalid() && this->l_topology && this->mstate) + { assembleGlobalMatrix(); + assembleConstantForce(); + } return this->getComponentState(); }, {}); } template -void FEMSourceTerm::init() +void FEMSourceTermIntegrator::init() { sofa::core::behavior::ForceField::init(); @@ -54,14 +57,15 @@ void FEMSourceTerm::init() sofa::core::behavior::TopologyAccessor::init(); } - if (!this->isComponentStateInvalid() && this->mstate) + if (!this->isComponentStateInvalid()) { - this->resizeNodalSourceDensity(this->mstate->getSize()); + this->validateSources(); } if (!this->isComponentStateInvalid() && this->l_topology && this->mstate) { this->assembleGlobalMatrix(); + this->assembleConstantForce(); } if (!this->isComponentStateInvalid()) @@ -71,18 +75,28 @@ void FEMSourceTerm::init() } template -void FEMSourceTerm::resizeNodalSourceDensity(const std::size_t size) +void FEMSourceTermIntegrator::validateSources() { - sofa::helper::WriteAccessor nodalSourceDensity = sofa::helper::getWriteAccessor(d_nodalSourceDensity); - - if (nodalSourceDensity.size() < size) + // Gather all ConstantSourceTerm components in Context if empty + if (l_constantSources.empty()) { - nodalSourceDensity.resize(size, sofa::Deriv_t{}); + const auto sourcesInContext = this->getContext()->template getObjects >( + sofa::core::objectmodel::BaseContext::Local); + + for (const auto& source : sourcesInContext) + l_constantSources.add(source); + + msg_info_when(!sourcesInContext.empty(), this) << "No source term linked: the " + << sourcesInContext.size() << " one(s) found in the current context are used."; } + + msg_warning_when(l_constantSources.empty(), this) + << "No source term linked, and none found in the current context '" + << this->getContext()->getName() << "'. This component has zero force contribution."; } template -void FEMSourceTerm::assembleGlobalMatrix() +void FEMSourceTermIntegrator::assembleGlobalMatrix() { const auto& elements = FiniteElement::getElementSequence(*this->l_topology); sofa::type::vector elementMatrices; @@ -95,7 +109,7 @@ void FEMSourceTerm::assembleGlobalMatrix() } template -void FEMSourceTerm::calculateElementMatrix( +void FEMSourceTermIntegrator::calculateElementMatrix( const auto& elements, sofa::type::vector& elementMatrices) { const auto restPositionsAccessor = this->mstate->readRestPositions(); @@ -103,7 +117,7 @@ void FEMSourceTerm::calculateElementMatrix( const auto quadratureRule = FiniteElement::quadratureRule(d_quadratureDegree.getValue()); - for (std::size_t elementId = 0; elementId < elements.size(); ++elementId) + for (sofa::Index elementId = 0; elementId < elements.size(); ++elementId) { const auto& element = elements[elementId]; auto& elementMatrix = elementMatrices[elementId]; @@ -129,14 +143,14 @@ void FEMSourceTerm::calculateElementMatrix( } template -void FEMSourceTerm::initializeGlobalMatrix( +void FEMSourceTermIntegrator::initializeGlobalMatrix( const auto& elements, const sofa::type::vector& elementMatrices) { m_globalMatrix.clear(); const auto size = this->mstate->getSize(); m_globalMatrix.resize(size, size); - for (std::size_t elementId = 0; elementId < elements.size(); ++elementId) + for (sofa::Index elementId = 0; elementId < elements.size(); ++elementId) { const auto& element = elements[elementId]; const auto& elementMatrix = elementMatrices[elementId]; @@ -154,7 +168,44 @@ void FEMSourceTerm::initializeGlobalMatrix( } template -void FEMSourceTerm::addForce(const sofa::core::MechanicalParams* mparams, +void FEMSourceTermIntegrator::applyGlobalMatrix( + const sofa::VecDeriv_t& nodalSourceTerm, sofa::VecDeriv_t& result) const +{ + // f_i = sum_j M_ij b_j : apply the global matrix to the nodal source term. + for (sofa::Index xi = 0; xi < m_globalMatrix.rowIndex.size(); ++xi) + { + const auto rowId = m_globalMatrix.rowIndex[xi]; + typename GlobalMatrix::Range rowRange(m_globalMatrix.rowBegin[xi], m_globalMatrix.rowBegin[xi + 1]); + for (typename GlobalMatrix::Index xj = rowRange.begin(); xj < rowRange.end(); ++xj) + { + const auto columnId = m_globalMatrix.colsIndex[xj]; + const auto& value = m_globalMatrix.colsValue[xj]; + + result[rowId] += nodalSourceTerm[columnId] * value; + } + } +} + +template +void FEMSourceTermIntegrator::assembleConstantForce() +{ + const auto size = this->mstate->getSize(); + + // Aggregate all contributions to one vector before applying the global matrix + sofa::VecDeriv_t sourceTerms(size, sofa::Deriv_t{}); + + for (const auto& source : l_constantSources) + { + for (sofa::Index i = 0; i < size; ++i) + sourceTerms[i] += source->getNodeProperty(i); + } + + m_constantForce.assign(size, sofa::Deriv_t{}); + applyGlobalMatrix(sourceTerms, m_constantForce); +} + +template +void FEMSourceTermIntegrator::addForce(const sofa::core::MechanicalParams* mparams, sofa::DataVecDeriv_t& f, const sofa::DataVecCoord_t& x, const sofa::DataVecDeriv_t& v) @@ -163,26 +214,21 @@ void FEMSourceTerm::addForce(const sofa::core::Mechanica SOFA_UNUSED(x); SOFA_UNUSED(v); - const sofa::helper::ReadAccessor nodalSourceDensity = sofa::helper::getReadAccessor(d_nodalSourceDensity); + if (this->isComponentStateInvalid()) + { + return; + } + auto forceAccessor = sofa::helper::getWriteAccessor(f); - // f_i = sum_j M_ij b_j : apply the global matrix to the nodal source density. - for (std::size_t xi = 0; xi < m_globalMatrix.rowIndex.size(); ++xi) + for (sofa::Index i = 0; i < m_constantForce.size(); ++i) { - const auto rowId = m_globalMatrix.rowIndex[xi]; - typename GlobalMatrix::Range rowRange(m_globalMatrix.rowBegin[xi], m_globalMatrix.rowBegin[xi + 1]); - for (typename GlobalMatrix::Index xj = rowRange.begin(); xj < rowRange.end(); ++xj) - { - const auto columnId = m_globalMatrix.colsIndex[xj]; - const auto& value = m_globalMatrix.colsValue[xj]; - - forceAccessor[rowId] += nodalSourceDensity[columnId] * value; - } + forceAccessor[i] += m_constantForce[i]; } } template -void FEMSourceTerm::addDForce(const sofa::core::MechanicalParams* mparams, +void FEMSourceTermIntegrator::addDForce(const sofa::core::MechanicalParams* mparams, sofa::DataVecDeriv_t& df, const sofa::DataVecDeriv_t& dx) { @@ -192,18 +238,31 @@ void FEMSourceTerm::addDForce(const sofa::core::Mechanic } template -void FEMSourceTerm::buildStiffnessMatrix(sofa::core::behavior::StiffnessMatrix* matrix) +void FEMSourceTermIntegrator::buildStiffnessMatrix(sofa::core::behavior::StiffnessMatrix* matrix) { SOFA_UNUSED(matrix); } template -SReal FEMSourceTerm::getPotentialEnergy(const sofa::core::MechanicalParams* mparams, +SReal FEMSourceTermIntegrator::getPotentialEnergy(const sofa::core::MechanicalParams* mparams, const sofa::DataVecCoord_t& x) const { SOFA_UNUSED(mparams); - SOFA_UNUSED(x); - return 0.0; + + if (this->isComponentStateInvalid()) + { + return 0.0; + } + + const sofa::helper::ReadAccessor positionAccessor = sofa::helper::getReadAccessor(x); + const auto restPositionAccessor = this->mstate->readRestPositions(); + + SReal energy = 0.0; + for (sofa::Index i = 0; i < m_constantForce.size(); ++i) + { + energy -= dot(m_constantForce[i], positionAccessor[i] - restPositionAccessor.ref()[i]); + } + return energy; } } // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp index 8022759d7f1..181de014371 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp @@ -27,8 +27,9 @@ namespace sofa::component::solidmechanics::fem::elastic { extern void registerBeamFEMForceField(sofa::core::ObjectFactory* factory); +extern void registerConstantSourceTerm(sofa::core::ObjectFactory* factory); extern void registerCorotationalFEMForceField(sofa::core::ObjectFactory* factory); -extern void registerFEMSourceTerm(sofa::core::ObjectFactory* factory); +extern void registerFEMSourceTermIntegrator(sofa::core::ObjectFactory* factory); extern void registerFastTetrahedralCorotationalForceField(sofa::core::ObjectFactory* factory); extern void registerHexahedralFEMForceField(sofa::core::ObjectFactory* factory); extern void registerHexahedralFEMForceFieldAndMass(sofa::core::ObjectFactory* factory); @@ -69,8 +70,9 @@ const char* getModuleVersion() void registerObjects(sofa::core::ObjectFactory* factory) { registerBeamFEMForceField(factory); + registerConstantSourceTerm(factory); registerCorotationalFEMForceField(factory); - registerFEMSourceTerm(factory); + registerFEMSourceTermIntegrator(factory); registerFastTetrahedralCorotationalForceField(factory); registerHexahedralFEMForceField(factory); registerHexahedralFEMForceFieldAndMass(factory); diff --git a/examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn b/examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn new file mode 100644 index 00000000000..172a575b79c --- /dev/null +++ b/examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 62d3f2c3ec9a6327821ba88eb242be7d814801ef Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Tue, 1 Sep 2026 13:58:25 +0200 Subject: [PATCH 11/37] Demonstrate multiple sources --- .../FEM/FEMSourceTermIntegrator.scn | 229 ++++++++++++------ 1 file changed, 155 insertions(+), 74 deletions(-) diff --git a/examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn b/examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn index 172a575b79c..964c373b4b0 100644 --- a/examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn +++ b/examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn @@ -30,81 +30,162 @@ - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 7f22f118f0de53efde6960c01da36ce025644e18 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Tue, 1 Sep 2026 14:41:19 +0200 Subject: [PATCH 12/37] Add scene to show gravity replication --- .../SolidMechanics/FEM/ConstantSourceTerm.scn | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 examples/Component/SolidMechanics/FEM/ConstantSourceTerm.scn diff --git a/examples/Component/SolidMechanics/FEM/ConstantSourceTerm.scn b/examples/Component/SolidMechanics/FEM/ConstantSourceTerm.scn new file mode 100644 index 00000000000..8fe1c5fa6eb --- /dev/null +++ b/examples/Component/SolidMechanics/FEM/ConstantSourceTerm.scn @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 03c27359341524203b33bf2d859c63f0ddcde368 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Tue, 1 Sep 2026 15:54:04 +0200 Subject: [PATCH 13/37] Transform example to unit test --- .../FEM/Elastic/tests/CMakeLists.txt | 1 + .../tests/FEMSourceTermIntegrator_test.cpp | 149 ++++++++++++++++++ .../FEM/FEMSourceTermIntegrator.scn | 79 ---------- 3 files changed, 150 insertions(+), 79 deletions(-) create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/tests/FEMSourceTermIntegrator_test.cpp diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/tests/CMakeLists.txt b/Sofa/Component/SolidMechanics/FEM/Elastic/tests/CMakeLists.txt index d5e6a1bcb25..b8a38e68eb4 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/tests/CMakeLists.txt +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/tests/CMakeLists.txt @@ -6,6 +6,7 @@ set(SOURCE_FILES BaseTetrahedronFEMForceField_test.h BeamFEMForceField_test.cpp FastTetrahedralCorotationalForceField_test.cpp + FEMSourceTermIntegrator_test.cpp HexahedronFEMForceField_test.cpp StrainDisplacement_test.cpp TetrahedralCorotationalFEMForceField_test.cpp diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/tests/FEMSourceTermIntegrator_test.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/tests/FEMSourceTermIntegrator_test.cpp new file mode 100644 index 00000000000..99d5598fdd3 --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/tests/FEMSourceTermIntegrator_test.cpp @@ -0,0 +1,149 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace sofa +{ +using sofa::simpleapi::createObject; +using sofa::simpleapi::createRootNode; +using DataTypes = defaulttype::Vec2Types; +using Integrator = component::solidmechanics::fem::elastic::FEMSourceTermIntegrator; +using DOF = component::statecontainer::MechanicalObject; +using VecCoord = DataTypes::VecCoord; +using VecDeriv = DataTypes::VecDeriv; + +class FEMSourceTermIntegrator_test : public testing::BaseTest +{ +protected: + simulation::Simulation* m_simulation = nullptr; + simulation::Node::SPtr m_root; + + void doSetUp() override + { + m_simulation = sofa::simulation::getSimulation(); + } + + void doTearDown() override + { + if (m_root != nullptr) + sofa::simulation::node::unload(m_root); + } + + simulation::Node::SPtr makeMesh() + { + this->loadPlugins({"Sofa.Component.StateContainer", + "Sofa.Component.Topology.Container.Constant", "Sofa.Component.SolidMechanics.FEM.Elastic"}); + + auto root = createRootNode(m_simulation, "root"); + createObject(root, "MechanicalObject", {{"template", "Vec2"}, {"position", "0 0 1 0 1 1 0 1"}}); + createObject(root, "MeshTopology", {{"name", "mesh"}, {"triangles", "0 1 2 0 2 3"}}); + return root; + } + + static VecDeriv addForce(Integrator* integrator) + { + core::MechanicalParams mparams; + Data f; + f.setValue(VecDeriv(4)); + Data x; + Data v; + integrator->addForce(&mparams, f, x, v); + return f.getValue(); + } +}; + +// Splitting one ConstantSourceTerm into several must not change the integrated force. +TEST_F(FEMSourceTermIntegrator_test, MultipleSourcesSumToOne) +{ + m_root = makeMesh(); + + createObject(m_root, "ConstantSourceTerm", {{"name", "full"}, {"template", "Vec2"}, {"property", "300 -600"}}); + auto* one = dynamic_cast(createObject(m_root, "FEMSourceTermIntegrator", + {{"name", "one"}, {"template", "Vec2,Triangle"}, {"topology", "@mesh"}, {"constantSources", "@full"}}).get()); + + createObject(m_root, "ConstantSourceTerm", {{"name", "a"}, {"template", "Vec2"}, {"property", "100 -200"}}); + createObject(m_root, "ConstantSourceTerm", {{"name", "b"}, {"template", "Vec2"}, {"property", "100 -200"}}); + createObject(m_root, "ConstantSourceTerm", {{"name", "c"}, {"template", "Vec2"}, {"property", "100 -200"}}); + auto* three = dynamic_cast(createObject(m_root, "FEMSourceTermIntegrator", + {{"name", "three"}, {"template", "Vec2,Triangle"}, {"topology", "@mesh"}, {"constantSources", "@a @b @c"}}).get()); + + simulation::node::initRoot(m_root.get()); + ASSERT_NE(one, nullptr); + ASSERT_NE(three, nullptr); + + const VecDeriv fOne = addForce(one); + const VecDeriv fThree = addForce(three); + ASSERT_EQ(fOne.size(), fThree.size()); + for (std::size_t i = 0; i < fOne.size(); ++i) + EXPECT_EQ(fOne[i], fThree[i]); +} + +// dE = -dX . F, the conservative-force identity getPotentialEnergy relies on. +TEST_F(FEMSourceTermIntegrator_test, PotentialEnergyMatchesWork) +{ + m_root = makeMesh(); + + createObject(m_root, "ConstantSourceTerm", {{"name", "bodyForce"}, {"template", "Vec2"}, {"property", "300 -600"}}); + auto* integrator = dynamic_cast(createObject(m_root, "FEMSourceTermIntegrator", + {{"name", "source"}, {"template", "Vec2,Triangle"}, {"topology", "@mesh"}, {"constantSources", "@bodyForce"}}).get()); + + simulation::node::initRoot(m_root.get()); + ASSERT_NE(integrator, nullptr); + + const VecDeriv f = addForce(integrator); + + VecCoord x0(4); + testing::copyFromData(x0, m_root->get()->readPositions()); + + VecCoord x1 = x0; + for (auto& xi : x1) + xi += DataTypes::Coord(0.01, -0.02); + + core::MechanicalParams mparams; + Data x0Data; + x0Data.setValue(x0); + Data x1Data; + x1Data.setValue(x1); + + const SReal e0 = integrator->getPotentialEnergy(&mparams, x0Data); + const SReal e1 = integrator->getPotentialEnergy(&mparams, x1Data); + + SReal work = 0; + for (std::size_t i = 0; i < f.size(); ++i) + work += dot(f[i], x1[i] - x0[i]); + + EXPECT_NEAR(e1 - e0, -work, 1e-9); +} + +} // namespace sofa diff --git a/examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn b/examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn index 964c373b4b0..f473fd8b62e 100644 --- a/examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn +++ b/examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn @@ -109,83 +109,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From a9070313f13e8572bdd12f1bd9a98cbeff4a9cdc Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Wed, 9 Sep 2026 14:47:05 +0200 Subject: [PATCH 14/37] Add QuadratureContext --- .../SolidMechanics/FEM/Elastic/CMakeLists.txt | 5 +- .../fem/elastic/ConstantSourceTerm.cpp | 45 --------- .../fem/elastic/FEMSourceTermIntegrator.h | 8 +- .../fem/elastic/FEMSourceTermIntegrator.inl | 4 +- .../fem/elastic/GeometricSourceTerm.cpp | 58 +++++++++++ .../fem/elastic/GeometricSourceTerm.h | 99 +++++++++++++++++++ ...nstantSourceTerm.h => QuadratureContext.h} | 67 ++++++++----- .../solidmechanics/fem/elastic/init.cpp | 4 +- .../tests/FEMSourceTermIntegrator_test.cpp | 14 +-- ...SourceTerm.scn => GeometricSourceTerm.scn} | 0 10 files changed, 218 insertions(+), 86 deletions(-) delete mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ConstantSourceTerm.cpp create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.cpp create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.h rename Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/{ConstantSourceTerm.h => QuadratureContext.h} (50%) rename examples/Component/SolidMechanics/FEM/{ConstantSourceTerm.scn => GeometricSourceTerm.scn} (100%) diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt index 6cbf65b8f00..15c70df93f2 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt @@ -14,11 +14,12 @@ set(HEADER_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BeamFEMForceField.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BeamFEMForceField.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CauchyStressEvaluator.h - ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/ConstantSourceTerm.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.inl + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/GeometricSourceTerm.h + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/QuadratureContext.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FastTetrahedralCorotationalForceField.h @@ -74,9 +75,9 @@ set(SOURCE_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseElementLinearFEMForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseLinearElasticityFEMForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BeamFEMForceField.cpp - ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/ConstantSourceTerm.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.cpp + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/GeometricSourceTerm.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FastTetrahedralCorotationalForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMForceField.cpp diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ConstantSourceTerm.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ConstantSourceTerm.cpp deleted file mode 100644 index 569b3d7e86f..00000000000 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ConstantSourceTerm.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#define SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_CONSTANT_SOURCE_TERM_CPP - -#include - -#include -#include - -namespace sofa::component::solidmechanics::fem::elastic -{ - -void registerConstantSourceTerm(sofa::core::ObjectFactory* factory) -{ - factory->registerObjects(sofa::core::ObjectRegistrationData("Constant source term (per unit volume) prescribed at the nodes") - .add< ConstantSourceTerm >() - .add< ConstantSourceTerm >() - .add< ConstantSourceTerm >() - ); -} - -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API ConstantSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API ConstantSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API ConstantSourceTerm; - -} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h index 7873aec5cdf..5a3be3e2f62 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h @@ -22,7 +22,7 @@ #pragma once #include -#include +#include #include #include #include @@ -41,7 +41,7 @@ namespace sofa::component::solidmechanics::fem::elastic * @brief Integrates source terms into consistent nodal loads. * * A source term contributes \f$ \int_{\Omega} N_a \, r \, d\Omega \f$ to the right-hand side, where - * r is the per-node density carried by a linked ConstantSourceTerm (through l_constantSources) and + * r is the per-node density carried by a linked GeometricSourceTerm (through l_constantSources) and * does not depend on the displacement. Every term is thus summed and integrated once in init(); * addForce merely accumulates the result. * @@ -75,9 +75,9 @@ class FEMSourceTermIntegrator : /** * @brief Source terms integrated by this component. * - * If left empty, the ConstantSourceTerm components found in the current context are used. + * If left empty, the GeometricSourceTerm components found in the current context are used. */ - sofa::MultiLink, ConstantSourceTerm, + sofa::MultiLink, GeometricSourceTerm, sofa::BaseLink::FLAG_STOREPATH | sofa::BaseLink::FLAG_STRONGLINK> l_constantSources; /** diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl index 6e69510ed09..2308a785043 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl @@ -77,10 +77,10 @@ void FEMSourceTermIntegrator::init() template void FEMSourceTermIntegrator::validateSources() { - // Gather all ConstantSourceTerm components in Context if empty + // Gather all GeometricSourceTerm components in Context if empty if (l_constantSources.empty()) { - const auto sourcesInContext = this->getContext()->template getObjects >( + const auto sourcesInContext = this->getContext()->template getObjects >( sofa::core::objectmodel::BaseContext::Local); for (const auto& source : sourcesInContext) diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.cpp new file mode 100644 index 00000000000..ce0f5f44ef2 --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.cpp @@ -0,0 +1,58 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#define SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_GEOMETRIC_SOURCE_TERM_CPP + +#include + +#include +#include +#include + +namespace sofa::component::solidmechanics::fem::elastic +{ + +void registerGeometricSourceTerm(sofa::core::ObjectFactory* factory) +{ + factory->registerObjects(sofa::core::ObjectRegistrationData("Source density prescribed at the nodes, per unit measure of the element") + .add< GeometricSourceTerm >() + .add< GeometricSourceTerm >() + .add< GeometricSourceTerm >() + .add< GeometricSourceTerm >() + .add< GeometricSourceTerm >() + .add< GeometricSourceTerm >() + .add< GeometricSourceTerm >() + .add< GeometricSourceTerm >() + .add< GeometricSourceTerm >() + ); +} + +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.h new file mode 100644 index 00000000000..8332367581a --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.h @@ -0,0 +1,99 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once + +#include +#include +#include +#include + +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_GEOMETRIC_SOURCE_TERM_CPP) +#include +#include +#endif + +namespace sofa::component::solidmechanics::fem::elastic +{ + +/** + * @class GeometricSourceTerm + * @brief A source density whose value is determined by the geometry, not by the solution. + * + * The component calculates the integrand in evaluate() given QuadratureContext from the integrator. + * + * The density is the inherited "property" Data (see BaseNodalProperty): a vector shorter than + * the mechanical state broadcasts its last value to the remaining nodes, so a uniform density is + * written with a single value. + * + * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). + * @tparam TElementType The type of finite element (e.g., sofa::geometry::Tetrahedron). + */ +template +class GeometricSourceTerm : public sofa::core::BaseNodalProperty> +{ +public: + using DataTypes = TDataTypes; + using ElementType = TElementType; + + SOFA_CLASS(SOFA_TEMPLATE2(GeometricSourceTerm, DataTypes, ElementType), + SOFA_TEMPLATE(sofa::core::BaseNodalProperty, sofa::Deriv_t)); + + using Deriv = sofa::Deriv_t; + using Context = QuadratureContext; + + /** + * @brief Source density at one quadrature point, per unit physical measure. + */ + virtual Deriv evaluate(const Context& context) const + { + SOFA_UNUSED(context); + + // The density is prescribed at the nodes, so r(q) = sum_a N_a(q) r_a. That interpolation + // is still to be decided, so a single representative value stands in for the whole + // element: a property that does vary from node to node has entry 0 applied everywhere. + // + // Deriv density{}; + // for (sofa::Size a = 0; a < Context::NumberOfNodesInElement; ++a) + // density += this->getNodeProperty(context.element[a]) * context.N[a]; + // return density; + + return this->getNodeProperty(0); + } + +protected: + + GeometricSourceTerm() : sofa::core::BaseNodalProperty(Deriv{}) {} +}; + +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_GEOMETRIC_SOURCE_TERM_CPP) +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; +#endif + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ConstantSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadratureContext.h similarity index 50% rename from Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ConstantSourceTerm.h rename to Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadratureContext.h index e03bf47622a..cd14621076f 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ConstantSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadratureContext.h @@ -21,46 +21,65 @@ ******************************************************************************/ #pragma once -#include -#include #include - -#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_CONSTANT_SOURCE_TERM_CPP) -#include -#endif +#include +#include +#include namespace sofa::component::solidmechanics::fem::elastic { /** - * @class ConstantSourceTerm - * @brief A source density prescribed at the nodes, independent of the current displacement. + * @struct QuadratureContext + * @brief Everything the integrator knows at one quadrature point. * - * The density is the inherited "property" Data (see BaseNodalProperty): a vector shorter than the - * mechanical state broadcasts its last value to the remaining nodes, so a uniform density is - * written with a single value. Link it to a FEMSourceTermIntegrator through l_constantSources. + * Built once per quadrature point and handed to every integrated term. + * A source term reads from it and returns an integrand. * * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). + * @tparam TElementType The type of finite element (e.g., sofa::geometry::Tetrahedron). */ -template -class ConstantSourceTerm : public sofa::core::BaseNodalProperty> +template +struct QuadratureContext { -public: using DataTypes = TDataTypes; + using ElementType = TElementType; + using FiniteElement = sofa::fem::FiniteElement; + + using Real = sofa::Real_t; + using Coord = sofa::Coord_t; using Deriv = sofa::Deriv_t; - SOFA_CLASS(SOFA_TEMPLATE(ConstantSourceTerm, DataTypes), - SOFA_TEMPLATE(sofa::core::BaseNodalProperty, Deriv)); + static constexpr sofa::Size NumberOfNodesInElement = ElementType::NumberOfNodes; + static constexpr sofa::Size spatial_dimensions = DataTypes::spatial_dimensions; + static constexpr sofa::Size TopologicalDimension = FiniteElement::TopologicalDimension; -protected: + using Element = typename FiniteElement::TopologyElement; + using ShapeFunctions = sofa::type::Vec; + using GradientShapeFunctions = sofa::type::Mat; + using Jacobian = sofa::type::Mat; - ConstantSourceTerm() : sofa::core::BaseNodalProperty(Deriv{}) {} -}; + /// Node indices of the element being integrated, with which a term gathers its own nodal + /// degrees of freedom. + const Element& element; + + /// Shape function value at this quadrature point. + ShapeFunctions N; + + /// Reference-space gradients of the shape functions at this quadrature point. + GradientShapeFunctions gradientShapeFunctions; -#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_CONSTANT_SOURCE_TERM_CPP) -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API ConstantSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API ConstantSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API ConstantSourceTerm; -#endif + /// dx/dq of the reference-to-physical mapping, on the configuration the integrator chose. + Jacobian jacobian; + + /// \f$ |\det J| \f$, for information only: the integrator applies it, a term must not. + Real measure; + + /// Interpolated rest position at this quadrature point. + Coord restPosition; + + /// Interpolated displacement at this quadrature point. + Deriv displacement; +}; } // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp index 181de014371..f211cf32b5d 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp @@ -27,10 +27,10 @@ namespace sofa::component::solidmechanics::fem::elastic { extern void registerBeamFEMForceField(sofa::core::ObjectFactory* factory); -extern void registerConstantSourceTerm(sofa::core::ObjectFactory* factory); extern void registerCorotationalFEMForceField(sofa::core::ObjectFactory* factory); extern void registerFEMSourceTermIntegrator(sofa::core::ObjectFactory* factory); extern void registerFastTetrahedralCorotationalForceField(sofa::core::ObjectFactory* factory); +extern void registerGeometricSourceTerm(sofa::core::ObjectFactory* factory); extern void registerHexahedralFEMForceField(sofa::core::ObjectFactory* factory); extern void registerHexahedralFEMForceFieldAndMass(sofa::core::ObjectFactory* factory); extern void registerHexahedronFEMForceField(sofa::core::ObjectFactory* factory); @@ -70,10 +70,10 @@ const char* getModuleVersion() void registerObjects(sofa::core::ObjectFactory* factory) { registerBeamFEMForceField(factory); - registerConstantSourceTerm(factory); registerCorotationalFEMForceField(factory); registerFEMSourceTermIntegrator(factory); registerFastTetrahedralCorotationalForceField(factory); + registerGeometricSourceTerm(factory); registerHexahedralFEMForceField(factory); registerHexahedralFEMForceFieldAndMass(factory); registerHexahedronFEMForceField(factory); diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/tests/FEMSourceTermIntegrator_test.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/tests/FEMSourceTermIntegrator_test.cpp index 99d5598fdd3..68b793f6a11 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/tests/FEMSourceTermIntegrator_test.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/tests/FEMSourceTermIntegrator_test.cpp @@ -28,7 +28,7 @@ #include #include -#include +#include #include #include #include @@ -83,18 +83,18 @@ class FEMSourceTermIntegrator_test : public testing::BaseTest } }; -// Splitting one ConstantSourceTerm into several must not change the integrated force. +// Splitting one GeometricSourceTerm into several must not change the integrated force. TEST_F(FEMSourceTermIntegrator_test, MultipleSourcesSumToOne) { m_root = makeMesh(); - createObject(m_root, "ConstantSourceTerm", {{"name", "full"}, {"template", "Vec2"}, {"property", "300 -600"}}); + createObject(m_root, "GeometricSourceTerm", {{"name", "full"}, {"template", "Vec2,Triangle"}, {"property", "300 -600"}}); auto* one = dynamic_cast(createObject(m_root, "FEMSourceTermIntegrator", {{"name", "one"}, {"template", "Vec2,Triangle"}, {"topology", "@mesh"}, {"constantSources", "@full"}}).get()); - createObject(m_root, "ConstantSourceTerm", {{"name", "a"}, {"template", "Vec2"}, {"property", "100 -200"}}); - createObject(m_root, "ConstantSourceTerm", {{"name", "b"}, {"template", "Vec2"}, {"property", "100 -200"}}); - createObject(m_root, "ConstantSourceTerm", {{"name", "c"}, {"template", "Vec2"}, {"property", "100 -200"}}); + createObject(m_root, "GeometricSourceTerm", {{"name", "a"}, {"template", "Vec2,Triangle"}, {"property", "100 -200"}}); + createObject(m_root, "GeometricSourceTerm", {{"name", "b"}, {"template", "Vec2,Triangle"}, {"property", "100 -200"}}); + createObject(m_root, "GeometricSourceTerm", {{"name", "c"}, {"template", "Vec2,Triangle"}, {"property", "100 -200"}}); auto* three = dynamic_cast(createObject(m_root, "FEMSourceTermIntegrator", {{"name", "three"}, {"template", "Vec2,Triangle"}, {"topology", "@mesh"}, {"constantSources", "@a @b @c"}}).get()); @@ -114,7 +114,7 @@ TEST_F(FEMSourceTermIntegrator_test, PotentialEnergyMatchesWork) { m_root = makeMesh(); - createObject(m_root, "ConstantSourceTerm", {{"name", "bodyForce"}, {"template", "Vec2"}, {"property", "300 -600"}}); + createObject(m_root, "GeometricSourceTerm", {{"name", "bodyForce"}, {"template", "Vec2,Triangle"}, {"property", "300 -600"}}); auto* integrator = dynamic_cast(createObject(m_root, "FEMSourceTermIntegrator", {{"name", "source"}, {"template", "Vec2,Triangle"}, {"topology", "@mesh"}, {"constantSources", "@bodyForce"}}).get()); diff --git a/examples/Component/SolidMechanics/FEM/ConstantSourceTerm.scn b/examples/Component/SolidMechanics/FEM/GeometricSourceTerm.scn similarity index 100% rename from examples/Component/SolidMechanics/FEM/ConstantSourceTerm.scn rename to examples/Component/SolidMechanics/FEM/GeometricSourceTerm.scn From 140c6a69d8e911088440d6208039a42b228e71c6 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Wed, 9 Sep 2026 14:47:32 +0200 Subject: [PATCH 15/37] Change name in scenes --- .../Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn | 4 ++-- examples/Component/SolidMechanics/FEM/GeometricSourceTerm.scn | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn b/examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn index f473fd8b62e..106e168465f 100644 --- a/examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn +++ b/examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn @@ -67,7 +67,7 @@ - - - + @@ -65,7 +65,7 @@ - + From d9939f493907288e5d33478a7cd9546c25880cb4 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Wed, 9 Sep 2026 15:22:35 +0200 Subject: [PATCH 16/37] Generalize evaluateValueInElement for all types --- Sofa/framework/FEM/src/sofa/fem/FiniteElement.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sofa/framework/FEM/src/sofa/fem/FiniteElement.h b/Sofa/framework/FEM/src/sofa/fem/FiniteElement.h index a490fddd6a3..9c389914f9b 100644 --- a/Sofa/framework/FEM/src/sofa/fem/FiniteElement.h +++ b/Sofa/framework/FEM/src/sofa/fem/FiniteElement.h @@ -86,7 +86,7 @@ struct FiniteElementHelper const std::array& valuesAtNodes, const sofa::type::Vec& shapeFunctions) { - return std::inner_product(valuesAtNodes.begin(), valuesAtNodes.end(), shapeFunctions.begin(), T(0)); + return std::inner_product(valuesAtNodes.begin(), valuesAtNodes.end(), shapeFunctions.begin(), T{}); } }; From d4937b690e47d2d85717b1f69a8225b06bb66413 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Wed, 9 Sep 2026 15:26:08 +0200 Subject: [PATCH 17/37] Adjust FEmSourceTermIntegrator --- .../fem/elastic/FEMSourceTermIntegrator.h | 68 +++------- .../fem/elastic/FEMSourceTermIntegrator.inl | 116 +++++------------- 2 files changed, 45 insertions(+), 139 deletions(-) diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h index 5a3be3e2f62..d0278870b5f 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h @@ -27,7 +27,6 @@ #include #include #include -#include #if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_FEM_SOURCE_TERM_INTEGRATOR_CPP) #include @@ -38,12 +37,11 @@ namespace sofa::component::solidmechanics::fem::elastic /** * @class FEMSourceTermIntegrator - * @brief Integrates source terms into consistent nodal loads. + * @brief Integrates a source density into consistent nodal loads. * * A source term contributes \f$ \int_{\Omega} N_a \, r \, d\Omega \f$ to the right-hand side, where - * r is the per-node density carried by a linked GeometricSourceTerm (through l_constantSources) and - * does not depend on the displacement. Every term is thus summed and integrated once in init(); - * addForce merely accumulates the result. + * r is the density evaluated by a linked GeometricSourceTerm (through l_constantSources) at each + * quadrature point. * * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). * @tparam TElementType The type of finite element (e.g., sofa::geometry::Tetrahedron). @@ -67,9 +65,6 @@ class FEMSourceTermIntegrator : static constexpr sofa::Size NumberOfNodesInElement = ElementType::NumberOfNodes; static constexpr sofa::Size spatial_dimensions = DataTypes::spatial_dimensions; - using ElementMatrix = sofa::type::Mat>; - using GlobalMatrix = sofa::linearalgebra::CompressedRowSparseMatrixMechanical>; - public: /** @@ -87,20 +82,17 @@ class FEMSourceTermIntegrator : * 1. Initializes the base force field. * 2. Initializes the topology accessor. * 3. Validates the linked source terms. - * 4. Assembles the global matrix M. - * 5. Integrates the source terms into the constant nodal force. + * 4. Integrates the source terms into the nodal force. */ void init() override; /** * @brief Adds the nodal source term to the RHS vector. * - * The source terms were integrated once in init and are only accumulated here. - * * @param mparams Mechanical parameters for the computation. * @param f The force vector to which the source term will be added. - * @param x The current positions (unused: the load is prescribed on the rest configuration). - * @param v The current velocities (unused in this implementation). + * @param x The current positions. + * @param v The current velocities. */ void addForce( const sofa::core::MechanicalParams* mparams, @@ -109,26 +101,26 @@ class FEMSourceTermIntegrator : const sofa::DataVecDeriv_t& v) override; /** - * @brief No-op: TODO for non-const source terms + * @brief No-op. */ void addDForce(const sofa::core::MechanicalParams* mparams, sofa::DataVecDeriv_t& df, const sofa::DataVecDeriv_t& dx) override; /** - * @brief No-op: TODO for non-const source terms + * @brief No-op. */ void buildStiffnessMatrix(sofa::core::behavior::StiffnessMatrix* matrix) override; using sofa::core::behavior::ForceField::getPotentialEnergy; /** - * @brief Potential energy of the constant nodal load, \f$ V = -\sum_a F_a \cdot (x_a - x_{0,a}) \f$. + * @brief Potential energy of the nodal load, \f$ V = -\sum_a F_a \cdot (x_a - x_{0,a}) \f$. */ SReal getPotentialEnergy(const sofa::core::MechanicalParams* mparams, const sofa::DataVecCoord_t& x) const override; /** - * @brief Degree of the quadrature rule integrating the element matrix M. + * @brief Degree of the quadrature rule integrating the source terms. */ sofa::Data d_quadratureDegree; @@ -145,46 +137,16 @@ class FEMSourceTermIntegrator : void validateSources(); /** - * @brief Assembles and stores the geometry-only matrix \f$ M_{ij} = \int_{\Omega} N_i N_j \, d\Omega \f$ over each element on the rest configuration. - */ - void assembleGlobalMatrix(); - - /** - * @brief Sums every displacement-independent source density and integrates it once into m_constantForce. + * @brief Runs the quadrature and accumulates every linked source term into m_constantForce. * - * Integration is linear, so the sum of the terms integrates to the sum of their contributions: - * a single matrix-vector product covers all of them. + * For each element and each quadrature point, a QuadratureContext is built and handed to every + * source term; the density it returns is weighted by \f$ w \, |\det J| \, N_a \f$ and scattered + * onto the element nodes. */ void assembleConstantForce(); /** - * @brief Applies the geometry-only matrix M to a nodal source term. - */ - void applyGlobalMatrix(const sofa::VecDeriv_t& nodalSourceTerm, - sofa::VecDeriv_t& result) const; - - /** - * @brief Computes the geometry-only matrix of each element. - */ - void calculateElementMatrix(const auto& elements, sofa::type::vector& elementMatrices); - - /** - * @brief Scatters the element matrices into the global matrix. - */ - void initializeGlobalMatrix(const auto& elements, const sofa::type::vector& elementMatrices); - - /** - * @brief Geometry-only matrix \f$ M_{ij} = \int_{\Omega} N_i N_j \, d\Omega \f$ of the system. - * - * Stored in compressed sparse row format. Assembled once in init on the rest configuration. - */ - GlobalMatrix m_globalMatrix; - - /** - * @brief Nodal load of every term in l_constantSources, integrated once in init. - * - * @note Their contribution is integrated once, so editing the property of a linked term at run - * time has no effect until the scene is reinitialised. + * @brief Nodal load of every term in l_constantSources. */ sofa::VecDeriv_t m_constantForce; }; diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl index 2308a785043..6fb32c41b4a 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl @@ -31,15 +31,13 @@ FEMSourceTermIntegrator::FEMSourceTermIntegrator() : l_constantSources(initLink("constantSources", "Source terms of the weak form integrated by " "this component. If empty, the ones found in the current context are used.")) , d_quadratureDegree(initData(&d_quadratureDegree, static_cast(1), "quadratureDegree", - "Degree of the quadrature rule integrating the element matrix M.")) + "Degree of the quadrature rule integrating the source terms.")) { - // Re-compute global matrix and constant forces in case of quadrature degree change - this->addUpdateCallback("reassembleSourceMatrix", {&d_quadratureDegree}, + this->addUpdateCallback("reassembleConstantForce", {&d_quadratureDegree}, [this](const sofa::core::DataTracker&) { if (!this->isComponentStateInvalid() && this->l_topology && this->mstate) { - assembleGlobalMatrix(); assembleConstantForce(); } @@ -64,7 +62,6 @@ void FEMSourceTermIntegrator::init() if (!this->isComponentStateInvalid() && this->l_topology && this->mstate) { - this->assembleGlobalMatrix(); this->assembleConstantForce(); } @@ -96,36 +93,29 @@ void FEMSourceTermIntegrator::validateSources() } template -void FEMSourceTermIntegrator::assembleGlobalMatrix() +void FEMSourceTermIntegrator::assembleConstantForce() { - const auto& elements = FiniteElement::getElementSequence(*this->l_topology); - sofa::type::vector elementMatrices; - - // 1. compute the geometry-only matrix of each element - calculateElementMatrix(elements, elementMatrices); + m_constantForce.assign(this->mstate->getSize(), sofa::Deriv_t{}); - // 2. scatter the element matrices into the global matrix - initializeGlobalMatrix(elements, elementMatrices); -} - -template -void FEMSourceTermIntegrator::calculateElementMatrix( - const auto& elements, sofa::type::vector& elementMatrices) -{ const auto restPositionsAccessor = this->mstate->readRestPositions(); - elementMatrices.resize(elements.size()); + const auto positionsAccessor = this->mstate->readPositions(); + const auto& elements = FiniteElement::getElementSequence(*this->l_topology); const auto quadratureRule = FiniteElement::quadratureRule(d_quadratureDegree.getValue()); - for (sofa::Index elementId = 0; elementId < elements.size(); ++elementId) + for (const auto& element : elements) { - const auto& element = elements[elementId]; - auto& elementMatrix = elementMatrices[elementId]; - const std::array, NumberOfNodesInElement> elementNodesRestCoordinates = extractNodesVectorFromGlobalVector(element, restPositionsAccessor.ref()); + const std::array, NumberOfNodesInElement> elementNodesCoordinates = + extractNodesVectorFromGlobalVector(element, positionsAccessor.ref()); + + std::array, NumberOfNodesInElement> elementNodesDisplacement; + for (sofa::Size i = 0; i < NumberOfNodesInElement; ++i) + { + elementNodesDisplacement[i] = elementNodesCoordinates[i] - elementNodesRestCoordinates[i]; + } - // M_ij = integral of N_i N_j dV, evaluated on the rest configuration (geometry only). for (const auto& [quadraturePoint, weight] : quadratureRule) { const auto N = FiniteElement::shapeFunctions(quadraturePoint); @@ -133,77 +123,31 @@ void FEMSourceTermIntegrator::calculateElementMatrix( const auto jacobian = FiniteElement::Helper::jacobianFromReferenceToPhysical( elementNodesRestCoordinates, dN_dq_ref); - const auto detJ = sofa::type::absGeneralizedDeterminant(jacobian); + const auto measure = static_cast(sofa::type::absGeneralizedDeterminant(jacobian)); - const auto NT_N = sofa::type::dyad(N, N); + const auto restPosition = + FiniteElement::Helper::evaluateValueInElement(elementNodesRestCoordinates, N); + const auto displacement = + FiniteElement::Helper::evaluateValueInElement(elementNodesDisplacement, N); - elementMatrix += (weight * detJ) * NT_N; - } - } -} + const QuadratureContext context{ + element, N, dN_dq_ref, jacobian, measure, restPosition, displacement}; -template -void FEMSourceTermIntegrator::initializeGlobalMatrix( - const auto& elements, const sofa::type::vector& elementMatrices) -{ - m_globalMatrix.clear(); - const auto size = this->mstate->getSize(); - m_globalMatrix.resize(size, size); - - for (sofa::Index elementId = 0; elementId < elements.size(); ++elementId) - { - const auto& element = elements[elementId]; - const auto& elementMatrix = elementMatrices[elementId]; + const auto weightTimesMeasure = static_cast(weight) * measure; - for (sofa::Size i = 0; i < NumberOfNodesInElement; ++i) - { - for (sofa::Size j = 0; j < NumberOfNodesInElement; ++j) + for (const auto& source : l_constantSources) { - m_globalMatrix.add(element[i], element[j], elementMatrix(i, j)); - } - } - } + const auto density = source->evaluate(context); - m_globalMatrix.compress(); -} - -template -void FEMSourceTermIntegrator::applyGlobalMatrix( - const sofa::VecDeriv_t& nodalSourceTerm, sofa::VecDeriv_t& result) const -{ - // f_i = sum_j M_ij b_j : apply the global matrix to the nodal source term. - for (sofa::Index xi = 0; xi < m_globalMatrix.rowIndex.size(); ++xi) - { - const auto rowId = m_globalMatrix.rowIndex[xi]; - typename GlobalMatrix::Range rowRange(m_globalMatrix.rowBegin[xi], m_globalMatrix.rowBegin[xi + 1]); - for (typename GlobalMatrix::Index xj = rowRange.begin(); xj < rowRange.end(); ++xj) - { - const auto columnId = m_globalMatrix.colsIndex[xj]; - const auto& value = m_globalMatrix.colsValue[xj]; - - result[rowId] += nodalSourceTerm[columnId] * value; + for (sofa::Size a = 0; a < NumberOfNodesInElement; ++a) + { + m_constantForce[element[a]] += density * (weightTimesMeasure * N[a]); + } + } } } } -template -void FEMSourceTermIntegrator::assembleConstantForce() -{ - const auto size = this->mstate->getSize(); - - // Aggregate all contributions to one vector before applying the global matrix - sofa::VecDeriv_t sourceTerms(size, sofa::Deriv_t{}); - - for (const auto& source : l_constantSources) - { - for (sofa::Index i = 0; i < size; ++i) - sourceTerms[i] += source->getNodeProperty(i); - } - - m_constantForce.assign(size, sofa::Deriv_t{}); - applyGlobalMatrix(sourceTerms, m_constantForce); -} - template void FEMSourceTermIntegrator::addForce(const sofa::core::MechanicalParams* mparams, sofa::DataVecDeriv_t& f, From 79fe075d7b743424e4a5637a09f53594ccf1f433 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Wed, 9 Sep 2026 16:13:20 +0200 Subject: [PATCH 18/37] Interpolate the nodal property during integration --- .../fem/elastic/FEMSourceTermIntegrator.h | 5 ++-- .../fem/elastic/FEMSourceTermIntegrator.inl | 17 ++++++++++--- .../fem/elastic/GeometricSourceTerm.h | 25 ++++++------------- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h index d0278870b5f..6ef08e23f01 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h @@ -140,8 +140,9 @@ class FEMSourceTermIntegrator : * @brief Runs the quadrature and accumulates every linked source term into m_constantForce. * * For each element and each quadrature point, a QuadratureContext is built and handed to every - * source term; the density it returns is weighted by \f$ w \, |\det J| \, N_a \f$ and scattered - * onto the element nodes. + * source term, together with the nodal property of that term interpolated at the point; the + * density it returns is weighted by \f$ w \, |\det J| \, N_a \f$ and scattered onto the element + * nodes. */ void assembleConstantForce(); diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl index 6fb32c41b4a..7ce1bfdae8e 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl @@ -137,11 +137,22 @@ void FEMSourceTermIntegrator::assembleConstantForce() for (const auto& source : l_constantSources) { - const auto density = source->evaluate(context); + sofa::helper::ReadAccessor propertyAccessor { source->d_property }; - for (sofa::Size a = 0; a < NumberOfNodesInElement; ++a) + std::array, NumberOfNodesInElement> elementNodesProperty; + for (sofa::Size i = 0; i < NumberOfNodesInElement; ++i) { - m_constantForce[element[a]] += density * (weightTimesMeasure * N[a]); + elementNodesProperty[i] = source->getNodeProperty(element[i], propertyAccessor); + } + + const auto property = + FiniteElement::Helper::evaluateValueInElement(elementNodesProperty, N); + + const auto density = source->evaluate(context, property); + + for (sofa::Size i = 0; i < NumberOfNodesInElement; ++i) + { + m_constantForce[element[i]] += density * (weightTimesMeasure * N[i]); } } } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.h index 8332367581a..96a71435e27 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.h @@ -38,11 +38,8 @@ namespace sofa::component::solidmechanics::fem::elastic * @class GeometricSourceTerm * @brief A source density whose value is determined by the geometry, not by the solution. * - * The component calculates the integrand in evaluate() given QuadratureContext from the integrator. - * - * The density is the inherited "property" Data (see BaseNodalProperty): a vector shorter than - * the mechanical state broadcasts its last value to the remaining nodes, so a uniform density is - * written with a single value. + * The component calculates the integrand in evaluate() given a QuadratureContext and the + * interpolated property field. * * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). * @tparam TElementType The type of finite element (e.g., sofa::geometry::Tetrahedron). @@ -58,25 +55,19 @@ class GeometricSourceTerm : public sofa::core::BaseNodalProperty)); using Deriv = sofa::Deriv_t; - using Context = QuadratureContext; + using QuadratureContext = QuadratureContext; /** * @brief Source density at one quadrature point, per unit physical measure. + * + * @param context Geometry of the quadrature point. + * @param property The nodal property interpolated at the quadrature point. */ - virtual Deriv evaluate(const Context& context) const + virtual Deriv evaluate(const QuadratureContext& context, const Deriv& property) const { SOFA_UNUSED(context); - // The density is prescribed at the nodes, so r(q) = sum_a N_a(q) r_a. That interpolation - // is still to be decided, so a single representative value stands in for the whole - // element: a property that does vary from node to node has entry 0 applied everywhere. - // - // Deriv density{}; - // for (sofa::Size a = 0; a < Context::NumberOfNodesInElement; ++a) - // density += this->getNodeProperty(context.element[a]) * context.N[a]; - // return density; - - return this->getNodeProperty(0); + return property; } protected: From 1e0f5a5d5d2185efb352555360cfb11b70b2e9e3 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Wed, 9 Sep 2026 17:22:16 +0200 Subject: [PATCH 19/37] Add BaseGeometricSourceTerm --- .../SolidMechanics/FEM/Elastic/CMakeLists.txt | 2 + .../fem/elastic/BaseGeometricSourceTerm.cpp | 42 ++++++++++ .../fem/elastic/BaseGeometricSourceTerm.h | 83 +++++++++++++++++++ 3 files changed, 127 insertions(+) create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseGeometricSourceTerm.cpp create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseGeometricSourceTerm.h diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt index 15c70df93f2..d9a0e065998 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt @@ -9,6 +9,7 @@ set(HEADER_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/fwd.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseElementLinearFEMForceField.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseElementLinearFEMForceField.inl + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseGeometricSourceTerm.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseLinearElasticityFEMForceField.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseLinearElasticityFEMForceField.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BeamFEMForceField.h @@ -73,6 +74,7 @@ set(HEADER_FILES set(SOURCE_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/init.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseElementLinearFEMForceField.cpp + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseGeometricSourceTerm.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseLinearElasticityFEMForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BeamFEMForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.cpp diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseGeometricSourceTerm.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseGeometricSourceTerm.cpp new file mode 100644 index 00000000000..7e6cf617062 --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseGeometricSourceTerm.cpp @@ -0,0 +1,42 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#define SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_BASE_GEOMETRIC_SOURCE_TERM_CPP + +#include + +#include +#include + +namespace sofa::component::solidmechanics::fem::elastic +{ + +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseGeometricSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseGeometricSourceTerm.h new file mode 100644 index 00000000000..64c91610c7c --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseGeometricSourceTerm.h @@ -0,0 +1,83 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once + +#include +#include +#include +#include + +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_BASE_GEOMETRIC_SOURCE_TERM_CPP) +#include +#include +#endif + +namespace sofa::component::solidmechanics::fem::elastic +{ + +/** + * @class BaseGeometricSourceTerm + * @brief A source density whose value is determined by the geometry, not by the solution. + * + * The component calculates the integrand in evaluate() given a QuadratureContext. + * + * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). + * @tparam TElementType The type of finite element (e.g., sofa::geometry::Tetrahedron). + */ +template +class BaseGeometricSourceTerm : public sofa::core::objectmodel::BaseComponent +{ +public: + using DataTypes = TDataTypes; + using ElementType = TElementType; + + SOFA_CLASS(SOFA_TEMPLATE2(BaseGeometricSourceTerm, DataTypes, ElementType), + sofa::core::objectmodel::BaseComponent); + + using Deriv = sofa::Deriv_t; + using QuadratureContext = QuadratureContext; + + /** + * @brief Source density at one quadrature point, per unit physical measure. + * + * @param context Geometry of the quadrature point. + */ + virtual Deriv evaluate(const QuadratureContext& context) const = 0; + +protected: + + BaseGeometricSourceTerm() = default; +}; + +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_BASE_GEOMETRIC_SOURCE_TERM_CPP) +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; +#endif + +} // namespace sofa::component::solidmechanics::fem::elastic From 30b8106635c905e88453e963418932fe459baff8 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Wed, 9 Sep 2026 17:38:27 +0200 Subject: [PATCH 20/37] Create the interpolate property base --- .../SolidMechanics/FEM/Elastic/CMakeLists.txt | 1 + .../fem/elastic/FEMSourceTermIntegrator.h | 15 ++-- .../fem/elastic/FEMSourceTermIntegrator.inl | 15 +--- .../fem/elastic/GeometricSourceTerm.cpp | 36 +++------- .../fem/elastic/GeometricSourceTerm.h | 66 +++++++++-------- .../fem/elastic/GeometricSourceTerm.inl | 72 +++++++++++++++++++ .../solidmechanics/fem/elastic/init.cpp | 2 - 7 files changed, 130 insertions(+), 77 deletions(-) create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.inl diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt index d9a0e065998..58a80c827f7 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt @@ -20,6 +20,7 @@ set(HEADER_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/GeometricSourceTerm.h + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/GeometricSourceTerm.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/QuadratureContext.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.inl diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h index 6ef08e23f01..a9932c5dc3d 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h @@ -22,7 +22,7 @@ #pragma once #include -#include +#include #include #include #include @@ -40,8 +40,8 @@ namespace sofa::component::solidmechanics::fem::elastic * @brief Integrates a source density into consistent nodal loads. * * A source term contributes \f$ \int_{\Omega} N_a \, r \, d\Omega \f$ to the right-hand side, where - * r is the density evaluated by a linked GeometricSourceTerm (through l_constantSources) at each - * quadrature point. + * r is the density evaluated by a linked BaseGeometricSourceTerm (through l_constantSources) at + * each quadrature point. * * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). * @tparam TElementType The type of finite element (e.g., sofa::geometry::Tetrahedron). @@ -70,9 +70,9 @@ class FEMSourceTermIntegrator : /** * @brief Source terms integrated by this component. * - * If left empty, the GeometricSourceTerm components found in the current context are used. + * If left empty, the BaseGeometricSourceTerm components found in the current context are used. */ - sofa::MultiLink, GeometricSourceTerm, + sofa::MultiLink, BaseGeometricSourceTerm, sofa::BaseLink::FLAG_STOREPATH | sofa::BaseLink::FLAG_STRONGLINK> l_constantSources; /** @@ -140,9 +140,8 @@ class FEMSourceTermIntegrator : * @brief Runs the quadrature and accumulates every linked source term into m_constantForce. * * For each element and each quadrature point, a QuadratureContext is built and handed to every - * source term, together with the nodal property of that term interpolated at the point; the - * density it returns is weighted by \f$ w \, |\det J| \, N_a \f$ and scattered onto the element - * nodes. + * source term; the density it returns is weighted by \f$ w \, |\det J| \, N_a \f$ and scattered + * onto the element nodes. */ void assembleConstantForce(); diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl index 7ce1bfdae8e..944b7e7e23b 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl @@ -77,7 +77,7 @@ void FEMSourceTermIntegrator::validateSources() // Gather all GeometricSourceTerm components in Context if empty if (l_constantSources.empty()) { - const auto sourcesInContext = this->getContext()->template getObjects >( + const auto sourcesInContext = this->getContext()->template getObjects >( sofa::core::objectmodel::BaseContext::Local); for (const auto& source : sourcesInContext) @@ -137,18 +137,7 @@ void FEMSourceTermIntegrator::assembleConstantForce() for (const auto& source : l_constantSources) { - sofa::helper::ReadAccessor propertyAccessor { source->d_property }; - - std::array, NumberOfNodesInElement> elementNodesProperty; - for (sofa::Size i = 0; i < NumberOfNodesInElement; ++i) - { - elementNodesProperty[i] = source->getNodeProperty(element[i], propertyAccessor); - } - - const auto property = - FiniteElement::Helper::evaluateValueInElement(elementNodesProperty, N); - - const auto density = source->evaluate(context, property); + const auto density = source->evaluate(context); for (sofa::Size i = 0; i < NumberOfNodesInElement; ++i) { diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.cpp index ce0f5f44ef2..4d74481b4fb 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.cpp @@ -21,38 +21,22 @@ ******************************************************************************/ #define SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_GEOMETRIC_SOURCE_TERM_CPP -#include +#include -#include #include #include namespace sofa::component::solidmechanics::fem::elastic { -void registerGeometricSourceTerm(sofa::core::ObjectFactory* factory) -{ - factory->registerObjects(sofa::core::ObjectRegistrationData("Source density prescribed at the nodes, per unit measure of the element") - .add< GeometricSourceTerm >() - .add< GeometricSourceTerm >() - .add< GeometricSourceTerm >() - .add< GeometricSourceTerm >() - .add< GeometricSourceTerm >() - .add< GeometricSourceTerm >() - .add< GeometricSourceTerm >() - .add< GeometricSourceTerm >() - .add< GeometricSourceTerm >() - ); -} - -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; } // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.h index 96a71435e27..6d7bb4dbd9c 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.h @@ -22,9 +22,10 @@ #pragma once #include -#include +#include #include -#include +#include +#include #if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_GEOMETRIC_SOURCE_TERM_CPP) #include @@ -36,55 +37,64 @@ namespace sofa::component::solidmechanics::fem::elastic /** * @class GeometricSourceTerm - * @brief A source density whose value is determined by the geometry, not by the solution. + * @brief A source term built from a nodal property. * - * The component calculates the integrand in evaluate() given a QuadratureContext and the - * interpolated property field. + * The property is provided by a linked BaseNodalProperty component. A derived class interpolates + * it with interpolateProperty() and turns it into a source density in evaluate(). * * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). * @tparam TElementType The type of finite element (e.g., sofa::geometry::Tetrahedron). + * @tparam TPropertyType The type of the nodal property (e.g., Deriv). */ -template -class GeometricSourceTerm : public sofa::core::BaseNodalProperty> +template +class GeometricSourceTerm : public BaseGeometricSourceTerm { public: using DataTypes = TDataTypes; using ElementType = TElementType; + using PropertyType = TPropertyType; - SOFA_CLASS(SOFA_TEMPLATE2(GeometricSourceTerm, DataTypes, ElementType), - SOFA_TEMPLATE(sofa::core::BaseNodalProperty, sofa::Deriv_t)); + SOFA_CLASS(SOFA_TEMPLATE3(GeometricSourceTerm, DataTypes, ElementType, PropertyType), + SOFA_TEMPLATE2(BaseGeometricSourceTerm, DataTypes, ElementType)); using Deriv = sofa::Deriv_t; using QuadratureContext = QuadratureContext; + using NodalProperty = sofa::core::BaseNodalProperty; /** - * @brief Source density at one quadrature point, per unit physical measure. - * - * @param context Geometry of the quadrature point. - * @param property The nodal property interpolated at the quadrature point. + * @brief Nodal values of the property this term is built from. */ - virtual Deriv evaluate(const QuadratureContext& context, const Deriv& property) const - { - SOFA_UNUSED(context); + sofa::SingleLink, NodalProperty, + sofa::BaseLink::FLAG_STOREPATH | sofa::BaseLink::FLAG_STRONGLINK> l_nodalProperty; - return property; - } + /** + * @brief Initializes the component and validates the linked nodal property. + */ + void init() override; protected: + using FiniteElement = sofa::fem::FiniteElement; + + static constexpr sofa::Size NumberOfNodesInElement = ElementType::NumberOfNodes; - GeometricSourceTerm() : sofa::core::BaseNodalProperty(Deriv{}) {} + GeometricSourceTerm(); + + /** + * @brief Value of the linked nodal property interpolated at the quadrature point. + */ + PropertyType interpolateProperty(const QuadratureContext& context) const; }; #if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_GEOMETRIC_SOURCE_TERM_CPP) -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; #endif } // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.inl new file mode 100644 index 00000000000..48ba8ce992f --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.inl @@ -0,0 +1,72 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once +#include + +namespace sofa::component::solidmechanics::fem::elastic +{ + +template +GeometricSourceTerm::GeometricSourceTerm() + : l_nodalProperty(initLink("nodalProperty", "Nodal values of the property this source term is " + "built from.")) +{ +} + +template +void GeometricSourceTerm::init() +{ + BaseGeometricSourceTerm::init(); + + if (this->isComponentStateInvalid()) + { + return; + } + + if (!l_nodalProperty) + { + msg_error(this) << "The 'nodalProperty' link must be set to a BaseNodalProperty component. " + "Linked path: '" << l_nodalProperty.getLinkedPath() << "'."; + this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); + return; + } + + this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid); +} + +template +PropertyType GeometricSourceTerm::interpolateProperty( + const QuadratureContext& context) const +{ + sofa::helper::ReadAccessor propertyAccessor { l_nodalProperty->d_property }; + + std::array elementNodesProperty; + for (sofa::Size i = 0; i < NumberOfNodesInElement; ++i) + { + elementNodesProperty[i] = + l_nodalProperty->getNodeProperty(context.element[i], propertyAccessor); + } + + return FiniteElement::Helper::evaluateValueInElement(elementNodesProperty, context.N); +} + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp index f211cf32b5d..7d8ea06ef9b 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp @@ -30,7 +30,6 @@ extern void registerBeamFEMForceField(sofa::core::ObjectFactory* factory); extern void registerCorotationalFEMForceField(sofa::core::ObjectFactory* factory); extern void registerFEMSourceTermIntegrator(sofa::core::ObjectFactory* factory); extern void registerFastTetrahedralCorotationalForceField(sofa::core::ObjectFactory* factory); -extern void registerGeometricSourceTerm(sofa::core::ObjectFactory* factory); extern void registerHexahedralFEMForceField(sofa::core::ObjectFactory* factory); extern void registerHexahedralFEMForceFieldAndMass(sofa::core::ObjectFactory* factory); extern void registerHexahedronFEMForceField(sofa::core::ObjectFactory* factory); @@ -73,7 +72,6 @@ void registerObjects(sofa::core::ObjectFactory* factory) registerCorotationalFEMForceField(factory); registerFEMSourceTermIntegrator(factory); registerFastTetrahedralCorotationalForceField(factory); - registerGeometricSourceTerm(factory); registerHexahedralFEMForceField(factory); registerHexahedralFEMForceFieldAndMass(factory); registerHexahedronFEMForceField(factory); From 6ae491c833b28d2bd48542707acf260a476adc86 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Wed, 9 Sep 2026 18:28:28 +0200 Subject: [PATCH 21/37] rename BaseGeometricSourceTerm to BaseSourceTerm --- .../SolidMechanics/FEM/Elastic/CMakeLists.txt | 4 +-- ...etricSourceTerm.cpp => BaseSourceTerm.cpp} | 22 +++++++------- ...GeometricSourceTerm.h => BaseSourceTerm.h} | 30 +++++++++---------- .../fem/elastic/FEMSourceTermIntegrator.h | 8 ++--- .../fem/elastic/FEMSourceTermIntegrator.inl | 2 +- .../fem/elastic/GeometricSourceTerm.h | 6 ++-- .../fem/elastic/GeometricSourceTerm.inl | 2 +- 7 files changed, 37 insertions(+), 37 deletions(-) rename Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/{BaseGeometricSourceTerm.cpp => BaseSourceTerm.cpp} (74%) rename Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/{BaseGeometricSourceTerm.h => BaseSourceTerm.h} (75%) diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt index 58a80c827f7..8c433423737 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt @@ -9,9 +9,9 @@ set(HEADER_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/fwd.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseElementLinearFEMForceField.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseElementLinearFEMForceField.inl - ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseGeometricSourceTerm.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseLinearElasticityFEMForceField.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseLinearElasticityFEMForceField.inl + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseSourceTerm.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BeamFEMForceField.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BeamFEMForceField.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CauchyStressEvaluator.h @@ -75,8 +75,8 @@ set(HEADER_FILES set(SOURCE_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/init.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseElementLinearFEMForceField.cpp - ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseGeometricSourceTerm.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseLinearElasticityFEMForceField.cpp + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseSourceTerm.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BeamFEMForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.cpp diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseGeometricSourceTerm.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.cpp similarity index 74% rename from Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseGeometricSourceTerm.cpp rename to Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.cpp index 7e6cf617062..b6cc5a93170 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseGeometricSourceTerm.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.cpp @@ -19,9 +19,9 @@ * * * Contact information: contact@sofa-framework.org * ******************************************************************************/ -#define SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_BASE_GEOMETRIC_SOURCE_TERM_CPP +#define SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_BASE_SOURCE_TERM_CPP -#include +#include #include #include @@ -29,14 +29,14 @@ namespace sofa::component::solidmechanics::fem::elastic { -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseSourceTerm; } // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseGeometricSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h similarity index 75% rename from Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseGeometricSourceTerm.h rename to Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h index 64c91610c7c..eae58b3057c 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseGeometricSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h @@ -26,7 +26,7 @@ #include #include -#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_BASE_GEOMETRIC_SOURCE_TERM_CPP) +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_BASE_SOURCE_TERM_CPP) #include #include #endif @@ -35,7 +35,7 @@ namespace sofa::component::solidmechanics::fem::elastic { /** - * @class BaseGeometricSourceTerm + * @class BaseSourceTerm * @brief A source density whose value is determined by the geometry, not by the solution. * * The component calculates the integrand in evaluate() given a QuadratureContext. @@ -44,13 +44,13 @@ namespace sofa::component::solidmechanics::fem::elastic * @tparam TElementType The type of finite element (e.g., sofa::geometry::Tetrahedron). */ template -class BaseGeometricSourceTerm : public sofa::core::objectmodel::BaseComponent +class BaseSourceTerm : public sofa::core::objectmodel::BaseComponent { public: using DataTypes = TDataTypes; using ElementType = TElementType; - SOFA_CLASS(SOFA_TEMPLATE2(BaseGeometricSourceTerm, DataTypes, ElementType), + SOFA_CLASS(SOFA_TEMPLATE2(BaseSourceTerm, DataTypes, ElementType), sofa::core::objectmodel::BaseComponent); using Deriv = sofa::Deriv_t; @@ -65,19 +65,19 @@ class BaseGeometricSourceTerm : public sofa::core::objectmodel::BaseComponent protected: - BaseGeometricSourceTerm() = default; + BaseSourceTerm() = default; }; -#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_BASE_GEOMETRIC_SOURCE_TERM_CPP) -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseGeometricSourceTerm; +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_BASE_SOURCE_TERM_CPP) +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API BaseSourceTerm; #endif } // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h index a9932c5dc3d..c401aa202c4 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h @@ -22,7 +22,7 @@ #pragma once #include -#include +#include #include #include #include @@ -40,7 +40,7 @@ namespace sofa::component::solidmechanics::fem::elastic * @brief Integrates a source density into consistent nodal loads. * * A source term contributes \f$ \int_{\Omega} N_a \, r \, d\Omega \f$ to the right-hand side, where - * r is the density evaluated by a linked BaseGeometricSourceTerm (through l_constantSources) at + * r is the density evaluated by a linked BaseSourceTerm (through l_constantSources) at * each quadrature point. * * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). @@ -70,9 +70,9 @@ class FEMSourceTermIntegrator : /** * @brief Source terms integrated by this component. * - * If left empty, the BaseGeometricSourceTerm components found in the current context are used. + * If left empty, the BaseSourceTerm components found in the current context are used. */ - sofa::MultiLink, BaseGeometricSourceTerm, + sofa::MultiLink, BaseSourceTerm, sofa::BaseLink::FLAG_STOREPATH | sofa::BaseLink::FLAG_STRONGLINK> l_constantSources; /** diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl index 944b7e7e23b..9a5f45a7142 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl @@ -77,7 +77,7 @@ void FEMSourceTermIntegrator::validateSources() // Gather all GeometricSourceTerm components in Context if empty if (l_constantSources.empty()) { - const auto sourcesInContext = this->getContext()->template getObjects >( + const auto sourcesInContext = this->getContext()->template getObjects >( sofa::core::objectmodel::BaseContext::Local); for (const auto& source : sourcesInContext) diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.h index 6d7bb4dbd9c..dc3b1b086ce 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.h @@ -22,7 +22,7 @@ #pragma once #include -#include +#include #include #include #include @@ -47,7 +47,7 @@ namespace sofa::component::solidmechanics::fem::elastic * @tparam TPropertyType The type of the nodal property (e.g., Deriv). */ template -class GeometricSourceTerm : public BaseGeometricSourceTerm +class GeometricSourceTerm : public BaseSourceTerm { public: using DataTypes = TDataTypes; @@ -55,7 +55,7 @@ class GeometricSourceTerm : public BaseGeometricSourceTerm; using QuadratureContext = QuadratureContext; diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.inl index 48ba8ce992f..21436b10645 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.inl +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.inl @@ -35,7 +35,7 @@ GeometricSourceTerm::GeometricSourceTerm() template void GeometricSourceTerm::init() { - BaseGeometricSourceTerm::init(); + BaseSourceTerm::init(); if (this->isComponentStateInvalid()) { From 6ccd0616434935f9b71704043182b4804b4afa69 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Wed, 9 Sep 2026 18:29:03 +0200 Subject: [PATCH 22/37] add nodal property interpolation helper --- .../SolidMechanics/FEM/Elastic/CMakeLists.txt | 1 + .../fem/elastic/NodalPropertyInterpolation.h | 63 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPropertyInterpolation.h diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt index 8c433423737..3210ea2457e 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt @@ -21,6 +21,7 @@ set(HEADER_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/GeometricSourceTerm.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/GeometricSourceTerm.inl + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalPropertyInterpolation.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/QuadratureContext.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.inl diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPropertyInterpolation.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPropertyInterpolation.h new file mode 100644 index 00000000000..312eca71a30 --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPropertyInterpolation.h @@ -0,0 +1,63 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once + +#include +#include +#include + +#include + +namespace sofa::component::solidmechanics::fem::elastic +{ + +/** + * @brief Value of a nodal property interpolated at a quadrature point. + * + * The property is gathered at the nodes of the element being integrated and combined with the + * shape functions evaluated at that point. + * + * @param property The component holding the nodal values. + * @param context Geometry of the quadrature point. + */ +template +PropertyType interpolateNodalProperty( + const sofa::core::BaseNodalProperty& property, + const QuadratureContext& context) +{ + using FiniteElement = typename QuadratureContext::FiniteElement; + + static constexpr sofa::Size NumberOfNodesInElement = QuadratureContext::NumberOfNodesInElement; + + sofa::helper::ReadAccessor>> propertyAccessor { + property.d_property}; + + std::array elementNodesProperty; + for (sofa::Size i = 0; i < NumberOfNodesInElement; ++i) + { + elementNodesProperty[i] = property.getNodeProperty(context.element[i], propertyAccessor); + } + + return FiniteElement::Helper::evaluateValueInElement(elementNodesProperty, context.N); +} + +} // namespace sofa::component::solidmechanics::fem::elastic From c90f6cb8aba6c1bf6387be4dbf570038f2b4673f Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Wed, 9 Sep 2026 18:33:12 +0200 Subject: [PATCH 23/37] add VectorSourceTerm and NodalSourceDensity --- .../SolidMechanics/FEM/Elastic/CMakeLists.txt | 8 ++- .../fem/elastic/FEMSourceTermIntegrator.inl | 2 +- ...cSourceTerm.cpp => NodalSourceDensity.cpp} | 27 ++++---- .../fem/elastic/NodalSourceDensity.h | 62 +++++++++++++++++ .../fem/elastic/VectorSourceTerm.cpp | 58 ++++++++++++++++ ...ometricSourceTerm.h => VectorSourceTerm.h} | 68 +++++++++---------- ...ricSourceTerm.inl => VectorSourceTerm.inl} | 34 ++++------ .../solidmechanics/fem/elastic/init.cpp | 4 ++ .../tests/FEMSourceTermIntegrator_test.cpp | 17 +++-- .../FEM/FEMSourceTermIntegrator.scn | 6 +- ...ricSourceTerm.scn => VectorSourceTerm.scn} | 7 +- 11 files changed, 210 insertions(+), 83 deletions(-) rename Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/{GeometricSourceTerm.cpp => NodalSourceDensity.cpp} (50%) create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalSourceDensity.h create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.cpp rename Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/{GeometricSourceTerm.h => VectorSourceTerm.h} (55%) rename Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/{GeometricSourceTerm.inl => VectorSourceTerm.inl} (63%) rename examples/Component/SolidMechanics/FEM/{GeometricSourceTerm.scn => VectorSourceTerm.scn} (92%) diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt index 3210ea2457e..5a54a6add6d 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt @@ -19,10 +19,11 @@ set(HEADER_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.inl - ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/GeometricSourceTerm.h - ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/GeometricSourceTerm.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalPropertyInterpolation.h + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalSourceDensity.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/QuadratureContext.h + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/VectorSourceTerm.h + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/VectorSourceTerm.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FastTetrahedralCorotationalForceField.h @@ -81,8 +82,9 @@ set(SOURCE_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BeamFEMForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.cpp - ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/GeometricSourceTerm.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.cpp + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalSourceDensity.cpp + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/VectorSourceTerm.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FastTetrahedralCorotationalForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/HexahedralFEMForceField.cpp diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl index 9a5f45a7142..1bc2ba9d2df 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl @@ -74,7 +74,7 @@ void FEMSourceTermIntegrator::init() template void FEMSourceTermIntegrator::validateSources() { - // Gather all GeometricSourceTerm components in Context if empty + // Gather all BaseSourceTerm components in Context if empty if (l_constantSources.empty()) { const auto sourcesInContext = this->getContext()->template getObjects >( diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalSourceDensity.cpp similarity index 50% rename from Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.cpp rename to Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalSourceDensity.cpp index 4d74481b4fb..e50d5adfba0 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalSourceDensity.cpp @@ -19,24 +19,27 @@ * * * Contact information: contact@sofa-framework.org * ******************************************************************************/ -#define SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_GEOMETRIC_SOURCE_TERM_CPP +#define SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_NODAL_SOURCE_DENSITY_CPP -#include +#include +#include #include -#include namespace sofa::component::solidmechanics::fem::elastic { -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; +void registerNodalSourceDensity(sofa::core::ObjectFactory* factory) +{ + factory->registerObjects(sofa::core::ObjectRegistrationData("Definition of a nodal source density (one vector per dof).") + .add< NodalSourceDensity >() + .add< NodalSourceDensity >() + .add< NodalSourceDensity >() + ); +} + +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalSourceDensity; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalSourceDensity; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalSourceDensity; } // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalSourceDensity.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalSourceDensity.h new file mode 100644 index 00000000000..e068695950b --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalSourceDensity.h @@ -0,0 +1,62 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once + +#include +#include +#include + +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_NODAL_SOURCE_DENSITY_CPP) +#include +#endif + +namespace sofa::component::solidmechanics::fem::elastic +{ + +/** + * @class NodalSourceDensity + * @brief A source density prescribed at the nodes, one vector per node. + * + * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). + */ +template +class NodalSourceDensity : public sofa::core::BaseNodalProperty> +{ +public: + using DataTypes = TDataTypes; + using Deriv = sofa::Deriv_t; + + SOFA_CLASS(SOFA_TEMPLATE(NodalSourceDensity, DataTypes), + SOFA_TEMPLATE(sofa::core::BaseNodalProperty, sofa::Deriv_t)); + +protected: + + NodalSourceDensity() : sofa::core::BaseNodalProperty(Deriv{}) {} +}; + +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_NODAL_SOURCE_DENSITY_CPP) +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalSourceDensity; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalSourceDensity; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalSourceDensity; +#endif + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.cpp new file mode 100644 index 00000000000..61c9507d0c0 --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.cpp @@ -0,0 +1,58 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#define SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_VECTOR_SOURCE_TERM_CPP + +#include + +#include +#include +#include + +namespace sofa::component::solidmechanics::fem::elastic +{ + +void registerVectorSourceTerm(sofa::core::ObjectFactory* factory) +{ + factory->registerObjects(sofa::core::ObjectRegistrationData("Source density given as a vector at each node, per unit measure of the element") + .add< VectorSourceTerm >() + .add< VectorSourceTerm >() + .add< VectorSourceTerm >() + .add< VectorSourceTerm >() + .add< VectorSourceTerm >() + .add< VectorSourceTerm >() + .add< VectorSourceTerm >() + .add< VectorSourceTerm >() + .add< VectorSourceTerm >() + ); +} + +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API VectorSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API VectorSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API VectorSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API VectorSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API VectorSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API VectorSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API VectorSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API VectorSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API VectorSourceTerm; + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.h similarity index 55% rename from Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.h rename to Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.h index dc3b1b086ce..63ac51b5655 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.h @@ -23,11 +23,10 @@ #include #include -#include +#include #include -#include -#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_GEOMETRIC_SOURCE_TERM_CPP) +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_VECTOR_SOURCE_TERM_CPP) #include #include #endif @@ -36,65 +35,62 @@ namespace sofa::component::solidmechanics::fem::elastic { /** - * @class GeometricSourceTerm - * @brief A source term built from a nodal property. + * @class VectorSourceTerm + * @brief A source density given directly as a vector at each node. * - * The property is provided by a linked BaseNodalProperty component. A derived class interpolates - * it with interpolateProperty() and turns it into a source density in evaluate(). + * The linked NodalSourceDensity is interpolated at the quadrature point and returned unchanged. + * The measure the integrator divides it by is the one of the element it is attached to: a body + * force on a volume element, a traction on a boundary one. * * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). * @tparam TElementType The type of finite element (e.g., sofa::geometry::Tetrahedron). - * @tparam TPropertyType The type of the nodal property (e.g., Deriv). */ -template -class GeometricSourceTerm : public BaseSourceTerm +template +class VectorSourceTerm : public BaseSourceTerm { public: using DataTypes = TDataTypes; using ElementType = TElementType; - using PropertyType = TPropertyType; - SOFA_CLASS(SOFA_TEMPLATE3(GeometricSourceTerm, DataTypes, ElementType, PropertyType), + SOFA_CLASS(SOFA_TEMPLATE2(VectorSourceTerm, DataTypes, ElementType), SOFA_TEMPLATE2(BaseSourceTerm, DataTypes, ElementType)); using Deriv = sofa::Deriv_t; using QuadratureContext = QuadratureContext; - using NodalProperty = sofa::core::BaseNodalProperty; + using NodalSourceDensity = + ::sofa::component::solidmechanics::fem::elastic::NodalSourceDensity; /** - * @brief Nodal values of the property this term is built from. + * @brief Nodal values of the source density this term integrates. */ - sofa::SingleLink, NodalProperty, - sofa::BaseLink::FLAG_STOREPATH | sofa::BaseLink::FLAG_STRONGLINK> l_nodalProperty; + sofa::SingleLink, NodalSourceDensity, + sofa::BaseLink::FLAG_STOREPATH | sofa::BaseLink::FLAG_STRONGLINK> l_sourceDensity; /** - * @brief Initializes the component and validates the linked nodal property. + * @brief Initializes the component and checks that a source density is linked. */ void init() override; -protected: - using FiniteElement = sofa::fem::FiniteElement; - - static constexpr sofa::Size NumberOfNodesInElement = ElementType::NumberOfNodes; - - GeometricSourceTerm(); - /** - * @brief Value of the linked nodal property interpolated at the quadrature point. + * @brief The linked source density interpolated at the quadrature point. */ - PropertyType interpolateProperty(const QuadratureContext& context) const; + Deriv evaluate(const QuadratureContext& context) const override; + +protected: + + VectorSourceTerm(); }; -#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_GEOMETRIC_SOURCE_TERM_CPP) -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API GeometricSourceTerm>; +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_VECTOR_SOURCE_TERM_CPP) +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API VectorSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API VectorSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API VectorSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API VectorSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API VectorSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API VectorSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API VectorSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API VectorSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API VectorSourceTerm; #endif } // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.inl similarity index 63% rename from Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.inl rename to Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.inl index 21436b10645..ac661fe64bc 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/GeometricSourceTerm.inl +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.inl @@ -20,20 +20,20 @@ * Contact information: contact@sofa-framework.org * ******************************************************************************/ #pragma once -#include +#include +#include namespace sofa::component::solidmechanics::fem::elastic { -template -GeometricSourceTerm::GeometricSourceTerm() - : l_nodalProperty(initLink("nodalProperty", "Nodal values of the property this source term is " - "built from.")) +template +VectorSourceTerm::VectorSourceTerm() + : l_sourceDensity(initLink("sourceDensity", "Nodal source density integrated by this term.")) { } -template -void GeometricSourceTerm::init() +template +void VectorSourceTerm::init() { BaseSourceTerm::init(); @@ -42,10 +42,10 @@ void GeometricSourceTerm::init() return; } - if (!l_nodalProperty) + if (!l_sourceDensity) { - msg_error(this) << "The 'nodalProperty' link must be set to a BaseNodalProperty component. " - "Linked path: '" << l_nodalProperty.getLinkedPath() << "'."; + msg_error(this) << "The 'sourceDensity' link must be set to a NodalSourceDensity " + "component. Linked path: '" << l_sourceDensity.getLinkedPath() << "'."; this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); return; } @@ -53,20 +53,16 @@ void GeometricSourceTerm::init() this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid); } -template -PropertyType GeometricSourceTerm::interpolateProperty( +template +sofa::Deriv_t VectorSourceTerm::evaluate( const QuadratureContext& context) const { - sofa::helper::ReadAccessor propertyAccessor { l_nodalProperty->d_property }; - - std::array elementNodesProperty; - for (sofa::Size i = 0; i < NumberOfNodesInElement; ++i) + if (!l_sourceDensity) { - elementNodesProperty[i] = - l_nodalProperty->getNodeProperty(context.element[i], propertyAccessor); + return Deriv{}; } - return FiniteElement::Helper::evaluateValueInElement(elementNodesProperty, context.N); + return interpolateNodalProperty(*l_sourceDensity, context); } } // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp index 7d8ea06ef9b..83938c46454 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp @@ -35,6 +35,7 @@ extern void registerHexahedralFEMForceFieldAndMass(sofa::core::ObjectFactory* fa extern void registerHexahedronFEMForceField(sofa::core::ObjectFactory* factory); extern void registerHexahedronFEMForceFieldAndMass(sofa::core::ObjectFactory* factory); extern void registerLinearSmallStrainFEMForceField(sofa::core::ObjectFactory* factory); +extern void registerNodalSourceDensity(sofa::core::ObjectFactory* factory); extern void registerQuadBendingFEMForceField(sofa::core::ObjectFactory* factory); extern void registerTetrahedralCorotationalFEMForceField(sofa::core::ObjectFactory* factory); extern void registerTetrahedronFEMForceField(sofa::core::ObjectFactory* factory); @@ -42,6 +43,7 @@ extern void registerTriangleFEMForceField(sofa::core::ObjectFactory* factory); extern void registerTriangularAnisotropicFEMForceField(sofa::core::ObjectFactory* factory); extern void registerTriangularFEMForceField(sofa::core::ObjectFactory* factory); extern void registerTriangularFEMForceFieldOptim(sofa::core::ObjectFactory* factory); +extern void registerVectorSourceTerm(sofa::core::ObjectFactory* factory); extern void registerVonMisesStress(sofa::core::ObjectFactory* factory); extern "C" { @@ -77,6 +79,7 @@ void registerObjects(sofa::core::ObjectFactory* factory) registerHexahedronFEMForceField(factory); registerHexahedronFEMForceFieldAndMass(factory); registerLinearSmallStrainFEMForceField(factory); + registerNodalSourceDensity(factory); registerQuadBendingFEMForceField(factory); registerTetrahedralCorotationalFEMForceField(factory); registerTetrahedronFEMForceField(factory); @@ -84,6 +87,7 @@ void registerObjects(sofa::core::ObjectFactory* factory) registerTriangularAnisotropicFEMForceField(factory); registerTriangularFEMForceField(factory); registerTriangularFEMForceFieldOptim(factory); + registerVectorSourceTerm(factory); registerVonMisesStress(factory); } diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/tests/FEMSourceTermIntegrator_test.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/tests/FEMSourceTermIntegrator_test.cpp index 68b793f6a11..2e76cacf63a 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/tests/FEMSourceTermIntegrator_test.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/tests/FEMSourceTermIntegrator_test.cpp @@ -28,7 +28,7 @@ #include #include -#include +#include #include #include #include @@ -83,18 +83,20 @@ class FEMSourceTermIntegrator_test : public testing::BaseTest } }; -// Splitting one GeometricSourceTerm into several must not change the integrated force. +// Splitting one VectorSourceTerm into several must not change the integrated force. TEST_F(FEMSourceTermIntegrator_test, MultipleSourcesSumToOne) { m_root = makeMesh(); - createObject(m_root, "GeometricSourceTerm", {{"name", "full"}, {"template", "Vec2,Triangle"}, {"property", "300 -600"}}); + createObject(m_root, "NodalSourceDensity", {{"name", "fullDensity"}, {"template", "Vec2"}, {"property", "300 -600"}}); + createObject(m_root, "VectorSourceTerm", {{"name", "full"}, {"template", "Vec2,Triangle"}, {"sourceDensity", "@fullDensity"}}); auto* one = dynamic_cast(createObject(m_root, "FEMSourceTermIntegrator", {{"name", "one"}, {"template", "Vec2,Triangle"}, {"topology", "@mesh"}, {"constantSources", "@full"}}).get()); - createObject(m_root, "GeometricSourceTerm", {{"name", "a"}, {"template", "Vec2,Triangle"}, {"property", "100 -200"}}); - createObject(m_root, "GeometricSourceTerm", {{"name", "b"}, {"template", "Vec2,Triangle"}, {"property", "100 -200"}}); - createObject(m_root, "GeometricSourceTerm", {{"name", "c"}, {"template", "Vec2,Triangle"}, {"property", "100 -200"}}); + createObject(m_root, "NodalSourceDensity", {{"name", "thirdDensity"}, {"template", "Vec2"}, {"property", "100 -200"}}); + createObject(m_root, "VectorSourceTerm", {{"name", "a"}, {"template", "Vec2,Triangle"}, {"sourceDensity", "@thirdDensity"}}); + createObject(m_root, "VectorSourceTerm", {{"name", "b"}, {"template", "Vec2,Triangle"}, {"sourceDensity", "@thirdDensity"}}); + createObject(m_root, "VectorSourceTerm", {{"name", "c"}, {"template", "Vec2,Triangle"}, {"sourceDensity", "@thirdDensity"}}); auto* three = dynamic_cast(createObject(m_root, "FEMSourceTermIntegrator", {{"name", "three"}, {"template", "Vec2,Triangle"}, {"topology", "@mesh"}, {"constantSources", "@a @b @c"}}).get()); @@ -114,7 +116,8 @@ TEST_F(FEMSourceTermIntegrator_test, PotentialEnergyMatchesWork) { m_root = makeMesh(); - createObject(m_root, "GeometricSourceTerm", {{"name", "bodyForce"}, {"template", "Vec2,Triangle"}, {"property", "300 -600"}}); + createObject(m_root, "NodalSourceDensity", {{"name", "bodyForceDensity"}, {"template", "Vec2"}, {"property", "300 -600"}}); + createObject(m_root, "VectorSourceTerm", {{"name", "bodyForce"}, {"template", "Vec2,Triangle"}, {"sourceDensity", "@bodyForceDensity"}}); auto* integrator = dynamic_cast(createObject(m_root, "FEMSourceTermIntegrator", {{"name", "source"}, {"template", "Vec2,Triangle"}, {"topology", "@mesh"}, {"constantSources", "@bodyForce"}}).get()); diff --git a/examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn b/examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn index 106e168465f..ee34aa4d3de 100644 --- a/examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn +++ b/examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn @@ -67,12 +67,13 @@ - + @@ -97,12 +98,13 @@ - + diff --git a/examples/Component/SolidMechanics/FEM/GeometricSourceTerm.scn b/examples/Component/SolidMechanics/FEM/VectorSourceTerm.scn similarity index 92% rename from examples/Component/SolidMechanics/FEM/GeometricSourceTerm.scn rename to examples/Component/SolidMechanics/FEM/VectorSourceTerm.scn index a02fcc6c035..a2bb497f3de 100644 --- a/examples/Component/SolidMechanics/FEM/GeometricSourceTerm.scn +++ b/examples/Component/SolidMechanics/FEM/VectorSourceTerm.scn @@ -42,7 +42,7 @@ - + @@ -64,8 +64,9 @@ - - + + + From 07b3fef70e8469af6d6869186eeb4d934663ac17 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Wed, 9 Sep 2026 18:35:48 +0200 Subject: [PATCH 24/37] add PressureSourceTerm and NodalPressure --- .../SolidMechanics/FEM/Elastic/CMakeLists.txt | 6 ++ .../fem/elastic/ElementNormal.h | 56 ++++++++++++ .../fem/elastic/NodalPressure.cpp | 43 +++++++++ .../fem/elastic/NodalPressure.h | 61 +++++++++++++ .../fem/elastic/PressureSourceTerm.cpp | 46 ++++++++++ .../fem/elastic/PressureSourceTerm.h | 91 +++++++++++++++++++ .../fem/elastic/PressureSourceTerm.inl | 69 ++++++++++++++ .../solidmechanics/fem/elastic/init.cpp | 4 + 8 files changed, 376 insertions(+) create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ElementNormal.h create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPressure.cpp create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPressure.h create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.cpp create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.h create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.inl diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt index 5a54a6add6d..122c99c8317 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt @@ -17,10 +17,14 @@ set(HEADER_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CauchyStressEvaluator.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.inl + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/ElementNormal.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.inl + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalPressure.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalPropertyInterpolation.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalSourceDensity.h + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/PressureSourceTerm.h + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/PressureSourceTerm.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/QuadratureContext.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/VectorSourceTerm.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/VectorSourceTerm.inl @@ -83,7 +87,9 @@ set(SOURCE_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.cpp + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalPressure.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalSourceDensity.cpp + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/PressureSourceTerm.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/VectorSourceTerm.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FastTetrahedralCorotationalForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMForceField.cpp diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ElementNormal.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ElementNormal.h new file mode 100644 index 00000000000..d2d6456050d --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ElementNormal.h @@ -0,0 +1,56 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once + +#include +#include + +namespace sofa::component::solidmechanics::fem::elastic +{ + +/** + * @brief Unit normal of a codimension-1 element, from the jacobian of its mapping. + * + * Defined only where the element spans one dimension less than the space it lives in: a surface + * element in 3D, an edge in 2D. Its orientation follows the node ordering of the element. + * + * @param jacobian dx/dq of the reference-to-physical mapping at the point of interest. + */ +template +sofa::type::Vec elementNormal( + const sofa::type::Mat& jacobian) +{ + static_assert(TopologicalDimension + 1 == spatial_dimensions, + "A normal is only defined for an element of codimension 1."); + + if constexpr (spatial_dimensions == 3) + { + return jacobian.col(0).cross(jacobian.col(1)).normalized(); + } + else + { + const sofa::type::Vec<2, Real> tangent = jacobian.col(0); + return sofa::type::Vec<2, Real>(tangent[1], -tangent[0]).normalized(); + } +} + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPressure.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPressure.cpp new file mode 100644 index 00000000000..c2251b6be5e --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPressure.cpp @@ -0,0 +1,43 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#define SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_NODAL_PRESSURE_CPP + +#include + +#include +#include + +namespace sofa::component::solidmechanics::fem::elastic +{ + +void registerNodalPressure(sofa::core::ObjectFactory* factory) +{ + factory->registerObjects(sofa::core::ObjectRegistrationData("Definition of a nodal pressure (one scalar per dof).") + .add< NodalPressure >() + .add< NodalPressure >() + ); +} + +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalPressure; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalPressure; + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPressure.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPressure.h new file mode 100644 index 00000000000..adf237ee09b --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPressure.h @@ -0,0 +1,61 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once + +#include +#include +#include + +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_NODAL_PRESSURE_CPP) +#include +#endif + +namespace sofa::component::solidmechanics::fem::elastic +{ + +/** + * @class NodalPressure + * @brief A pressure prescribed at the nodes, one scalar per node. + * + * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). + */ +template +class NodalPressure : public sofa::core::BaseNodalProperty> +{ +public: + using DataTypes = TDataTypes; + using Real = sofa::Real_t; + + SOFA_CLASS(SOFA_TEMPLATE(NodalPressure, DataTypes), + SOFA_TEMPLATE(sofa::core::BaseNodalProperty, sofa::Real_t)); + +protected: + + NodalPressure() : sofa::core::BaseNodalProperty(Real{}) {} +}; + +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_NODAL_PRESSURE_CPP) +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalPressure; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalPressure; +#endif + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.cpp new file mode 100644 index 00000000000..d7481005499 --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.cpp @@ -0,0 +1,46 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#define SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_PRESSURE_SOURCE_TERM_CPP + +#include + +#include +#include +#include + +namespace sofa::component::solidmechanics::fem::elastic +{ + +void registerPressureSourceTerm(sofa::core::ObjectFactory* factory) +{ + factory->registerObjects(sofa::core::ObjectRegistrationData("Traction obtained from a pressure prescribed at the nodes, acting along the normal of the element") + .add< PressureSourceTerm >() + .add< PressureSourceTerm >() + .add< PressureSourceTerm >() + ); +} + +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API PressureSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API PressureSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API PressureSourceTerm; + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.h new file mode 100644 index 00000000000..7302e7145da --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.h @@ -0,0 +1,91 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once + +#include +#include +#include +#include + +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_PRESSURE_SOURCE_TERM_CPP) +#include +#include +#endif + +namespace sofa::component::solidmechanics::fem::elastic +{ + +/** + * @class PressureSourceTerm + * @brief A traction \f$ p \, n \f$ built from a pressure prescribed at the nodes. + * + * The linked NodalPressure is interpolated at the quadrature point and multiplied by the unit + * normal of the element. A positive pressure acts along the normal, whose orientation follows the + * node ordering of the element. + * + * Only available on elements of codimension 1, the ones that have a normal. + * + * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). + * @tparam TElementType The type of finite element (e.g., sofa::geometry::Triangle). + */ +template +class PressureSourceTerm : public BaseSourceTerm +{ +public: + using DataTypes = TDataTypes; + using ElementType = TElementType; + + SOFA_CLASS(SOFA_TEMPLATE2(PressureSourceTerm, DataTypes, ElementType), + SOFA_TEMPLATE2(BaseSourceTerm, DataTypes, ElementType)); + + using Deriv = sofa::Deriv_t; + using QuadratureContext = QuadratureContext; + using NodalPressure = ::sofa::component::solidmechanics::fem::elastic::NodalPressure; + + /** + * @brief Nodal values of the pressure this term integrates. + */ + sofa::SingleLink, NodalPressure, + sofa::BaseLink::FLAG_STOREPATH | sofa::BaseLink::FLAG_STRONGLINK> l_pressure; + + /** + * @brief Initializes the component and checks that a pressure is linked. + */ + void init() override; + + /** + * @brief The linked pressure interpolated at the quadrature point, times the unit normal. + */ + Deriv evaluate(const QuadratureContext& context) const override; + +protected: + + PressureSourceTerm(); +}; + +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_PRESSURE_SOURCE_TERM_CPP) +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API PressureSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API PressureSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API PressureSourceTerm; +#endif + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.inl new file mode 100644 index 00000000000..ae6e7f99b13 --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.inl @@ -0,0 +1,69 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once +#include +#include +#include + +namespace sofa::component::solidmechanics::fem::elastic +{ + +template +PressureSourceTerm::PressureSourceTerm() + : l_pressure(initLink("pressure", "Nodal pressure integrated by this term.")) +{ +} + +template +void PressureSourceTerm::init() +{ + BaseSourceTerm::init(); + + if (this->isComponentStateInvalid()) + { + return; + } + + if (!l_pressure) + { + msg_error(this) << "The 'pressure' link must be set to a NodalPressure component. " + "Linked path: '" << l_pressure.getLinkedPath() << "'."; + this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); + return; + } + + this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid); +} + +template +sofa::Deriv_t PressureSourceTerm::evaluate( + const QuadratureContext& context) const +{ + if (!l_pressure) + { + return Deriv{}; + } + + return elementNormal(context.jacobian) * interpolateNodalProperty(*l_pressure, context); +} + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp index 83938c46454..61264ce3411 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp @@ -35,7 +35,9 @@ extern void registerHexahedralFEMForceFieldAndMass(sofa::core::ObjectFactory* fa extern void registerHexahedronFEMForceField(sofa::core::ObjectFactory* factory); extern void registerHexahedronFEMForceFieldAndMass(sofa::core::ObjectFactory* factory); extern void registerLinearSmallStrainFEMForceField(sofa::core::ObjectFactory* factory); +extern void registerNodalPressure(sofa::core::ObjectFactory* factory); extern void registerNodalSourceDensity(sofa::core::ObjectFactory* factory); +extern void registerPressureSourceTerm(sofa::core::ObjectFactory* factory); extern void registerQuadBendingFEMForceField(sofa::core::ObjectFactory* factory); extern void registerTetrahedralCorotationalFEMForceField(sofa::core::ObjectFactory* factory); extern void registerTetrahedronFEMForceField(sofa::core::ObjectFactory* factory); @@ -79,7 +81,9 @@ void registerObjects(sofa::core::ObjectFactory* factory) registerHexahedronFEMForceField(factory); registerHexahedronFEMForceFieldAndMass(factory); registerLinearSmallStrainFEMForceField(factory); + registerNodalPressure(factory); registerNodalSourceDensity(factory); + registerPressureSourceTerm(factory); registerQuadBendingFEMForceField(factory); registerTetrahedralCorotationalFEMForceField(factory); registerTetrahedronFEMForceField(factory); From b58cea467f03e971b61ad88bcef26b113eba9540 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Wed, 9 Sep 2026 18:38:13 +0200 Subject: [PATCH 25/37] add StressSourceTerm and NodalStress --- .../SolidMechanics/FEM/Elastic/CMakeLists.txt | 5 + .../fem/elastic/NodalStress.cpp | 43 ++++++++ .../solidmechanics/fem/elastic/NodalStress.h | 72 ++++++++++++++ .../fem/elastic/StressSourceTerm.cpp | 46 +++++++++ .../fem/elastic/StressSourceTerm.h | 97 +++++++++++++++++++ .../fem/elastic/StressSourceTerm.inl | 77 +++++++++++++++ .../solidmechanics/fem/elastic/init.cpp | 4 + 7 files changed, 344 insertions(+) create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalStress.cpp create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalStress.h create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.cpp create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.h create mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.inl diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt index 122c99c8317..b4409a5224b 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt @@ -23,9 +23,12 @@ set(HEADER_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalPressure.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalPropertyInterpolation.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalSourceDensity.h + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalStress.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/PressureSourceTerm.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/PressureSourceTerm.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/QuadratureContext.h + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/StressSourceTerm.h + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/StressSourceTerm.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/VectorSourceTerm.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/VectorSourceTerm.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.h @@ -89,7 +92,9 @@ set(SOURCE_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalPressure.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalSourceDensity.cpp + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalStress.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/PressureSourceTerm.cpp + ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/StressSourceTerm.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/VectorSourceTerm.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FastTetrahedralCorotationalForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMForceField.cpp diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalStress.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalStress.cpp new file mode 100644 index 00000000000..9df21bd28dc --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalStress.cpp @@ -0,0 +1,43 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#define SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_NODAL_STRESS_CPP + +#include + +#include +#include + +namespace sofa::component::solidmechanics::fem::elastic +{ + +void registerNodalStress(sofa::core::ObjectFactory* factory) +{ + factory->registerObjects(sofa::core::ObjectRegistrationData("Definition of a nodal symmetric stress tensor (one tensor per dof).") + .add< NodalStress >() + .add< NodalStress >() + ); +} + +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalStress; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalStress; + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalStress.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalStress.h new file mode 100644 index 00000000000..3884cfa2542 --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalStress.h @@ -0,0 +1,72 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once + +#include +#include +#include +#include +#include + +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_NODAL_STRESS_CPP) +#include +#endif + +namespace sofa::component::solidmechanics::fem::elastic +{ + +/// The independent components of a symmetric stress tensor, in the storage order of MatSym. +template +using StressComponents = sofa::type::Vec< + sofa::type::NumberOfIndependentElements, + sofa::Real_t>; + +/** + * @class NodalStress + * @brief A symmetric stress tensor prescribed at the nodes, one tensor per node. + * + * A tensor is written as its independent components in the storage order of MatSym, which is not + * the standard Voigt one: xx xy yy xz yz zz in 3D, xx xy yy in 2D. + * + * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). + */ +template +class NodalStress : public sofa::core::BaseNodalProperty> +{ +public: + using DataTypes = TDataTypes; + using Components = StressComponents; + + SOFA_CLASS(SOFA_TEMPLATE(NodalStress, DataTypes), + SOFA_TEMPLATE(sofa::core::BaseNodalProperty, StressComponents)); + +protected: + + NodalStress() : sofa::core::BaseNodalProperty(Components{}) {} +}; + +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_NODAL_STRESS_CPP) +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalStress; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalStress; +#endif + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.cpp new file mode 100644 index 00000000000..02b205ba1e7 --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.cpp @@ -0,0 +1,46 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#define SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_STRESS_SOURCE_TERM_CPP + +#include + +#include +#include +#include + +namespace sofa::component::solidmechanics::fem::elastic +{ + +void registerStressSourceTerm(sofa::core::ObjectFactory* factory) +{ + factory->registerObjects(sofa::core::ObjectRegistrationData("Traction obtained from a symmetric stress tensor prescribed at the nodes, contracted with the normal of the element") + .add< StressSourceTerm >() + .add< StressSourceTerm >() + .add< StressSourceTerm >() + ); +} + +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API StressSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API StressSourceTerm; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API StressSourceTerm; + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.h new file mode 100644 index 00000000000..d062298009c --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.h @@ -0,0 +1,97 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once + +#include +#include +#include +#include + +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_STRESS_SOURCE_TERM_CPP) +#include +#include +#endif + +namespace sofa::component::solidmechanics::fem::elastic +{ + +/** + * @class StressSourceTerm + * @brief A traction \f$ \sigma \, n \f$ built from a stress tensor prescribed at the nodes. + * + * The linked NodalStress is interpolated at the quadrature point and contracted with the unit + * normal of the element, whose orientation follows the node ordering. + * + * The tensor must be symmetric, which excludes the first Piola-Kirchhoff stress. + * + * Only available on elements of codimension 1, the ones that have a normal. + * + * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). + * @tparam TElementType The type of finite element (e.g., sofa::geometry::Triangle). + */ +template +class StressSourceTerm : public BaseSourceTerm +{ +public: + using DataTypes = TDataTypes; + using ElementType = TElementType; + + SOFA_CLASS(SOFA_TEMPLATE2(StressSourceTerm, DataTypes, ElementType), + SOFA_TEMPLATE2(BaseSourceTerm, DataTypes, ElementType)); + + using Deriv = sofa::Deriv_t; + using Real = sofa::Real_t; + using QuadratureContext = QuadratureContext; + using NodalStress = ::sofa::component::solidmechanics::fem::elastic::NodalStress; + + static constexpr sofa::Size spatial_dimensions = DataTypes::spatial_dimensions; + + using StressTensor = sofa::type::MatSym; + + /** + * @brief Nodal values of the stress tensor this term integrates. + */ + sofa::SingleLink, NodalStress, + sofa::BaseLink::FLAG_STOREPATH | sofa::BaseLink::FLAG_STRONGLINK> l_stress; + + /** + * @brief Initializes the component and checks that a stress is linked. + */ + void init() override; + + /** + * @brief The linked stress interpolated at the quadrature point, contracted with the normal. + */ + Deriv evaluate(const QuadratureContext& context) const override; + +protected: + + StressSourceTerm(); +}; + +#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_STRESS_SOURCE_TERM_CPP) +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API StressSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API StressSourceTerm; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API StressSourceTerm; +#endif + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.inl new file mode 100644 index 00000000000..edb834e24df --- /dev/null +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.inl @@ -0,0 +1,77 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once +#include +#include +#include + +namespace sofa::component::solidmechanics::fem::elastic +{ + +template +StressSourceTerm::StressSourceTerm() + : l_stress(initLink("stress", "Nodal stress tensor integrated by this term.")) +{ +} + +template +void StressSourceTerm::init() +{ + BaseSourceTerm::init(); + + if (this->isComponentStateInvalid()) + { + return; + } + + if (!l_stress) + { + msg_error(this) << "The 'stress' link must be set to a NodalStress component. " + "Linked path: '" << l_stress.getLinkedPath() << "'."; + this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); + return; + } + + this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid); +} + +template +sofa::Deriv_t StressSourceTerm::evaluate( + const QuadratureContext& context) const +{ + if (!l_stress) + { + return Deriv{}; + } + + const auto components = interpolateNodalProperty(*l_stress, context); + + StressTensor stress; + for (sofa::Size i = 0; i < StressTensor::NumberStoredValues; ++i) + { + stress[i] = components[i]; + } + + return stress * elementNormal(context.jacobian); +} + +} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp index 61264ce3411..23056027c0c 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/init.cpp @@ -37,8 +37,10 @@ extern void registerHexahedronFEMForceFieldAndMass(sofa::core::ObjectFactory* fa extern void registerLinearSmallStrainFEMForceField(sofa::core::ObjectFactory* factory); extern void registerNodalPressure(sofa::core::ObjectFactory* factory); extern void registerNodalSourceDensity(sofa::core::ObjectFactory* factory); +extern void registerNodalStress(sofa::core::ObjectFactory* factory); extern void registerPressureSourceTerm(sofa::core::ObjectFactory* factory); extern void registerQuadBendingFEMForceField(sofa::core::ObjectFactory* factory); +extern void registerStressSourceTerm(sofa::core::ObjectFactory* factory); extern void registerTetrahedralCorotationalFEMForceField(sofa::core::ObjectFactory* factory); extern void registerTetrahedronFEMForceField(sofa::core::ObjectFactory* factory); extern void registerTriangleFEMForceField(sofa::core::ObjectFactory* factory); @@ -83,8 +85,10 @@ void registerObjects(sofa::core::ObjectFactory* factory) registerLinearSmallStrainFEMForceField(factory); registerNodalPressure(factory); registerNodalSourceDensity(factory); + registerNodalStress(factory); registerPressureSourceTerm(factory); registerQuadBendingFEMForceField(factory); + registerStressSourceTerm(factory); registerTetrahedralCorotationalFEMForceField(factory); registerTetrahedronFEMForceField(factory); registerTriangleFEMForceField(factory); From 0086e486a0617417cff833703ac0273f80939369 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Thu, 10 Sep 2026 11:12:22 +0200 Subject: [PATCH 26/37] move property interpolation into BaseSourceTerm --- .../SolidMechanics/FEM/Elastic/CMakeLists.txt | 1 - .../fem/elastic/BaseSourceTerm.h | 34 ++++++++++ .../fem/elastic/NodalPropertyInterpolation.h | 63 ------------------- .../fem/elastic/PressureSourceTerm.inl | 3 +- .../fem/elastic/StressSourceTerm.inl | 3 +- .../fem/elastic/VectorSourceTerm.inl | 3 +- 6 files changed, 37 insertions(+), 70 deletions(-) delete mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPropertyInterpolation.h diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt index b4409a5224b..fcb8de179c4 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt @@ -21,7 +21,6 @@ set(HEADER_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalPressure.h - ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalPropertyInterpolation.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalSourceDensity.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalStress.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/PressureSourceTerm.h diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h index eae58b3057c..c84fbb65810 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h @@ -23,8 +23,13 @@ #include #include +#include #include #include +#include +#include + +#include #if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_BASE_SOURCE_TERM_CPP) #include @@ -66,6 +71,35 @@ class BaseSourceTerm : public sofa::core::objectmodel::BaseComponent protected: BaseSourceTerm() = default; + + /** + * @brief Value of a nodal property interpolated at the quadrature point. + * + * The property is gathered at the nodes of the element being integrated and combined with the + * shape functions evaluated at that point. + * + * @param property The component holding the nodal values. + * @param context Geometry of the quadrature point. + */ + template + static PropertyType interpolateProperty( + const sofa::core::BaseNodalProperty& property, + const QuadratureContext& context) + { + static constexpr sofa::Size NumberOfNodesInElement = ElementType::NumberOfNodes; + + sofa::helper::ReadAccessor>> propertyAccessor { + property.d_property}; + + std::array elementNodesProperty; + for (sofa::Size i = 0; i < NumberOfNodesInElement; ++i) + { + elementNodesProperty[i] = property.getNodeProperty(context.element[i], propertyAccessor); + } + + return QuadratureContext::FiniteElement::Helper::evaluateValueInElement( + elementNodesProperty, context.N); + } }; #if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_BASE_SOURCE_TERM_CPP) diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPropertyInterpolation.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPropertyInterpolation.h deleted file mode 100644 index 312eca71a30..00000000000 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPropertyInterpolation.h +++ /dev/null @@ -1,63 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once - -#include -#include -#include - -#include - -namespace sofa::component::solidmechanics::fem::elastic -{ - -/** - * @brief Value of a nodal property interpolated at a quadrature point. - * - * The property is gathered at the nodes of the element being integrated and combined with the - * shape functions evaluated at that point. - * - * @param property The component holding the nodal values. - * @param context Geometry of the quadrature point. - */ -template -PropertyType interpolateNodalProperty( - const sofa::core::BaseNodalProperty& property, - const QuadratureContext& context) -{ - using FiniteElement = typename QuadratureContext::FiniteElement; - - static constexpr sofa::Size NumberOfNodesInElement = QuadratureContext::NumberOfNodesInElement; - - sofa::helper::ReadAccessor>> propertyAccessor { - property.d_property}; - - std::array elementNodesProperty; - for (sofa::Size i = 0; i < NumberOfNodesInElement; ++i) - { - elementNodesProperty[i] = property.getNodeProperty(context.element[i], propertyAccessor); - } - - return FiniteElement::Helper::evaluateValueInElement(elementNodesProperty, context.N); -} - -} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.inl index ae6e7f99b13..239a9e7c6b1 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.inl +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.inl @@ -22,7 +22,6 @@ #pragma once #include #include -#include namespace sofa::component::solidmechanics::fem::elastic { @@ -63,7 +62,7 @@ sofa::Deriv_t PressureSourceTerm::evaluate( return Deriv{}; } - return elementNormal(context.jacobian) * interpolateNodalProperty(*l_pressure, context); + return elementNormal(context.jacobian) * this->interpolateProperty(*l_pressure, context); } } // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.inl index edb834e24df..7691cce89fd 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.inl +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.inl @@ -22,7 +22,6 @@ #pragma once #include #include -#include namespace sofa::component::solidmechanics::fem::elastic { @@ -63,7 +62,7 @@ sofa::Deriv_t StressSourceTerm::evaluate( return Deriv{}; } - const auto components = interpolateNodalProperty(*l_stress, context); + const auto components = this->interpolateProperty(*l_stress, context); StressTensor stress; for (sofa::Size i = 0; i < StressTensor::NumberStoredValues; ++i) diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.inl index ac661fe64bc..51db6195968 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.inl +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.inl @@ -21,7 +21,6 @@ ******************************************************************************/ #pragma once #include -#include namespace sofa::component::solidmechanics::fem::elastic { @@ -62,7 +61,7 @@ sofa::Deriv_t VectorSourceTerm::evaluate( return Deriv{}; } - return interpolateNodalProperty(*l_sourceDensity, context); + return this->interpolateProperty(*l_sourceDensity, context); } } // namespace sofa::component::solidmechanics::fem::elastic From 5d9851fd49a790b5d8c20dccec74733d4f7e451f Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Thu, 10 Sep 2026 11:22:34 +0200 Subject: [PATCH 27/37] move elementNormal into BaseSourceTerm.h --- .../SolidMechanics/FEM/Elastic/CMakeLists.txt | 1 - .../fem/elastic/BaseSourceTerm.h | 28 ++++++++++ .../fem/elastic/ElementNormal.h | 56 ------------------- .../fem/elastic/PressureSourceTerm.inl | 1 - .../fem/elastic/StressSourceTerm.inl | 1 - 5 files changed, 28 insertions(+), 59 deletions(-) delete mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ElementNormal.h diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt index fcb8de179c4..855fe2d715d 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt @@ -17,7 +17,6 @@ set(HEADER_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CauchyStressEvaluator.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.inl - ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/ElementNormal.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalPressure.h diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h index c84fbb65810..453822afd7d 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include @@ -39,6 +41,32 @@ namespace sofa::component::solidmechanics::fem::elastic { +/** + * @brief Unit normal of a codimension-1 element, from the jacobian of its mapping. + * + * Defined only where the element spans one dimension less than the space it lives in: a surface + * element in 3D, an edge in 2D. Its orientation follows the node ordering of the element. + * + * @param jacobian dx/dq of the reference-to-physical mapping at the point of interest. + */ +template +sofa::type::Vec elementNormal( + const sofa::type::Mat& jacobian) +{ + static_assert(TopologicalDimension + 1 == spatial_dimensions, + "A normal is only defined for an element of codimension 1."); + + if constexpr (spatial_dimensions == 3) + { + return jacobian.col(0).cross(jacobian.col(1)).normalized(); + } + else + { + const sofa::type::Vec<2, Real> tangent = jacobian.col(0); + return sofa::type::Vec<2, Real>(tangent[1], -tangent[0]).normalized(); + } +} + /** * @class BaseSourceTerm * @brief A source density whose value is determined by the geometry, not by the solution. diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ElementNormal.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ElementNormal.h deleted file mode 100644 index d2d6456050d..00000000000 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/ElementNormal.h +++ /dev/null @@ -1,56 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once - -#include -#include - -namespace sofa::component::solidmechanics::fem::elastic -{ - -/** - * @brief Unit normal of a codimension-1 element, from the jacobian of its mapping. - * - * Defined only where the element spans one dimension less than the space it lives in: a surface - * element in 3D, an edge in 2D. Its orientation follows the node ordering of the element. - * - * @param jacobian dx/dq of the reference-to-physical mapping at the point of interest. - */ -template -sofa::type::Vec elementNormal( - const sofa::type::Mat& jacobian) -{ - static_assert(TopologicalDimension + 1 == spatial_dimensions, - "A normal is only defined for an element of codimension 1."); - - if constexpr (spatial_dimensions == 3) - { - return jacobian.col(0).cross(jacobian.col(1)).normalized(); - } - else - { - const sofa::type::Vec<2, Real> tangent = jacobian.col(0); - return sofa::type::Vec<2, Real>(tangent[1], -tangent[0]).normalized(); - } -} - -} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.inl index 239a9e7c6b1..8a7603da0da 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.inl +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.inl @@ -21,7 +21,6 @@ ******************************************************************************/ #pragma once #include -#include namespace sofa::component::solidmechanics::fem::elastic { diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.inl index 7691cce89fd..dd7e8831d76 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.inl +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.inl @@ -21,7 +21,6 @@ ******************************************************************************/ #pragma once #include -#include namespace sofa::component::solidmechanics::fem::elastic { From 69561b870b7d7358c0ae8a5725a22e1a7ecb979f Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Thu, 10 Sep 2026 11:27:41 +0200 Subject: [PATCH 28/37] merge each nodal property into its source term --- .../SolidMechanics/FEM/Elastic/CMakeLists.txt | 6 -- .../fem/elastic/NodalPressure.cpp | 43 ----------- .../fem/elastic/NodalPressure.h | 61 ---------------- .../fem/elastic/NodalSourceDensity.cpp | 45 ------------ .../fem/elastic/NodalSourceDensity.h | 62 ---------------- .../fem/elastic/NodalStress.cpp | 43 ----------- .../solidmechanics/fem/elastic/NodalStress.h | 72 ------------------- .../fem/elastic/PressureSourceTerm.cpp | 11 +++ .../fem/elastic/PressureSourceTerm.h | 26 ++++++- .../fem/elastic/StressSourceTerm.cpp | 11 +++ .../fem/elastic/StressSourceTerm.h | 36 +++++++++- .../fem/elastic/VectorSourceTerm.cpp | 13 ++++ .../fem/elastic/VectorSourceTerm.h | 27 ++++++- 13 files changed, 121 insertions(+), 335 deletions(-) delete mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPressure.cpp delete mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPressure.h delete mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalSourceDensity.cpp delete mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalSourceDensity.h delete mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalStress.cpp delete mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalStress.h diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt index 855fe2d715d..33d159216ba 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt @@ -19,9 +19,6 @@ set(HEADER_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.inl - ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalPressure.h - ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalSourceDensity.h - ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalStress.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/PressureSourceTerm.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/PressureSourceTerm.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/QuadratureContext.h @@ -88,9 +85,6 @@ set(SOURCE_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.cpp - ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalPressure.cpp - ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalSourceDensity.cpp - ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/NodalStress.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/PressureSourceTerm.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/StressSourceTerm.cpp ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/VectorSourceTerm.cpp diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPressure.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPressure.cpp deleted file mode 100644 index c2251b6be5e..00000000000 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPressure.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#define SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_NODAL_PRESSURE_CPP - -#include - -#include -#include - -namespace sofa::component::solidmechanics::fem::elastic -{ - -void registerNodalPressure(sofa::core::ObjectFactory* factory) -{ - factory->registerObjects(sofa::core::ObjectRegistrationData("Definition of a nodal pressure (one scalar per dof).") - .add< NodalPressure >() - .add< NodalPressure >() - ); -} - -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalPressure; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalPressure; - -} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPressure.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPressure.h deleted file mode 100644 index adf237ee09b..00000000000 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalPressure.h +++ /dev/null @@ -1,61 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once - -#include -#include -#include - -#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_NODAL_PRESSURE_CPP) -#include -#endif - -namespace sofa::component::solidmechanics::fem::elastic -{ - -/** - * @class NodalPressure - * @brief A pressure prescribed at the nodes, one scalar per node. - * - * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). - */ -template -class NodalPressure : public sofa::core::BaseNodalProperty> -{ -public: - using DataTypes = TDataTypes; - using Real = sofa::Real_t; - - SOFA_CLASS(SOFA_TEMPLATE(NodalPressure, DataTypes), - SOFA_TEMPLATE(sofa::core::BaseNodalProperty, sofa::Real_t)); - -protected: - - NodalPressure() : sofa::core::BaseNodalProperty(Real{}) {} -}; - -#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_NODAL_PRESSURE_CPP) -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalPressure; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalPressure; -#endif - -} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalSourceDensity.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalSourceDensity.cpp deleted file mode 100644 index e50d5adfba0..00000000000 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalSourceDensity.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#define SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_NODAL_SOURCE_DENSITY_CPP - -#include - -#include -#include - -namespace sofa::component::solidmechanics::fem::elastic -{ - -void registerNodalSourceDensity(sofa::core::ObjectFactory* factory) -{ - factory->registerObjects(sofa::core::ObjectRegistrationData("Definition of a nodal source density (one vector per dof).") - .add< NodalSourceDensity >() - .add< NodalSourceDensity >() - .add< NodalSourceDensity >() - ); -} - -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalSourceDensity; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalSourceDensity; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalSourceDensity; - -} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalSourceDensity.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalSourceDensity.h deleted file mode 100644 index e068695950b..00000000000 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalSourceDensity.h +++ /dev/null @@ -1,62 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once - -#include -#include -#include - -#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_NODAL_SOURCE_DENSITY_CPP) -#include -#endif - -namespace sofa::component::solidmechanics::fem::elastic -{ - -/** - * @class NodalSourceDensity - * @brief A source density prescribed at the nodes, one vector per node. - * - * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). - */ -template -class NodalSourceDensity : public sofa::core::BaseNodalProperty> -{ -public: - using DataTypes = TDataTypes; - using Deriv = sofa::Deriv_t; - - SOFA_CLASS(SOFA_TEMPLATE(NodalSourceDensity, DataTypes), - SOFA_TEMPLATE(sofa::core::BaseNodalProperty, sofa::Deriv_t)); - -protected: - - NodalSourceDensity() : sofa::core::BaseNodalProperty(Deriv{}) {} -}; - -#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_NODAL_SOURCE_DENSITY_CPP) -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalSourceDensity; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalSourceDensity; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalSourceDensity; -#endif - -} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalStress.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalStress.cpp deleted file mode 100644 index 9df21bd28dc..00000000000 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalStress.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#define SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_NODAL_STRESS_CPP - -#include - -#include -#include - -namespace sofa::component::solidmechanics::fem::elastic -{ - -void registerNodalStress(sofa::core::ObjectFactory* factory) -{ - factory->registerObjects(sofa::core::ObjectRegistrationData("Definition of a nodal symmetric stress tensor (one tensor per dof).") - .add< NodalStress >() - .add< NodalStress >() - ); -} - -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalStress; -template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalStress; - -} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalStress.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalStress.h deleted file mode 100644 index 3884cfa2542..00000000000 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/NodalStress.h +++ /dev/null @@ -1,72 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once - -#include -#include -#include -#include -#include - -#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_NODAL_STRESS_CPP) -#include -#endif - -namespace sofa::component::solidmechanics::fem::elastic -{ - -/// The independent components of a symmetric stress tensor, in the storage order of MatSym. -template -using StressComponents = sofa::type::Vec< - sofa::type::NumberOfIndependentElements, - sofa::Real_t>; - -/** - * @class NodalStress - * @brief A symmetric stress tensor prescribed at the nodes, one tensor per node. - * - * A tensor is written as its independent components in the storage order of MatSym, which is not - * the standard Voigt one: xx xy yy xz yz zz in 3D, xx xy yy in 2D. - * - * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). - */ -template -class NodalStress : public sofa::core::BaseNodalProperty> -{ -public: - using DataTypes = TDataTypes; - using Components = StressComponents; - - SOFA_CLASS(SOFA_TEMPLATE(NodalStress, DataTypes), - SOFA_TEMPLATE(sofa::core::BaseNodalProperty, StressComponents)); - -protected: - - NodalStress() : sofa::core::BaseNodalProperty(Components{}) {} -}; - -#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_NODAL_STRESS_CPP) -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalStress; -extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalStress; -#endif - -} // namespace sofa::component::solidmechanics::fem::elastic diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.cpp index d7481005499..44fe9dc1c25 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.cpp @@ -30,6 +30,17 @@ namespace sofa::component::solidmechanics::fem::elastic { +void registerNodalPressure(sofa::core::ObjectFactory* factory) +{ + factory->registerObjects(sofa::core::ObjectRegistrationData("Definition of a nodal pressure (one scalar per dof).") + .add< NodalPressure >() + .add< NodalPressure >() + ); +} + +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalPressure; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalPressure; + void registerPressureSourceTerm(sofa::core::ObjectFactory* factory) { factory->registerObjects(sofa::core::ObjectRegistrationData("Traction obtained from a pressure prescribed at the nodes, acting along the normal of the element") diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.h index 7302e7145da..91dd01a26c1 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.h @@ -23,7 +23,7 @@ #include #include -#include +#include #include #if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_PRESSURE_SOURCE_TERM_CPP) @@ -34,6 +34,27 @@ namespace sofa::component::solidmechanics::fem::elastic { +/** + * @class NodalPressure + * @brief A pressure prescribed at the nodes, one scalar per node. + * + * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). + */ +template +class NodalPressure : public sofa::core::BaseNodalProperty> +{ +public: + using DataTypes = TDataTypes; + using Real = sofa::Real_t; + + SOFA_CLASS(SOFA_TEMPLATE(NodalPressure, DataTypes), + SOFA_TEMPLATE(sofa::core::BaseNodalProperty, sofa::Real_t)); + +protected: + + NodalPressure() : sofa::core::BaseNodalProperty(Real{}) {} +}; + /** * @class PressureSourceTerm * @brief A traction \f$ p \, n \f$ built from a pressure prescribed at the nodes. @@ -83,6 +104,9 @@ class PressureSourceTerm : public BaseSourceTerm }; #if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_PRESSURE_SOURCE_TERM_CPP) +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalPressure; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalPressure; + extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API PressureSourceTerm; extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API PressureSourceTerm; extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API PressureSourceTerm; diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.cpp index 02b205ba1e7..f8d63ab23a9 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.cpp @@ -30,6 +30,17 @@ namespace sofa::component::solidmechanics::fem::elastic { +void registerNodalStress(sofa::core::ObjectFactory* factory) +{ + factory->registerObjects(sofa::core::ObjectRegistrationData("Definition of a nodal symmetric stress tensor (one tensor per dof).") + .add< NodalStress >() + .add< NodalStress >() + ); +} + +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalStress; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalStress; + void registerStressSourceTerm(sofa::core::ObjectFactory* factory) { factory->registerObjects(sofa::core::ObjectRegistrationData("Traction obtained from a symmetric stress tensor prescribed at the nodes, contracted with the normal of the element") diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.h index d062298009c..15b117bae81 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.h @@ -23,8 +23,9 @@ #include #include -#include +#include #include +#include #if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_STRESS_SOURCE_TERM_CPP) #include @@ -34,6 +35,36 @@ namespace sofa::component::solidmechanics::fem::elastic { +/// The independent components of a symmetric stress tensor, in the storage order of MatSym. +template +using StressComponents = sofa::type::Vec< + sofa::type::NumberOfIndependentElements, + sofa::Real_t>; + +/** + * @class NodalStress + * @brief A symmetric stress tensor prescribed at the nodes, one tensor per node. + * + * A tensor is written as its independent components in the storage order of MatSym, which is not + * the standard Voigt one: xx xy yy xz yz zz in 3D, xx xy yy in 2D. + * + * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). + */ +template +class NodalStress : public sofa::core::BaseNodalProperty> +{ +public: + using DataTypes = TDataTypes; + using Components = StressComponents; + + SOFA_CLASS(SOFA_TEMPLATE(NodalStress, DataTypes), + SOFA_TEMPLATE(sofa::core::BaseNodalProperty, StressComponents)); + +protected: + + NodalStress() : sofa::core::BaseNodalProperty(Components{}) {} +}; + /** * @class StressSourceTerm * @brief A traction \f$ \sigma \, n \f$ built from a stress tensor prescribed at the nodes. @@ -89,6 +120,9 @@ class StressSourceTerm : public BaseSourceTerm }; #if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_STRESS_SOURCE_TERM_CPP) +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalStress; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalStress; + extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API StressSourceTerm; extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API StressSourceTerm; extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API StressSourceTerm; diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.cpp index 61c9507d0c0..3c6aa474337 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.cpp +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.cpp @@ -30,6 +30,19 @@ namespace sofa::component::solidmechanics::fem::elastic { +void registerNodalSourceDensity(sofa::core::ObjectFactory* factory) +{ + factory->registerObjects(sofa::core::ObjectRegistrationData("Definition of a nodal source density (one vector per dof).") + .add< NodalSourceDensity >() + .add< NodalSourceDensity >() + .add< NodalSourceDensity >() + ); +} + +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalSourceDensity; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalSourceDensity; +template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalSourceDensity; + void registerVectorSourceTerm(sofa::core::ObjectFactory* factory) { factory->registerObjects(sofa::core::ObjectRegistrationData("Source density given as a vector at each node, per unit measure of the element") diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.h index 63ac51b5655..8029847dc9e 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.h @@ -23,7 +23,7 @@ #include #include -#include +#include #include #if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_VECTOR_SOURCE_TERM_CPP) @@ -34,6 +34,27 @@ namespace sofa::component::solidmechanics::fem::elastic { +/** + * @class NodalSourceDensity + * @brief A source density prescribed at the nodes, one vector per node. + * + * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). + */ +template +class NodalSourceDensity : public sofa::core::BaseNodalProperty> +{ +public: + using DataTypes = TDataTypes; + using Deriv = sofa::Deriv_t; + + SOFA_CLASS(SOFA_TEMPLATE(NodalSourceDensity, DataTypes), + SOFA_TEMPLATE(sofa::core::BaseNodalProperty, sofa::Deriv_t)); + +protected: + + NodalSourceDensity() : sofa::core::BaseNodalProperty(Deriv{}) {} +}; + /** * @class VectorSourceTerm * @brief A source density given directly as a vector at each node. @@ -82,6 +103,10 @@ class VectorSourceTerm : public BaseSourceTerm }; #if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_VECTOR_SOURCE_TERM_CPP) +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalSourceDensity; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalSourceDensity; +extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API NodalSourceDensity; + extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API VectorSourceTerm; extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API VectorSourceTerm; extern template class SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_API VectorSourceTerm; From 5ddea022e945d1f8ce11f7c8b18634c005cacdd9 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Thu, 10 Sep 2026 12:30:09 +0200 Subject: [PATCH 29/37] Correct comment --- .../sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h index 453822afd7d..5cf10e4a260 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h @@ -42,7 +42,7 @@ namespace sofa::component::solidmechanics::fem::elastic { /** - * @brief Unit normal of a codimension-1 element, from the jacobian of its mapping. + * @brief Unit normal of an element, from the jacobian of its mapping. * * Defined only where the element spans one dimension less than the space it lives in: a surface * element in 3D, an edge in 2D. Its orientation follows the node ordering of the element. From 8b60e5a2f885934a9709a9085c816e2d2158ace0 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Thu, 10 Sep 2026 12:31:38 +0200 Subject: [PATCH 30/37] store the nodal stress as a MatSym --- .../fem/elastic/StressSourceTerm.h | 24 +++++++------------ .../fem/elastic/StressSourceTerm.inl | 10 +------- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.h index 15b117bae81..dffc29f7ce2 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.h @@ -35,34 +35,33 @@ namespace sofa::component::solidmechanics::fem::elastic { -/// The independent components of a symmetric stress tensor, in the storage order of MatSym. +/// The symmetric stress tensor prescribed at one node. template -using StressComponents = sofa::type::Vec< - sofa::type::NumberOfIndependentElements, - sofa::Real_t>; +using StressTensor = + sofa::type::MatSym>; /** * @class NodalStress * @brief A symmetric stress tensor prescribed at the nodes, one tensor per node. * - * A tensor is written as its independent components in the storage order of MatSym, which is not - * the standard Voigt one: xx xy yy xz yz zz in 3D, xx xy yy in 2D. + * A tensor is read from a scene as its independent components in the storage order of MatSym, + * which is not the standard Voigt one: xx xy yy xz yz zz in 3D, xx xy yy in 2D. * * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). */ template -class NodalStress : public sofa::core::BaseNodalProperty> +class NodalStress : public sofa::core::BaseNodalProperty> { public: using DataTypes = TDataTypes; - using Components = StressComponents; + using Tensor = StressTensor; SOFA_CLASS(SOFA_TEMPLATE(NodalStress, DataTypes), - SOFA_TEMPLATE(sofa::core::BaseNodalProperty, StressComponents)); + SOFA_TEMPLATE(sofa::core::BaseNodalProperty, StressTensor)); protected: - NodalStress() : sofa::core::BaseNodalProperty(Components{}) {} + NodalStress() : sofa::core::BaseNodalProperty(Tensor{}) {} }; /** @@ -90,14 +89,9 @@ class StressSourceTerm : public BaseSourceTerm SOFA_TEMPLATE2(BaseSourceTerm, DataTypes, ElementType)); using Deriv = sofa::Deriv_t; - using Real = sofa::Real_t; using QuadratureContext = QuadratureContext; using NodalStress = ::sofa::component::solidmechanics::fem::elastic::NodalStress; - static constexpr sofa::Size spatial_dimensions = DataTypes::spatial_dimensions; - - using StressTensor = sofa::type::MatSym; - /** * @brief Nodal values of the stress tensor this term integrates. */ diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.inl index dd7e8831d76..6215eea1b62 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.inl +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.inl @@ -61,15 +61,7 @@ sofa::Deriv_t StressSourceTerm::evaluate( return Deriv{}; } - const auto components = this->interpolateProperty(*l_stress, context); - - StressTensor stress; - for (sofa::Size i = 0; i < StressTensor::NumberStoredValues; ++i) - { - stress[i] = components[i]; - } - - return stress * elementNormal(context.jacobian); + return this->interpolateProperty(*l_stress, context) * elementNormal(context.jacobian); } } // namespace sofa::component::solidmechanics::fem::elastic From 359125cda64126f41c19deebfc0ceeb82ee3fa14 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Thu, 10 Sep 2026 12:38:34 +0200 Subject: [PATCH 31/37] add scenes comparing VectorSourceTerm to the dead-load pressure fields --- .../SolidMechanics/FEM/VectorSourceTerm.scn | 326 +++++++++++++++--- 1 file changed, 272 insertions(+), 54 deletions(-) diff --git a/examples/Component/SolidMechanics/FEM/VectorSourceTerm.scn b/examples/Component/SolidMechanics/FEM/VectorSourceTerm.scn index a2bb497f3de..daddebd6e4d 100644 --- a/examples/Component/SolidMechanics/FEM/VectorSourceTerm.scn +++ b/examples/Component/SolidMechanics/FEM/VectorSourceTerm.scn @@ -1,74 +1,292 @@ - + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - + + - - - + + + + - - - - + + + - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + - - - - + + + - + + - - - - + - - - + + - + + + + + + + From 1565b1fce256c50fce99c7c70cf924935cb139e5 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Thu, 10 Sep 2026 16:56:41 +0200 Subject: [PATCH 32/37] Rename scene demonstrating quadrature rules --- .../FEM/{FEMSourceTermIntegrator.scn => QuadratureRules.scn} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/Component/SolidMechanics/FEM/{FEMSourceTermIntegrator.scn => QuadratureRules.scn} (100%) diff --git a/examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn b/examples/Component/SolidMechanics/FEM/QuadratureRules.scn similarity index 100% rename from examples/Component/SolidMechanics/FEM/FEMSourceTermIntegrator.scn rename to examples/Component/SolidMechanics/FEM/QuadratureRules.scn From e3fa51131612488bf33660b9a1171da2615cfedb Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Thu, 10 Sep 2026 17:22:35 +0200 Subject: [PATCH 33/37] Move QuadratureContext in BaseSourceTerm.h --- .../SolidMechanics/FEM/Elastic/CMakeLists.txt | 1 - .../fem/elastic/BaseSourceTerm.h | 55 +++++++++++- .../fem/elastic/QuadratureContext.h | 85 ------------------- 3 files changed, 54 insertions(+), 87 deletions(-) delete mode 100644 Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadratureContext.h diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt index 33d159216ba..f086bbf1c90 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt @@ -21,7 +21,6 @@ set(HEADER_FILES ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/PressureSourceTerm.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/PressureSourceTerm.inl - ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/QuadratureContext.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/StressSourceTerm.h ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/StressSourceTerm.inl ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/VectorSourceTerm.h diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h index 5cf10e4a260..34777d97672 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h @@ -22,10 +22,10 @@ #pragma once #include -#include #include #include #include +#include #include #include #include @@ -41,6 +41,59 @@ namespace sofa::component::solidmechanics::fem::elastic { +/** + * @struct QuadratureContext + * @brief Everything the integrator knows at one quadrature point. + * + * Built once per quadrature point and handed to every integrated term. + * A source term reads from it and returns an integrand. + * + * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). + * @tparam TElementType The type of finite element (e.g., sofa::geometry::Tetrahedron). + */ +template +struct QuadratureContext +{ + using DataTypes = TDataTypes; + using ElementType = TElementType; + using FiniteElement = sofa::fem::FiniteElement; + + using Real = sofa::Real_t; + using Coord = sofa::Coord_t; + using Deriv = sofa::Deriv_t; + + static constexpr sofa::Size NumberOfNodesInElement = ElementType::NumberOfNodes; + static constexpr sofa::Size spatial_dimensions = DataTypes::spatial_dimensions; + static constexpr sofa::Size TopologicalDimension = FiniteElement::TopologicalDimension; + + using Element = typename FiniteElement::TopologyElement; + using ShapeFunctions = sofa::type::Vec; + using GradientShapeFunctions = sofa::type::Mat; + using Jacobian = sofa::type::Mat; + + /// Node indices of the element being integrated, with which a term gathers its own nodal + /// degrees of freedom. + const Element& element; + + /// Shape function value at this quadrature point. + ShapeFunctions N; + + /// Reference-space gradients of the shape functions at this quadrature point. + GradientShapeFunctions gradientShapeFunctions; + + /// dx/dq of the reference-to-physical mapping, on the configuration the integrator chose. + Jacobian jacobian; + + /// \f$ |\det J| \f$, for information only: the integrator applies it, a term must not. + Real measure; + + /// Interpolated rest position at this quadrature point. + Coord restPosition; + + /// Interpolated displacement at this quadrature point. + Deriv displacement; +}; + /** * @brief Unit normal of an element, from the jacobian of its mapping. * diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadratureContext.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadratureContext.h deleted file mode 100644 index cd14621076f..00000000000 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/QuadratureContext.h +++ /dev/null @@ -1,85 +0,0 @@ -/****************************************************************************** -* SOFA, Simulation Open-Framework Architecture * -* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * -* * -* This program is free software; you can redistribute it and/or modify it * -* under the terms of the GNU Lesser General Public License as published by * -* the Free Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * -* for more details. * -* * -* You should have received a copy of the GNU Lesser General Public License * -* along with this program. If not, see . * -******************************************************************************* -* Authors: The SOFA Team and external contributors (see Authors.txt) * -* * -* Contact information: contact@sofa-framework.org * -******************************************************************************/ -#pragma once - -#include -#include -#include -#include - -namespace sofa::component::solidmechanics::fem::elastic -{ - -/** - * @struct QuadratureContext - * @brief Everything the integrator knows at one quadrature point. - * - * Built once per quadrature point and handed to every integrated term. - * A source term reads from it and returns an integrand. - * - * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). - * @tparam TElementType The type of finite element (e.g., sofa::geometry::Tetrahedron). - */ -template -struct QuadratureContext -{ - using DataTypes = TDataTypes; - using ElementType = TElementType; - using FiniteElement = sofa::fem::FiniteElement; - - using Real = sofa::Real_t; - using Coord = sofa::Coord_t; - using Deriv = sofa::Deriv_t; - - static constexpr sofa::Size NumberOfNodesInElement = ElementType::NumberOfNodes; - static constexpr sofa::Size spatial_dimensions = DataTypes::spatial_dimensions; - static constexpr sofa::Size TopologicalDimension = FiniteElement::TopologicalDimension; - - using Element = typename FiniteElement::TopologyElement; - using ShapeFunctions = sofa::type::Vec; - using GradientShapeFunctions = sofa::type::Mat; - using Jacobian = sofa::type::Mat; - - /// Node indices of the element being integrated, with which a term gathers its own nodal - /// degrees of freedom. - const Element& element; - - /// Shape function value at this quadrature point. - ShapeFunctions N; - - /// Reference-space gradients of the shape functions at this quadrature point. - GradientShapeFunctions gradientShapeFunctions; - - /// dx/dq of the reference-to-physical mapping, on the configuration the integrator chose. - Jacobian jacobian; - - /// \f$ |\det J| \f$, for information only: the integrator applies it, a term must not. - Real measure; - - /// Interpolated rest position at this quadrature point. - Coord restPosition; - - /// Interpolated displacement at this quadrature point. - Deriv displacement; -}; - -} // namespace sofa::component::solidmechanics::fem::elastic From 814b615f873779c7e7c6439f669bef053ed8a79c Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Thu, 10 Sep 2026 19:05:22 +0200 Subject: [PATCH 34/37] Adjust text in doc --- .../component/solidmechanics/fem/elastic/StressSourceTerm.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.h index dffc29f7ce2..d7772aa713a 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.h @@ -44,8 +44,8 @@ using StressTensor = * @class NodalStress * @brief A symmetric stress tensor prescribed at the nodes, one tensor per node. * - * A tensor is read from a scene as its independent components in the storage order of MatSym, - * which is not the standard Voigt one: xx xy yy xz yz zz in 3D, xx xy yy in 2D. + * A tensor is read from a scene as a full matrix, row by row: nine components in 3D, four in 2D, + * of which MatSym keeps the independent ones. * * @tparam TDataTypes The data types used for positions, velocities, etc. (e.g., Vec3Types). */ From 124b2289dca5365feffdd569aff5348b591de25f Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Thu, 10 Sep 2026 19:20:19 +0200 Subject: [PATCH 35/37] Add scene for stress source term --- .../SolidMechanics/FEM/StressSourceTerm.scn | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 examples/Component/SolidMechanics/FEM/StressSourceTerm.scn diff --git a/examples/Component/SolidMechanics/FEM/StressSourceTerm.scn b/examples/Component/SolidMechanics/FEM/StressSourceTerm.scn new file mode 100644 index 00000000000..138e29f88f0 --- /dev/null +++ b/examples/Component/SolidMechanics/FEM/StressSourceTerm.scn @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 3c2db4da6c9fd8e5113a1c450950c80d291c6efb Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Mon, 14 Sep 2026 15:14:30 +0200 Subject: [PATCH 36/37] Remove scene with quadrature rules; better demonstrated in SofaPython3 --- .../SolidMechanics/FEM/QuadratureRules.scn | 114 ------------------ 1 file changed, 114 deletions(-) delete mode 100644 examples/Component/SolidMechanics/FEM/QuadratureRules.scn diff --git a/examples/Component/SolidMechanics/FEM/QuadratureRules.scn b/examples/Component/SolidMechanics/FEM/QuadratureRules.scn deleted file mode 100644 index ee34aa4d3de..00000000000 --- a/examples/Component/SolidMechanics/FEM/QuadratureRules.scn +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From e25ab65c5a467b9290beb4de487278fa6b747266 Mon Sep 17 00:00:00 2001 From: Themis Skamagkis Date: Mon, 14 Sep 2026 15:56:58 +0200 Subject: [PATCH 37/37] Resolve name conflict --- .../component/solidmechanics/fem/elastic/BaseSourceTerm.h | 8 ++++---- .../solidmechanics/fem/elastic/PressureSourceTerm.h | 4 ++-- .../solidmechanics/fem/elastic/PressureSourceTerm.inl | 2 +- .../solidmechanics/fem/elastic/StressSourceTerm.h | 4 ++-- .../solidmechanics/fem/elastic/StressSourceTerm.inl | 2 +- .../solidmechanics/fem/elastic/VectorSourceTerm.h | 4 ++-- .../solidmechanics/fem/elastic/VectorSourceTerm.inl | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h index 34777d97672..6e3c6ed0cdc 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h @@ -140,14 +140,14 @@ class BaseSourceTerm : public sofa::core::objectmodel::BaseComponent sofa::core::objectmodel::BaseComponent); using Deriv = sofa::Deriv_t; - using QuadratureContext = QuadratureContext; + using QuadratureContext_t = QuadratureContext; /** * @brief Source density at one quadrature point, per unit physical measure. * * @param context Geometry of the quadrature point. */ - virtual Deriv evaluate(const QuadratureContext& context) const = 0; + virtual Deriv evaluate(const QuadratureContext_t& context) const = 0; protected: @@ -165,7 +165,7 @@ class BaseSourceTerm : public sofa::core::objectmodel::BaseComponent template static PropertyType interpolateProperty( const sofa::core::BaseNodalProperty& property, - const QuadratureContext& context) + const QuadratureContext_t& context) { static constexpr sofa::Size NumberOfNodesInElement = ElementType::NumberOfNodes; @@ -178,7 +178,7 @@ class BaseSourceTerm : public sofa::core::objectmodel::BaseComponent elementNodesProperty[i] = property.getNodeProperty(context.element[i], propertyAccessor); } - return QuadratureContext::FiniteElement::Helper::evaluateValueInElement( + return QuadratureContext_t::FiniteElement::Helper::evaluateValueInElement( elementNodesProperty, context.N); } }; diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.h index 91dd01a26c1..ddae6978a7b 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.h @@ -79,7 +79,7 @@ class PressureSourceTerm : public BaseSourceTerm SOFA_TEMPLATE2(BaseSourceTerm, DataTypes, ElementType)); using Deriv = sofa::Deriv_t; - using QuadratureContext = QuadratureContext; + using QuadratureContext_t = QuadratureContext; using NodalPressure = ::sofa::component::solidmechanics::fem::elastic::NodalPressure; /** @@ -96,7 +96,7 @@ class PressureSourceTerm : public BaseSourceTerm /** * @brief The linked pressure interpolated at the quadrature point, times the unit normal. */ - Deriv evaluate(const QuadratureContext& context) const override; + Deriv evaluate(const QuadratureContext_t& context) const override; protected: diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.inl index 8a7603da0da..f04fb48a320 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.inl +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.inl @@ -54,7 +54,7 @@ void PressureSourceTerm::init() template sofa::Deriv_t PressureSourceTerm::evaluate( - const QuadratureContext& context) const + const QuadratureContext_t& context) const { if (!l_pressure) { diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.h index d7772aa713a..d967ea2c92f 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.h @@ -89,7 +89,7 @@ class StressSourceTerm : public BaseSourceTerm SOFA_TEMPLATE2(BaseSourceTerm, DataTypes, ElementType)); using Deriv = sofa::Deriv_t; - using QuadratureContext = QuadratureContext; + using QuadratureContext_t = QuadratureContext; using NodalStress = ::sofa::component::solidmechanics::fem::elastic::NodalStress; /** @@ -106,7 +106,7 @@ class StressSourceTerm : public BaseSourceTerm /** * @brief The linked stress interpolated at the quadrature point, contracted with the normal. */ - Deriv evaluate(const QuadratureContext& context) const override; + Deriv evaluate(const QuadratureContext_t& context) const override; protected: diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.inl index 6215eea1b62..5fbb13d465c 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.inl +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.inl @@ -54,7 +54,7 @@ void StressSourceTerm::init() template sofa::Deriv_t StressSourceTerm::evaluate( - const QuadratureContext& context) const + const QuadratureContext_t& context) const { if (!l_stress) { diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.h index 8029847dc9e..7fb83bcf25a 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.h +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.h @@ -77,7 +77,7 @@ class VectorSourceTerm : public BaseSourceTerm SOFA_TEMPLATE2(BaseSourceTerm, DataTypes, ElementType)); using Deriv = sofa::Deriv_t; - using QuadratureContext = QuadratureContext; + using QuadratureContext_t = QuadratureContext; using NodalSourceDensity = ::sofa::component::solidmechanics::fem::elastic::NodalSourceDensity; @@ -95,7 +95,7 @@ class VectorSourceTerm : public BaseSourceTerm /** * @brief The linked source density interpolated at the quadrature point. */ - Deriv evaluate(const QuadratureContext& context) const override; + Deriv evaluate(const QuadratureContext_t& context) const override; protected: diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.inl index 51db6195968..3acbcec062e 100644 --- a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.inl +++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.inl @@ -54,7 +54,7 @@ void VectorSourceTerm::init() template sofa::Deriv_t VectorSourceTerm::evaluate( - const QuadratureContext& context) const + const QuadratureContext_t& context) const { if (!l_sourceDensity) {