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); + } } }