diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt
index 85447e27cdc..f086bbf1c90 100644
--- a/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt
+++ b/Sofa/Component/SolidMechanics/FEM/Elastic/CMakeLists.txt
@@ -11,11 +11,20 @@ set(HEADER_FILES
${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseElementLinearFEMForceField.inl
${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
${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.h
${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/CorotationalFEMForceField.inl
+ ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.h
+ ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FEMSourceTermIntegrator.inl
+ ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/PressureSourceTerm.h
+ ${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/PressureSourceTerm.inl
+ ${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
${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.inl
${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/FastTetrahedralCorotationalForceField.h
@@ -70,9 +79,14 @@ set(SOURCE_FILES
${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/init.cpp
${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/BaseElementLinearFEMForceField.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
${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/LinearSmallStrainFEMForceField.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
${SOFACOMPONENTSOLIDMECHANICSFEMELASTIC_SOURCE_DIR}/HexahedralFEMForceField.cpp
diff --git a/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.cpp
new file mode 100644
index 00000000000..b6cc5a93170
--- /dev/null
+++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.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_SOURCE_TERM_CPP
+
+#include
+
+#include
+#include
+
+namespace sofa::component::solidmechanics::fem::elastic
+{
+
+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/BaseSourceTerm.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h
new file mode 100644
index 00000000000..6e3c6ed0cdc
--- /dev/null
+++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/BaseSourceTerm.h
@@ -0,0 +1,198 @@
+/******************************************************************************
+* 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
+#include
+#include
+#include
+#include
+
+#include
+
+#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_BASE_SOURCE_TERM_CPP)
+#include
+#include
+#endif
+
+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.
+ *
+ * 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.
+ *
+ * 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 BaseSourceTerm : public sofa::core::objectmodel::BaseComponent
+{
+public:
+ using DataTypes = TDataTypes;
+ using ElementType = TElementType;
+
+ SOFA_CLASS(SOFA_TEMPLATE2(BaseSourceTerm, DataTypes, ElementType),
+ sofa::core::objectmodel::BaseComponent);
+
+ using Deriv = sofa::Deriv_t;
+ 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_t& context) const = 0;
+
+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_t& 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_t::FiniteElement::Helper::evaluateValueInElement(
+ elementNodesProperty, context.N);
+ }
+};
+
+#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.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.cpp
new file mode 100644
index 00000000000..f7a908ed703
--- /dev/null
+++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.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_INTEGRATOR_CPP
+
+#include
+
+#include
+#include
+#include
+
+namespace sofa::component::solidmechanics::fem::elastic
+{
+
+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 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 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/FEMSourceTermIntegrator.h b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h
new file mode 100644
index 00000000000..c401aa202c4
--- /dev/null
+++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.h
@@ -0,0 +1,166 @@
+/******************************************************************************
+* 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
+#include
+
+#if !defined(SOFA_COMPONENT_SOLIDMECHANICS_FEM_ELASTIC_FEM_SOURCE_TERM_INTEGRATOR_CPP)
+#include
+#endif
+
+namespace sofa::component::solidmechanics::fem::elastic
+{
+
+/**
+ * @class FEMSourceTermIntegrator
+ * @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 BaseSourceTerm (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).
+ */
+template
+class FEMSourceTermIntegrator :
+ public sofa::core::behavior::ForceField,
+ public virtual sofa::core::behavior::TopologyAccessor
+{
+public:
+ using DataTypes = TDataTypes;
+ using ElementType = TElementType;
+ 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;
+
+public:
+
+ /**
+ * @brief Source terms integrated by this component.
+ *
+ * If left empty, the BaseSourceTerm components found in the current context are used.
+ */
+ sofa::MultiLink, BaseSourceTerm,
+ 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. Validates the linked source terms.
+ * 4. Integrates the source terms into the nodal force.
+ */
+ void init() override;
+
+ /**
+ * @brief Adds the nodal source term to the RHS vector.
+ *
+ * @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.
+ * @param v The current velocities.
+ */
+ void addForce(
+ const sofa::core::MechanicalParams* mparams,
+ sofa::DataVecDeriv_t& f,
+ const sofa::DataVecCoord_t& x,
+ const sofa::DataVecDeriv_t& v) override;
+
+ /**
+ * @brief No-op.
+ */
+ void addDForce(const sofa::core::MechanicalParams* mparams,
+ sofa::DataVecDeriv_t& df,
+ const sofa::DataVecDeriv_t& dx) override;
+
+ /**
+ * @brief No-op.
+ */
+ void buildStiffnessMatrix(sofa::core::behavior::StiffnessMatrix* matrix) override;
+
+ using sofa::core::behavior::ForceField::getPotentialEnergy;
+ /**
+ * @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 source terms.
+ */
+ sofa::Data d_quadratureDegree;
+
+protected:
+
+ /**
+ * @brief Default constructor.
+ */
+ FEMSourceTermIntegrator();
+
+ /**
+ * @brief Ensures that valid source terms are linked, falling back to the current context.
+ */
+ void validateSources();
+
+ /**
+ * @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.
+ */
+ void assembleConstantForce();
+
+ /**
+ * @brief Nodal load of every term in l_constantSources.
+ */
+ sofa::VecDeriv_t m_constantForce;
+};
+
+#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/FEMSourceTermIntegrator.inl b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl
new file mode 100644
index 00000000000..1bc2ba9d2df
--- /dev/null
+++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/FEMSourceTermIntegrator.inl
@@ -0,0 +1,212 @@
+/******************************************************************************
+* 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
+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 source terms."))
+{
+ this->addUpdateCallback("reassembleConstantForce", {&d_quadratureDegree},
+ [this](const sofa::core::DataTracker&)
+ {
+ if (!this->isComponentStateInvalid() && this->l_topology && this->mstate)
+ {
+ assembleConstantForce();
+ }
+
+ return this->getComponentState();
+ }, {});
+}
+
+template
+void FEMSourceTermIntegrator::init()
+{
+ sofa::core::behavior::ForceField::init();
+
+ if (!this->isComponentStateInvalid())
+ {
+ sofa::core::behavior::TopologyAccessor::init();
+ }
+
+ if (!this->isComponentStateInvalid())
+ {
+ this->validateSources();
+ }
+
+ if (!this->isComponentStateInvalid() && this->l_topology && this->mstate)
+ {
+ this->assembleConstantForce();
+ }
+
+ if (!this->isComponentStateInvalid())
+ {
+ this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid);
+ }
+}
+
+template
+void FEMSourceTermIntegrator::validateSources()
+{
+ // Gather all BaseSourceTerm components in Context if empty
+ if (l_constantSources.empty())
+ {
+ 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 FEMSourceTermIntegrator::assembleConstantForce()
+{
+ m_constantForce.assign(this->mstate->getSize(), sofa::Deriv_t{});
+
+ const auto restPositionsAccessor = this->mstate->readRestPositions();
+ const auto positionsAccessor = this->mstate->readPositions();
+
+ const auto& elements = FiniteElement::getElementSequence(*this->l_topology);
+ const auto quadratureRule = FiniteElement::quadratureRule(d_quadratureDegree.getValue());
+
+ for (const auto& element : elements)
+ {
+ 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];
+ }
+
+ for (const auto& [quadraturePoint, weight] : quadratureRule)
+ {
+ 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 measure = static_cast(sofa::type::absGeneralizedDeterminant(jacobian));
+
+ const auto restPosition =
+ FiniteElement::Helper::evaluateValueInElement(elementNodesRestCoordinates, N);
+ const auto displacement =
+ FiniteElement::Helper::evaluateValueInElement(elementNodesDisplacement, N);
+
+ const QuadratureContext context{
+ element, N, dN_dq_ref, jacobian, measure, restPosition, displacement};
+
+ const auto weightTimesMeasure = static_cast(weight) * measure;
+
+ for (const auto& source : l_constantSources)
+ {
+ const auto density = source->evaluate(context);
+
+ for (sofa::Size i = 0; i < NumberOfNodesInElement; ++i)
+ {
+ m_constantForce[element[i]] += density * (weightTimesMeasure * N[i]);
+ }
+ }
+ }
+ }
+}
+
+template
+void FEMSourceTermIntegrator::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);
+
+ if (this->isComponentStateInvalid())
+ {
+ return;
+ }
+
+ auto forceAccessor = sofa::helper::getWriteAccessor(f);
+
+ for (sofa::Index i = 0; i < m_constantForce.size(); ++i)
+ {
+ forceAccessor[i] += m_constantForce[i];
+ }
+}
+
+template
+void FEMSourceTermIntegrator::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 FEMSourceTermIntegrator::buildStiffnessMatrix(sofa::core::behavior::StiffnessMatrix* matrix)
+{
+ SOFA_UNUSED(matrix);
+}
+
+template
+SReal FEMSourceTermIntegrator::getPotentialEnergy(const sofa::core::MechanicalParams* mparams,
+ const sofa::DataVecCoord_t& x) const
+{
+ SOFA_UNUSED(mparams);
+
+ 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/PressureSourceTerm.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.cpp
new file mode 100644
index 00000000000..44fe9dc1c25
--- /dev/null
+++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.cpp
@@ -0,0 +1,57 @@
+/******************************************************************************
+* 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 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")
+ .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..ddae6978a7b
--- /dev/null
+++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.h
@@ -0,0 +1,115 @@
+/******************************************************************************
+* 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 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.
+ *
+ * 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_t = 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_t& 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 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;
+#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..f04fb48a320
--- /dev/null
+++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/PressureSourceTerm.inl
@@ -0,0 +1,67 @@
+/******************************************************************************
+* 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
+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_t& context) const
+{
+ if (!l_pressure)
+ {
+ return Deriv{};
+ }
+
+ 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.cpp b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.cpp
new file mode 100644
index 00000000000..f8d63ab23a9
--- /dev/null
+++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.cpp
@@ -0,0 +1,57 @@
+/******************************************************************************
+* 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 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")
+ .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..d967ea2c92f
--- /dev/null
+++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.h
@@ -0,0 +1,125 @@
+/******************************************************************************
+* 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_STRESS_SOURCE_TERM_CPP)
+#include
+#include
+#endif
+
+namespace sofa::component::solidmechanics::fem::elastic
+{
+
+/// The symmetric stress tensor prescribed at one node.
+template
+using StressTensor =
+ sofa::type::MatSym>;
+
+/**
+ * @class NodalStress
+ * @brief A symmetric stress tensor prescribed at the nodes, one tensor per node.
+ *
+ * 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).
+ */
+template
+class NodalStress : public sofa::core::BaseNodalProperty>
+{
+public:
+ using DataTypes = TDataTypes;
+ using Tensor = StressTensor;
+
+ SOFA_CLASS(SOFA_TEMPLATE(NodalStress, DataTypes),
+ SOFA_TEMPLATE(sofa::core::BaseNodalProperty, StressTensor));
+
+protected:
+
+ NodalStress() : sofa::core::BaseNodalProperty(Tensor{}) {}
+};
+
+/**
+ * @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 QuadratureContext_t = QuadratureContext;
+ using NodalStress = ::sofa::component::solidmechanics::fem::elastic::NodalStress;
+
+ /**
+ * @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_t& 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 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;
+#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..5fbb13d465c
--- /dev/null
+++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/StressSourceTerm.inl
@@ -0,0 +1,67 @@
+/******************************************************************************
+* 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
+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_t& context) const
+{
+ if (!l_stress)
+ {
+ return Deriv{};
+ }
+
+ return this->interpolateProperty(*l_stress, context) * elementNormal(context.jacobian);
+}
+
+} // 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..3c6aa474337
--- /dev/null
+++ b/Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/VectorSourceTerm.cpp
@@ -0,0 +1,71 @@
+/******************************************************************************
+* 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 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")
+ .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