2222#pragma once
2323
2424#include < sofa/testing/BaseSimulationTest.h>
25- using sofa::testing::BaseSimulationTest;
26-
2725#include < sofa/testing/NumericTest.h>
28- using sofa::testing::NumericTest;
29-
30- #include < sofa/simulation/graph/DAGSimulation.h>
3126#include < sofa/simulation/MechanicalVisitor.h>
3227#include < sofa/linearalgebra/EigenBaseSparseMatrix.h>
3328#include < sofa/core/behavior/SingleMatrixAccessor.h>
@@ -36,6 +31,7 @@ using sofa::testing::NumericTest;
3631#include < sofa/defaulttype/VecTypes.h>
3732#include < sofa/component/statecontainer/MechanicalObject.h>
3833#include < sofa/core/behavior/BaseForceField.h>
34+ #include < sofa/core/behavior/BaseLocalForceFieldMatrix.h>
3935
4036#include < sofa/simulation/mechanicalvisitor/MechanicalComputeDfVisitor.h>
4137using sofa::simulation::mechanicalvisitor::MechanicalComputeDfVisitor;
@@ -57,8 +53,8 @@ namespace sofa {
5753 * @author François Faure, 2014
5854 *
5955 */
60- template <typename _ForceFieldType>
61- struct ForceField_test : public BaseSimulationTest , NumericTest<typename _ForceFieldType::DataTypes::Real>
56+ template <typename _ForceFieldType> requires std::is_base_of_v<core::behavior::BaseForceField, _ForceFieldType>
57+ struct ForceField_test : public sofa ::testing:: BaseSimulationTest, public sofa::testing:: NumericTest<typename _ForceFieldType::DataTypes::Real>
6258{
6359 typedef _ForceFieldType ForceField;
6460 typedef typename ForceField::DataTypes DataTypes;
@@ -146,6 +142,147 @@ struct ForceField_test : public BaseSimulationTest, NumericTest<typename _ForceF
146142 force = addNew<ForceField>(node);
147143 }
148144
145+ void setupState ( const VecCoord& x, const VecDeriv& v)
146+ {
147+ std::size_t n = x.size ();
148+ this ->dof ->resize (static_cast <sofa::Size>(n));
149+ typename DOF::WriteVecCoord xdof = this ->dof ->writePositions ();
150+ sofa::testing::copyToData ( xdof, x );
151+ typename DOF::WriteVecDeriv vdof = this ->dof ->writeVelocities ();
152+ sofa::testing::copyToData ( vdof, v );
153+ }
154+
155+ void computeForce (core::MechanicalParams* mparams) const
156+ {
157+ MechanicalResetForceVisitor resetForce (mparams, core::vec_id::write_access::force);
158+ node->execute (resetForce);
159+ MechanicalComputeForceVisitor computeForce ( mparams, core::vec_id::write_access::force );
160+ this ->node ->execute (computeForce);
161+ }
162+
163+ void checkForce (const VecDeriv& ef, const std::string& message = " " )
164+ {
165+ typename DOF::ReadVecDeriv f= this ->dof ->readForces ();
166+ EXPECT_LT ( this ->vectorMaxDiff (f,ef), errorMax*this ->epsilon () ) << message;
167+ }
168+
169+ void checkPotentialEnergy (const core::MechanicalParams* mparams, SReal potentialEnergyBefore, const VecDeriv& curF, const VecDeriv& dX)
170+ {
171+ if ( !(flags & TEST_POTENTIAL_ENERGY) ) return ;
172+
173+ // Get potential energy after displacement of dofs
174+ SReal potentialEnergyAfterDisplacement = force->getPotentialEnergy (mparams);
175+
176+ // Check getPotentialEnergy() we should have dE = -dX.F
177+
178+ // Compute dE = E(x+dx)-E(x)
179+ SReal differencePotentialEnergy = potentialEnergyAfterDisplacement-potentialEnergyBefore;
180+
181+ // Compute the expected difference of potential energy: -dX.F (dot product between applied displacement and Force)
182+ SReal expectedDifferencePotentialEnergy = 0 ;
183+ for ( unsigned i=0 ; i<dX.size (); ++i){
184+ expectedDifferencePotentialEnergy = expectedDifferencePotentialEnergy - dot (dX[i],curF[i]);
185+ }
186+
187+ SReal absoluteErrorPotentialEnergy = std::abs (differencePotentialEnergy - expectedDifferencePotentialEnergy);
188+ if ( absoluteErrorPotentialEnergy> errorFactorPotentialEnergy*errorMax*this ->epsilon () )
189+ {
190+ ADD_FAILURE ()<<" dPotentialEnergy differs from -dX.F (threshold=" << errorFactorPotentialEnergy*errorMax*this ->epsilon () << " )" << std::endl
191+ << " dPotentialEnergy is " << differencePotentialEnergy << std::endl
192+ << " -dX.F is " << expectedDifferencePotentialEnergy << std::endl
193+ << " Failed seed number = " << this ->seed << std::endl;
194+ }
195+ }
196+
197+ void checkComputeDf (core::MechanicalParams* mparams, const VecDeriv& dX, const VecDeriv& changeOfForce)
198+ {
199+ MechanicalResetForceVisitor resetForce (mparams, core::vec_id::write_access::force);
200+ node->execute (resetForce);
201+ dof->vRealloc ( mparams, core::vec_id::write_access::dx); // dx is not allocated by default
202+ typename DOF::WriteVecDeriv wdx = dof->writeDx ();
203+ sofa::testing::copyToData ( wdx, dX );
204+ MechanicalComputeDfVisitor computeDf ( mparams, core::vec_id::write_access::force );
205+ node->execute (computeDf);
206+ VecDeriv dF;
207+ sofa::testing::copyFromData ( dF, dof->readForces () );
208+
209+ EXPECT_LE (this ->vectorMaxDiff (changeOfForce, dF), errorMax * this ->epsilon ()) <<
210+ " dF differs from change of force\n "
211+ " Failed seed number = " << this ->seed ;
212+ }
213+
214+ void checkAddKToMatrix (core::MechanicalParams* mparams, const VecDeriv& dX, const VecDeriv& changeOfForce)
215+ {
216+ typedef sofa::linearalgebra::EigenBaseSparseMatrix<SReal> Sqmat;
217+ const std::size_t n = dX.size ();
218+ const sofa::SignedIndex matrixSize = static_cast <sofa::SignedIndex>(n * DataTypes::deriv_total_size);
219+ Sqmat K ( matrixSize, matrixSize);
220+ sofa::core::behavior::SingleMatrixAccessor accessor ( &K );
221+ mparams->setKFactor (1.0 );
222+ force->addKToMatrix ( mparams, &accessor);
223+ K.compress ();
224+
225+ modeling::Vector dx;
226+ sofa::testing::data_traits<DataTypes>::VecDeriv_to_Vector ( dx, dX );
227+
228+ modeling::Vector Kdx = K * dx;
229+ modeling::Vector df;
230+ sofa::testing::data_traits<DataTypes>::VecDeriv_to_Vector ( df, changeOfForce );
231+
232+ if ( debug )
233+ {
234+ std::cout << " [addKToMatrix] dX = " << dX << std::endl;
235+ std::cout << " change of force = " << changeOfForce << std::endl;
236+ std::cout << " Kdx = " << Kdx.transpose () << std::endl;
237+ }
238+
239+ EXPECT_LE ( this ->vectorMaxDiff (Kdx, df), errorMax * this ->epsilon () ) <<
240+ " Kdx (from addKToMatrix) differs from change of force"
241+ " \n Failed seed number = " << this ->seed ;
242+ }
243+
244+ void checkBuildStiffnessMatrix (core::MechanicalParams* mparams, const VecDeriv& dX, const VecDeriv& changeOfForce)
245+ {
246+ typedef sofa::linearalgebra::EigenBaseSparseMatrix<SReal> Sqmat;
247+ const std::size_t n = dX.size ();
248+ const sofa::SignedIndex matrixSize = static_cast <sofa::SignedIndex>(n * DataTypes::deriv_total_size);
249+ Sqmat K ( matrixSize, matrixSize);
250+
251+ struct StiffnessMatrixAccumulatorTest : public core ::behavior::StiffnessMatrixAccumulator
252+ {
253+ StiffnessMatrixAccumulatorTest (Sqmat& m) : matrix(m) {}
254+ void add (sofa::SignedIndex row, sofa::SignedIndex col, float value) override { matrix.add (row, col, (SReal)value); }
255+ void add (sofa::SignedIndex row, sofa::SignedIndex col, double value) override { matrix.add (row, col, (SReal)value); }
256+ Sqmat& matrix;
257+ };
258+
259+ StiffnessMatrixAccumulatorTest accumulator (K);
260+ sofa::core::behavior::StiffnessMatrix stiffnessMatrix;
261+ stiffnessMatrix.setMatrixAccumulator (&accumulator, dof.get ());
262+ stiffnessMatrix.setMechanicalParams (mparams);
263+
264+ force->buildStiffnessMatrix (&stiffnessMatrix);
265+ K.compress ();
266+
267+ modeling::Vector dx;
268+ sofa::testing::data_traits<DataTypes>::VecDeriv_to_Vector ( dx, dX );
269+
270+ modeling::Vector Kdx = K * dx;
271+ modeling::Vector df;
272+ sofa::testing::data_traits<DataTypes>::VecDeriv_to_Vector ( df, changeOfForce );
273+
274+ if ( debug )
275+ {
276+ std::cout << " [buildStiffnessMatrix] dX = " << dX << std::endl;
277+ std::cout << " change of force = " << changeOfForce << std::endl;
278+ std::cout << " Kdx = " << Kdx.transpose () << std::endl;
279+ }
280+
281+ EXPECT_LE ( this ->vectorMaxDiff (Kdx, df), errorMax * this ->epsilon () ) <<
282+ " Kdx (from buildStiffnessMatrix) differs from change of force"
283+ " \n Failed seed number = " << this ->seed ;
284+ }
285+
149286 /* *
150287 * @brief Given positions and velocities, checks that the expected forces are obtained, and that a small change of positions generates the corresponding change of forces.
151288 * @param x positions
@@ -169,33 +306,29 @@ struct ForceField_test : public BaseSimulationTest, NumericTest<typename _ForceF
169306 std::size_t n = x.size ();
170307
171308 // copy the position and velocities to the scene graph
172- this ->dof ->resize (static_cast <sofa::Size>(n));
173- typename DOF::WriteVecCoord xdof = this ->dof ->writePositions ();
174- sofa::testing::copyToData ( xdof, x );
175- typename DOF::WriteVecDeriv vdof = this ->dof ->writeVelocities ();
176- sofa::testing::copyToData ( vdof, v );
309+ setupState (x, v);
177310
178311 // init scene and compute force
179312 if (initScene)
180313 {
181314 sofa::simulation::node::initRoot (this ->node .get ());
182315 }
316+
183317 core::MechanicalParams mparams;
184318 mparams.setKFactor (1.0 );
185- MechanicalResetForceVisitor resetForce (&mparams, core::vec_id::write_access::force);
186- node->execute (resetForce);
187- MechanicalComputeForceVisitor computeForce ( &mparams, core::vec_id::write_access::force );
188- this ->node ->execute (computeForce);
319+
320+ computeForce (&mparams);
189321
190322 // check force
191- typename DOF::ReadVecDeriv f= this ->dof ->readForces ();
192- if (debug){
323+ if (debug)
324+ {
325+ typename DOF::ReadVecDeriv f= this ->dof ->readForces ();
193326 std::cout << " run_test, x = " << x << std::endl;
194327 std::cout << " v = " << v << std::endl;
195328 std::cout << " expected f = " << ef << std::endl;
196329 std::cout << " actual f = " << f.ref () << std::endl;
197330 }
198- ASSERT_LT ( this -> vectorMaxDiff (f,ef), errorMax* this -> epsilon () );
331+ checkForce (ef );
199332
200333 if ( !checkStiffness ) return ;
201334
@@ -206,91 +339,35 @@ struct ForceField_test : public BaseSimulationTest, NumericTest<typename _ForceF
206339 sofa::testing::copyFromData ( curF, dof->readForces () );
207340
208341 // Get potential Energy before applying a displacement to dofs
209- SReal potentialEnergyBeforeDisplacement = (flags & TEST_POTENTIAL_ENERGY) ? (( const core::behavior::BaseForceField*) force. get ()) ->getPotentialEnergy (&mparams) : 0 ;
342+ SReal potentialEnergyBeforeDisplacement = (flags & TEST_POTENTIAL_ENERGY) ? force->getPotentialEnergy (&mparams) : 0 ;
210343
211344 // change position
212345 VecDeriv dX (n);
213- for ( unsigned i=0 ; i<n; i++ ){
214- dX[i] = DataTypes::randomDeriv ( deltaRange.first * this ->epsilon (), deltaRange.second * this ->epsilon () ); // todo: better random, with negative values
215- xdof[i] += dX[i];
346+ {
347+ typename DOF::WriteVecCoord xdof = this ->dof ->writePositions ();
348+ for ( unsigned i=0 ; i<n; i++ ){
349+ dX[i] = DataTypes::randomDeriv ( deltaRange.first * this ->epsilon (), deltaRange.second * this ->epsilon () ); // todo: better random, with negative values
350+ xdof[i] += dX[i];
351+ }
216352 }
217353
218354 // compute new force and difference between previous force
219- node-> execute (resetForce );
220- node-> execute (computeForce);
355+ computeForce (&mparams );
356+
221357 VecDeriv newF;
222358 sofa::testing::copyFromData ( newF, dof->readForces () );
223359 VecDeriv changeOfForce (curF);
224360 for ( unsigned i=0 ; i<curF.size (); ++i){
225361 changeOfForce[i] = newF[i] - curF[i];
226362 }
227363
228- if ( flags & TEST_POTENTIAL_ENERGY )
229- {
230- // Get potential energy after displacement of dofs
231- SReal potentialEnergyAfterDisplacement = ((const core::behavior::BaseForceField*)force.get ())->getPotentialEnergy (&mparams);
364+ checkPotentialEnergy (&mparams, potentialEnergyBeforeDisplacement, curF, dX);
232365
233- // Check getPotentialEnergy() we should have dE = -dX.F
366+ checkComputeDf (&mparams, dX, changeOfForce);
234367
235- // Compute dE = E(x+dx)-E(x)
236- SReal differencePotentialEnergy = potentialEnergyAfterDisplacement-potentialEnergyBeforeDisplacement;
368+ checkAddKToMatrix (&mparams, dX, changeOfForce);
237369
238- // Compute the expected difference of potential energy: -dX.F (dot product between applied displacement and Force)
239- SReal expectedDifferencePotentialEnergy = 0 ;
240- for ( unsigned i=0 ; i<n; ++i){
241- expectedDifferencePotentialEnergy = expectedDifferencePotentialEnergy - dot (dX[i],curF[i]);
242- }
243-
244- SReal absoluteErrorPotentialEnergy = std::abs (differencePotentialEnergy - expectedDifferencePotentialEnergy);
245- if ( absoluteErrorPotentialEnergy> errorFactorPotentialEnergy*errorMax*this ->epsilon () ){
246- ADD_FAILURE ()<<" dPotentialEnergy differs from -dX.F (threshold=" << errorFactorPotentialEnergy*errorMax*this ->epsilon () << " )" << std::endl
247- << " dPotentialEnergy is " << differencePotentialEnergy << std::endl
248- << " -dX.F is " << expectedDifferencePotentialEnergy << std::endl
249- << " Failed seed number = " << this ->seed << std::endl;
250- }
251- }
252-
253-
254- // check computeDf: compare its result to actual change
255- node->execute (resetForce);
256- dof->vRealloc ( &mparams, core::vec_id::write_access::dx); // dx is not allocated by default
257- typename DOF::WriteVecDeriv wdx = dof->writeDx ();
258- sofa::testing::copyToData ( wdx, dX );
259- MechanicalComputeDfVisitor computeDf ( &mparams, core::vec_id::write_access::force );
260- node->execute (computeDf);
261- VecDeriv dF;
262- sofa::testing::copyFromData ( dF, dof->readForces () );
263-
264- EXPECT_LE (this ->vectorMaxDiff (changeOfForce, dF), errorMax * this ->epsilon ()) <<
265- " dF differs from change of force\n "
266- " Failed seed number = " << this ->seed ;
267-
268- // check stiffness matrix: compare its product with dx to actual force change
269- typedef sofa::linearalgebra::EigenBaseSparseMatrix<SReal> Sqmat;
270- const sofa::SignedIndex matrixSize = static_cast <sofa::SignedIndex>(n * DataTypes::deriv_total_size);
271- Sqmat K ( matrixSize, matrixSize);
272- sofa::core::behavior::SingleMatrixAccessor accessor ( &K );
273- mparams.setKFactor (1.0 );
274- force->addKToMatrix ( &mparams, &accessor);
275- K.compress ();
276- // cout << "stiffness: " << K << endl;
277- modeling::Vector dx;
278- sofa::testing::data_traits<DataTypes>::VecDeriv_to_Vector ( dx, dX );
279-
280- modeling::Vector Kdx = K * dx;
281- if ( debug ){
282- std::cout << " dX = " << dX << std::endl;
283- std::cout << " newF = " << newF << std::endl;
284- std::cout << " change of force = " << changeOfForce << std::endl;
285- std::cout << " addDforce = " << dF << std::endl;
286- std::cout << " Kdx = " << Kdx.transpose () << std::endl;
287- }
288-
289- modeling::Vector df;
290- sofa::testing::data_traits<DataTypes>::VecDeriv_to_Vector ( df, changeOfForce );
291- EXPECT_LE ( this ->vectorMaxDiff (Kdx, df), errorMax * this ->epsilon () ) <<
292- " Kdx differs from change of force"
293- " \n Failed seed number = " << this ->seed ;
370+ checkBuildStiffnessMatrix (&mparams, dX, changeOfForce);
294371 }
295372
296373
0 commit comments