diff --git a/Sofa/Component/Mass/CMakeLists.txt b/Sofa/Component/Mass/CMakeLists.txt
index c05cffd0c00..a6dc70ba3e5 100644
--- a/Sofa/Component/Mass/CMakeLists.txt
+++ b/Sofa/Component/Mass/CMakeLists.txt
@@ -40,6 +40,10 @@ sofa_create_package_with_targets(
INCLUDE_INSTALL_DIR "${PROJECT_NAME}"
)
+if(SOFA_BUILD_TESTS)
+ add_subdirectory(Testing)
+endif()
+
# Tests
# If SOFA_BUILD_TESTS exists and is OFF, then these tests will be auto-disabled
cmake_dependent_option(SOFA_COMPONENT_MASS_BUILD_TESTS "Compile the automatic tests" ON "SOFA_BUILD_TESTS OR NOT DEFINED SOFA_BUILD_TESTS" OFF)
diff --git a/Sofa/Component/Mass/Testing/CMakeLists.txt b/Sofa/Component/Mass/Testing/CMakeLists.txt
new file mode 100644
index 00000000000..1f3e9ffc4ed
--- /dev/null
+++ b/Sofa/Component/Mass/Testing/CMakeLists.txt
@@ -0,0 +1,18 @@
+cmake_minimum_required(VERSION 3.22)
+
+project(Sofa.Component.Mass.Testing)
+
+set(SOFACOMPONENTMAPPINGTESTING_SRC "src/sofa/component/mass/testing")
+
+set(HEADER_FILES
+ ${SOFACOMPONENTMAPPINGTESTING_SRC}/MassTestCreation.h
+)
+
+set(SOURCE_FILES
+ ${SOFACOMPONENTMAPPINGTESTING_SRC}/empty.cpp
+)
+
+add_library(${PROJECT_NAME} SHARED ${HEADER_FILES} ${SOURCE_FILES})
+target_include_directories(${PROJECT_NAME} PUBLIC src/)
+
+target_link_libraries(${PROJECT_NAME} Sofa.Config Sofa.Simulation.Core)
diff --git a/Sofa/Component/Mass/Testing/src/sofa/component/mass/testing/MassTestCreation.h b/Sofa/Component/Mass/Testing/src/sofa/component/mass/testing/MassTestCreation.h
new file mode 100644
index 00000000000..7dce8da651f
--- /dev/null
+++ b/Sofa/Component/Mass/Testing/src/sofa/component/mass/testing/MassTestCreation.h
@@ -0,0 +1,258 @@
+/******************************************************************************
+* 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 General Public License as published by the Free *
+* Software Foundation; either version 2 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 General Public License for *
+* more details. *
+* *
+* You should have received a copy of the GNU 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
+
+namespace sofa::component::mass::testing
+{
+
+/**
+ * @class Mass_test
+ * @brief A test class to verify mass-related computations in a simulation environment.
+ *
+ * The class `Mass_test` is used to verify correctness of several key operations related to mass,
+ * such as the mass matrix, computation of kinetic energy, and force/matrix interactions in
+ * a simulation's mechanical state.
+ *
+ * @tparam _MassType Template parameter defining the mass type being tested. This type must define
+ * the associated `DataTypes` structure and methods for mass-related computations.
+ */
+template
+requires std::is_base_of_v
+struct Mass_test : public sofa::testing::BaseSimulationTest, public sofa::testing::NumericTest
+{
+ using Mass = _MassType;
+ using DataTypes = typename Mass::DataTypes;
+
+ using VecCoord = sofa::VecCoord_t;
+ using VecDeriv = sofa::VecDeriv_t;
+ using Coord = sofa::Coord_t;
+ using Deriv = sofa::Deriv_t;
+ using Real = sofa::Real_t;
+
+ using DOF = sofa::component::statecontainer::MechanicalObject;
+
+ /// @name Scene elements
+ /// {
+ typename DOF::SPtr m_dof;
+ typename Mass::SPtr m_mass;
+ typename simulation::Node::SPtr m_node;
+ /// }
+
+ /// @name Precision and control parameters
+ /// {
+ SReal m_errorMax; ///< tolerance in precision test. The actual value is this one times the epsilon of the Real numbers (typically float or double)
+ std::pair m_deltaRange;
+ bool m_debug; ///< Print debug messages. Default is false.
+ /// }
+
+ bool m_testAccFromF { true };
+ bool m_testAddMToMatrix { true };
+ bool m_testBuildMassMatrix { true };
+ bool m_testKineticEnergy { true };
+
+ Mass_test()
+ : m_errorMax( 100 )
+ , m_deltaRange( 1, 1000 )
+ , m_debug( false )
+ {
+ simulation::Simulation* simu = sofa::simulation::getSimulation();
+ assert(simu);
+
+ m_node = simu->createNewGraph("root");
+ m_dof = sofa::core::objectmodel::New();
+ m_node->addObject(m_dof);
+ m_mass = sofa::core::objectmodel::New();
+ m_node->addObject(m_mass);
+ }
+
+ void setupState( const VecCoord& x, const VecDeriv& v)
+ {
+ std::size_t n = x.size();
+ this->m_dof->resize(static_cast(n));
+ typename DOF::WriteVecCoord xdof = this->m_dof->writePositions();
+ sofa::testing::copyToData( xdof, x );
+ typename DOF::WriteVecDeriv vdof = this->m_dof->writeVelocities();
+ sofa::testing::copyToData( vdof, v );
+ }
+
+ void resetForce(core::MechanicalParams* mparams) const
+ {
+ simulation::mechanicalvisitor::MechanicalResetForceVisitor computeForce( mparams, core::vec_id::write_access::force );
+ this->m_node->execute(computeForce);
+ }
+
+ void checkAddMToMatrix(const VecDeriv& v, const core::MechanicalParams& mparams, VecDeriv Mv,
+ sofa::SignedIndex matrixSize)
+ {
+ sofa::linearalgebra::EigenBaseSparseMatrix testMatrix(matrixSize, matrixSize);
+
+ core::behavior::DefaultMultiMatrixAccessor accessor;
+ accessor.addMechanicalState(m_dof.get());
+ accessor.setGlobalMatrix(&testMatrix);
+
+ m_mass->addMToMatrix(&mparams, &accessor);
+ testMatrix.compress();
+
+ // Multiply by v
+ sofa::type::vector eigenV, eigenMv;
+ sofa::testing::data_traits::VecDeriv_to_Vector(eigenV, v);
+ eigenMv = testMatrix * eigenV;
+
+ sofa::type::vector expectedMv;
+ sofa::testing::data_traits::VecDeriv_to_Vector(expectedMv, Mv);
+
+ EXPECT_LT(this->vectorMaxDiff(eigenMv, expectedMv), m_errorMax * this->epsilon())
+ << "addMToMatrix inconsistent with addMDx";
+ }
+
+ void checkBuildMassMatrix(const VecDeriv& v, const VecDeriv& Mv, sofa::SignedIndex matrixSize)
+ {
+ sofa::linearalgebra::EigenBaseSparseMatrix testMatrix(matrixSize, matrixSize);
+
+ struct MassMatrixAccumulatorTest : public core::behavior::MassMatrixAccumulator
+ {
+ MassMatrixAccumulatorTest(sofa::linearalgebra::EigenBaseSparseMatrix& m)
+ : matrix(m)
+ {
+ }
+ void add(sofa::SignedIndex row, sofa::SignedIndex col, float value) override
+ {
+ matrix.add(row, col, (Real)value);
+ }
+ void add(sofa::SignedIndex row, sofa::SignedIndex col, double value) override
+ {
+ matrix.add(row, col, (Real)value);
+ }
+ sofa::linearalgebra::EigenBaseSparseMatrix& matrix;
+ } accumulator(testMatrix);
+
+ m_mass->buildMassMatrix(&accumulator);
+ testMatrix.compress();
+
+ // Multiply by v
+ sofa::type::vector eigenV, eigenMv;
+ sofa::testing::data_traits::VecDeriv_to_Vector(eigenV, v);
+ eigenMv = testMatrix * eigenV;
+
+ sofa::type::vector expectedMv;
+ sofa::testing::data_traits::VecDeriv_to_Vector(expectedMv, Mv);
+
+ EXPECT_LT((SReal)this->vectorMaxDiff(eigenMv, expectedMv), (SReal)(m_errorMax * this->epsilon()))
+ << "buildMassMatrix inconsistent with addMDx";
+ }
+
+ void checkKineticEnergy(const VecDeriv& v, std::size_t n, const core::MechanicalParams& mparams,
+ const VecDeriv& Mv)
+ {
+ SReal ke = m_mass->getKineticEnergy(&mparams);
+ SReal vMv = 0;
+ for (sofa::Size i = 0; i < static_cast(n); ++i)
+ {
+ vMv += static_cast(sofa::type::dot(v[i], Mv[i]));
+ }
+
+ // v * Mv should be the 2 * kinetic energy
+ EXPECT_LT(std::abs(vMv - 2 * ke), (SReal)(m_errorMax * this->epsilon()))
+ << "Kinetic energy inconsistent with addMDx";
+ }
+ /**
+ * @brief Given positions and velocities, checks mass methods.
+ * @param x positions
+ * @param v velocities
+ */
+ void run_test( const VecCoord& x, const VecDeriv& v, bool initScene = true )
+ {
+ if( m_deltaRange.second / m_errorMax <= sofa::testing::g_minDeltaErrorRatio )
+ ADD_FAILURE() << "The comparison threshold is too large for the finite difference delta";
+
+ ASSERT_EQ(x.size(), v.size());
+ std::size_t n = x.size();
+
+ // copy the position and velocities to the scene graph
+ setupState(x, v);
+
+ // init scene
+ if (initScene)
+ {
+ sofa::simulation::node::initRoot(this->m_node.get());
+ }
+
+ core::MechanicalParams mparams;
+ mparams.setMFactor(1.0);
+
+ m_dof->vRealloc(&mparams, core::vec_id::write_access::dx); // dx is not allocated by default
+ typename DOF::WriteVecDeriv dx = m_dof->writeDx();
+ sofa::testing::copyToData ( dx, v );
+
+ // Compute f = M * dx
+ resetForce(&mparams);
+ m_mass->addMDx(&mparams, core::vec_id::write_access::force, 1.0);
+
+ VecDeriv Mv;
+ sofa::testing::copyFromData(Mv, m_dof->readForces());
+
+ // 1. Kinetic Energy
+ if (m_testKineticEnergy)
+ {
+ checkKineticEnergy(v, n, mparams, Mv);
+ }
+
+ // 2. Test accFromF: a = M^-1 * f
+ // a = M^-1 * (M * v) should be v
+ if (m_testAccFromF)
+ {
+ m_mass->accFromF(&mparams, sofa::core::vec_id::write_access::force);
+
+ VecDeriv a;
+ sofa::testing::copyFromData(a, m_dof->readForces());
+
+ EXPECT_LT((SReal)this->vectorMaxDiff(a, v), (SReal)(m_errorMax * this->epsilon())) << "accFromF inconsistent with addMDx (M^-1 * M * v != v)";
+ }
+
+ sofa::SignedIndex matrixSize = (sofa::SignedIndex)(n * DataTypes::deriv_total_size);
+
+ // 3. Test buildMassMatrix
+ if (m_testBuildMassMatrix)
+ {
+ checkBuildMassMatrix(v, Mv, matrixSize);
+ }
+
+ // 4. Test addMToMatrix
+ if (m_testAddMToMatrix)
+ {
+ checkAddMToMatrix(v, mparams, Mv, matrixSize);
+ }
+ }
+};
+
+} // namespace sofa::component::mass::testing
diff --git a/Sofa/Component/Mass/Testing/src/sofa/component/mass/testing/empty.cpp b/Sofa/Component/Mass/Testing/src/sofa/component/mass/testing/empty.cpp
new file mode 100644
index 00000000000..109c1bda6e8
--- /dev/null
+++ b/Sofa/Component/Mass/Testing/src/sofa/component/mass/testing/empty.cpp
@@ -0,0 +1,24 @@
+/******************************************************************************
+* 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
+
+SOFA_EXPORT_DYNAMIC_LIBRARY void init(){}
diff --git a/Sofa/Component/Mass/src/sofa/component/mass/MeshMatrixMass.h b/Sofa/Component/Mass/src/sofa/component/mass/MeshMatrixMass.h
index 9b5a3e2eb85..eac01a2eedf 100644
--- a/Sofa/Component/Mass/src/sofa/component/mass/MeshMatrixMass.h
+++ b/Sofa/Component/Mass/src/sofa/component/mass/MeshMatrixMass.h
@@ -197,12 +197,15 @@ class MeshMatrixMass : public core::behavior::Mass, public virtual co
// -- Mass interface
+ using Inherited::addMDx;
void addMDx(const core::MechanicalParams*, DataVecDeriv& f, const DataVecDeriv& dx, SReal factor) override;
+ using Inherited::accFromF;
void accFromF(const core::MechanicalParams*, DataVecDeriv& a, const DataVecDeriv& f) override; // This function can't be used as it use M^-1
void addForce(const core::MechanicalParams*, DataVecDeriv& f, const DataVecCoord& x, const DataVecDeriv& v) override;
+ using Inherited::getKineticEnergy;
SReal getKineticEnergy(const core::MechanicalParams*, const DataVecDeriv& v) const override; ///< vMv/2 using dof->getV() override
SReal getPotentialEnergy(const core::MechanicalParams*, const DataVecCoord& x) const override; ///< Mgx potential in a uniform gravity field, null at origin
@@ -216,6 +219,7 @@ class MeshMatrixMass : public core::behavior::Mass, public virtual co
/// Add Mass contribution to global Matrix assembling
+ using Inherited::addMToMatrix;
void addMToMatrix(sofa::linearalgebra::BaseMatrix * mat, SReal mFact, unsigned int &offset) override;
void buildMassMatrix(sofa::core::behavior::MassMatrixAccumulator* matrices) override;
void buildStiffnessMatrix(core::behavior::StiffnessMatrix* /* matrix */) override {}
diff --git a/Sofa/Component/Mass/src/sofa/component/mass/UniformMass.h b/Sofa/Component/Mass/src/sofa/component/mass/UniformMass.h
index d20fed31751..01f7a5f90ef 100644
--- a/Sofa/Component/Mass/src/sofa/component/mass/UniformMass.h
+++ b/Sofa/Component/Mass/src/sofa/component/mass/UniformMass.h
@@ -129,10 +129,13 @@ class UniformMass : public core::behavior::Mass, public virtual core:
sofa::core::objectmodel::ComponentState updateFromTotalMass();
sofa::core::objectmodel::ComponentState updateFromVertexMass();
+ using Inherited::addMDx;
void addMDx(const core::MechanicalParams* mparams, DataVecDeriv& f, const DataVecDeriv& dx, SReal factor) override;
+ using Inherited::accFromF;
void accFromF(const core::MechanicalParams* mparams, DataVecDeriv& a, const DataVecDeriv& f) override;
void addForce(const core::MechanicalParams* mparams, DataVecDeriv& f, const DataVecCoord& x, const DataVecDeriv& v) override;
+ using Inherited::getKineticEnergy;
SReal getKineticEnergy(const core::MechanicalParams* mparams, const DataVecDeriv& d_v) const override; ///< vMv/2 using dof->getV() override
SReal getPotentialEnergy(const core::MechanicalParams* mparams, const DataVecCoord& x) const override; ///< Mgx potential in a uniform gravity field, null at origin
type::Vec6 getMomentum(const core::MechanicalParams* mparams, const DataVecCoord& x, const DataVecDeriv& v) const override; ///< (Mv,cross(x,Mv)+Iw) override
@@ -141,6 +144,7 @@ class UniformMass : public core::behavior::Mass, public virtual core:
void addGravityToV(const core::MechanicalParams* mparams, DataVecDeriv& d_v) override;
+ using Inherited::addMToMatrix;
void addMToMatrix(sofa::linearalgebra::BaseMatrix * mat, SReal mFact, unsigned int &offset) override; /// Add Mass contribution to global Matrix assembling
void buildMassMatrix(sofa::core::behavior::MassMatrixAccumulator* matrices) override;
void buildStiffnessMatrix(core::behavior::StiffnessMatrix* /* matrix */) override {}
diff --git a/Sofa/Component/Mass/tests/CMakeLists.txt b/Sofa/Component/Mass/tests/CMakeLists.txt
index a91d0d1519a..89d4cd08629 100644
--- a/Sofa/Component/Mass/tests/CMakeLists.txt
+++ b/Sofa/Component/Mass/tests/CMakeLists.txt
@@ -4,12 +4,14 @@ project(Sofa.Component.Mass_test)
set(SOURCE_FILES
DiagonalMass_test.cpp
- MeshMatrixMass_test.cpp
UniformMass_test.cpp
+ MassTestCreation[MeshMatrixMass].cpp
+ MassTestCreation[UniformMass].cpp
)
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
+target_link_libraries(${PROJECT_NAME} Sofa.Component.Mass.Testing)
target_link_libraries(${PROJECT_NAME} Sofa.Testing)
-target_link_libraries(${PROJECT_NAME} Sofa.Component.Mass Sofa.Component.Topology.Container.Dynamic Sofa.Component.StateContainer)
+target_link_libraries(${PROJECT_NAME} Sofa.Component.Mass Sofa.Component.Topology.Container Sofa.Component.StateContainer)
add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME})
diff --git a/Sofa/Component/Mass/tests/MassTestCreation[MeshMatrixMass].cpp b/Sofa/Component/Mass/tests/MassTestCreation[MeshMatrixMass].cpp
new file mode 100644
index 00000000000..55d51ae57a4
--- /dev/null
+++ b/Sofa/Component/Mass/tests/MassTestCreation[MeshMatrixMass].cpp
@@ -0,0 +1,164 @@
+/******************************************************************************
+* 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 General Public License as published by the Free *
+* Software Foundation; either version 2 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 General Public License for *
+* more details. *
+* *
+* You should have received a copy of the GNU 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
+
+namespace sofa::component::mass::testing
+{
+
+using MeshTopology = sofa::component::topology::container::constant::MeshTopology;
+
+/***************************************************************************************************
+ * MeshMatrixMass
+ **************************************************************************************************/
+
+template
+struct MeshMatrixMass_template_test : public Mass_test>
+{
+ using VecCoord = sofa::VecCoord_t;
+ using VecDeriv = sofa::VecDeriv_t;
+ using Real = sofa::Real_t;
+
+ MeshTopology::SPtr m_topology;
+
+ MeshMatrixMass_template_test()
+ {
+ m_topology = sofa::core::objectmodel::New();
+ this->m_node->addObject(m_topology);
+ }
+
+ void run(const std::vector>& coords, bool lumped = false)
+ {
+ this->m_mass->setTotalMass(10.0_sreal);
+
+ VecCoord x(static_cast(coords.size()));
+ VecDeriv v(static_cast(coords.size()));
+
+ for (size_t i = 0; i < coords.size(); ++i)
+ {
+ DataTypes::set(x[i], coords[i][0], coords[i][1], coords[i][2]);
+ DataTypes::set(v[i], (coords[i][0] * 0.1), (coords[i][1] * 0.1), (coords[i][2] * 0.1));
+ m_topology->addPoint(static_cast(coords[i][0]), static_cast(coords[i][1]), static_cast(coords[i][2]));
+ }
+
+ m_topology->computeCrossElementBuffers();
+
+ this->m_testAccFromF = lumped;
+ this->m_mass->d_lumping.setValue(lumped);
+
+ this->run_test(x, v);
+ }
+
+ void runTriangle(bool lumped)
+ {
+ this->m_topology->addTriangle(0, 1, 2);
+ this->m_topology->addEdge(0, 1);
+ this->m_topology->addEdge(1, 2);
+ this->m_topology->addEdge(2, 0);
+ this->run({{0,0,0}, {1,0,0}, {0,1,0}}, lumped);
+ }
+
+ void runQuad(bool lumped)
+ {
+ this->m_topology->addQuad(0, 1, 2, 3);
+ this->m_topology->addEdge(0, 1);
+ this->m_topology->addEdge(1, 2);
+ this->m_topology->addEdge(2, 3);
+ this->m_topology->addEdge(3, 0);
+ this->run({{0,0,0}, {1,0,0}, {1,1,0}, {0,1,0}}, lumped);
+ }
+
+ void runTetrahedron(bool lumped)
+ {
+ this->m_topology->addTetra(0, 1, 2, 3);
+
+ for (const auto edge : sofa::core::topology::edgesInTetrahedronArray)
+ {
+ this->m_topology->addEdge(edge[0], edge[1]);
+ }
+
+ this->run({{0,0,0}, {1,0,0}, {0,1,0}, {0,0,1}}, lumped);
+ }
+
+ void runHexahedron(bool lumped)
+ {
+ this->m_topology->addHexa(0, 1, 2, 3, 4, 5, 6, 7);
+
+ for (const auto edge : sofa::core::topology::edgesInHexahedronArray)
+ {
+ this->m_topology->addEdge(edge[0], edge[1]);
+ }
+
+ this->run({{0,0,0}, {1,0,0}, {1,1,0}, {0,1,0},
+ {0,0,1}, {1,0,1}, {1,1,1}, {0,1,1}}, lumped);
+ }
+};
+
+typedef ::testing::Types<
+ defaulttype::Vec3Types
+> MeshMatrixMassDataTypes;
+
+TYPED_TEST_SUITE(MeshMatrixMass_template_test, MeshMatrixMassDataTypes);
+
+TYPED_TEST(MeshMatrixMass_template_test, Triangle)
+{
+ this->runTriangle(false);
+}
+
+TYPED_TEST(MeshMatrixMass_template_test, TriangleLumped)
+{
+ this->runTriangle(true);
+}
+
+TYPED_TEST(MeshMatrixMass_template_test, Tetrahedron)
+{
+ this->runTetrahedron(false);
+}
+
+TYPED_TEST(MeshMatrixMass_template_test, TetrahedronLumped)
+{
+ this->runTetrahedron(true);
+}
+
+TYPED_TEST(MeshMatrixMass_template_test, Quad)
+{
+ this->runQuad(false);
+}
+
+TYPED_TEST(MeshMatrixMass_template_test, QuadLumped)
+{
+ this->runQuad(true);
+}
+
+TYPED_TEST(MeshMatrixMass_template_test, Hexahedron)
+{
+ this->runHexahedron(false);
+}
+
+TYPED_TEST(MeshMatrixMass_template_test, HexahedronLumped)
+{
+ this->runHexahedron(true);
+}
+
+} // namespace sofa::component::mass::testing
diff --git a/Sofa/Component/Mass/tests/MassTestCreation[UniformMass].cpp b/Sofa/Component/Mass/tests/MassTestCreation[UniformMass].cpp
new file mode 100644
index 00000000000..fd50df4abc6
--- /dev/null
+++ b/Sofa/Component/Mass/tests/MassTestCreation[UniformMass].cpp
@@ -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 General Public License as published by the Free *
+* Software Foundation; either version 2 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 General Public License for *
+* more details. *
+* *
+* You should have received a copy of the GNU 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
+
+namespace sofa::component::mass::testing
+{
+
+/***************************************************************************************************
+ * UniformMass
+ **************************************************************************************************/
+
+template
+struct UniformMass_template_test : public Mass_test>
+{
+ using VecCoord = sofa::VecCoord_t;
+ using VecDeriv = sofa::VecDeriv_t;
+
+ void run()
+ {
+ this->m_mass->setTotalMass(10.0_sreal);
+
+ VecCoord x(2);
+ DataTypes::set(x[0], 9.5, -49.2, 5.32);
+ DataTypes::set(x[1], 0.8, 17.6, -7.3);
+
+ VecDeriv v(2);
+ DataTypes::set(v[0], 3.54, -0.87, 12.09);
+ DataTypes::set(v[1], 0.048, -8.7, -0.12);
+
+ this->run_test(x, v);
+ }
+};
+
+typedef ::testing::Types<
+ defaulttype::Vec1Types,
+ defaulttype::Vec2Types,
+ defaulttype::Vec3Types,
+ defaulttype::Vec6Types
+> UniformMassDataTypes;
+
+TYPED_TEST_SUITE(UniformMass_template_test, UniformMassDataTypes);
+
+TYPED_TEST(UniformMass_template_test, test)
+{
+ this->run();
+}
+
+} // namespace sofa::component::mass::testing
diff --git a/Sofa/framework/Core/src/sofa/core/behavior/Mass.inl b/Sofa/framework/Core/src/sofa/core/behavior/Mass.inl
index 88f94b7bea5..ddfc9869433 100644
--- a/Sofa/framework/Core/src/sofa/core/behavior/Mass.inl
+++ b/Sofa/framework/Core/src/sofa/core/behavior/Mass.inl
@@ -50,7 +50,11 @@ void Mass::addMDx(const MechanicalParams* mparams, MultiVecDerivId fi
if (mparams)
{
auto mstate = this->mstate.get();
- addMDx(mparams, *fid[mstate].write(), *mparams->readDx(mstate), factor);
+
+ if (auto* dxData = mparams->readDx(mstate))
+ {
+ addMDx(mparams, *fid[mstate].write(), *dxData, factor);
+ }
}
}