Skip to content

Commit 9afc613

Browse files
committed
Merge branch 'master' of https://github.com/sofa-framework/sofa
2 parents 7f22c56 + 3e5e120 commit 9afc613

File tree

7 files changed

+180
-90
lines changed

7 files changed

+180
-90
lines changed

Sofa/Component/Collision/Response/Contact/src/sofa/component/collision/response/contact/PenalityContactForceField.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ class PenalityContactForceField : public core::behavior::PairInteractionForceFie
123123

124124
void buildDampingMatrix(core::behavior::DampingMatrix* /*matrix*/) final;
125125

126+
using Inherit::getPotentialEnergy;
126127
SReal getPotentialEnergy(const sofa::core::MechanicalParams*, const DataVecCoord&, const DataVecCoord& ) const override;
127128

128129
const type::vector< Contact >& getContact() const { return contacts.getValue();}

Sofa/Component/MechanicalLoad/src/sofa/component/mechanicalload/QuadPressureForceField.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ class QuadPressureForceField : public core::behavior::ForceField<DataTypes>
118118
void buildStiffnessMatrix(core::behavior::StiffnessMatrix* /*matrix*/) override;
119119
void buildDampingMatrix(core::behavior::DampingMatrix* /*matrix*/) final;
120120

121-
SReal getPotentialEnergy(const core::MechanicalParams* /*mparams*/, const DataVecCoord& /* x */) const override { msg_warning() << "Method getPotentialEnergy not implemented yet."; return 0.0; }
121+
using Inherit1::getPotentialEnergy;
122+
SReal getPotentialEnergy(const core::MechanicalParams* /*mparams*/, const DataVecCoord& /* x */) const override;
122123

123124
void draw(const core::visual::VisualParams* vparams) override;
124125

Sofa/Component/MechanicalLoad/src/sofa/component/mechanicalload/QuadPressureForceField.inl

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,15 @@ void QuadPressureForceField<DataTypes>::buildDampingMatrix(core::behavior::Dampi
232232
// No damping in this ForceField
233233
}
234234

235-
template<class DataTypes>
235+
template <class DataTypes>
236+
SReal QuadPressureForceField<DataTypes>::getPotentialEnergy(const core::MechanicalParams*,
237+
const DataVecCoord&) const
238+
{
239+
msg_warning() << "Method getPotentialEnergy not implemented yet.";
240+
return 0.0;
241+
}
242+
243+
template <class DataTypes>
236244
void QuadPressureForceField<DataTypes>::draw(const core::visual::VisualParams* vparams)
237245
{
238246
const auto stateLifeCycle = vparams->drawTool()->makeStateLifeCycle();

Sofa/Component/MechanicalLoad/src/sofa/component/mechanicalload/SurfacePressureForceField.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ class SurfacePressureForceField : public core::behavior::ForceField<DataTypes>
103103
void addKToMatrix(const core::MechanicalParams* mparams, const sofa::core::behavior::MultiMatrixAccessor* matrix) override;
104104
void buildStiffnessMatrix(core::behavior::StiffnessMatrix* matrix) override;
105105
void buildDampingMatrix(core::behavior::DampingMatrix* /*matrix*/) final;
106+
using Inherit1::getPotentialEnergy;
106107
SReal getPotentialEnergy(const core::MechanicalParams* /*mparams*/, const DataVecCoord& /* x */) const override;
107108

108109
void draw(const core::visual::VisualParams* vparams) override;

Sofa/Component/MechanicalLoad/src/sofa/component/mechanicalload/TrianglePressureForceField.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ class TrianglePressureForceField : public core::behavior::ForceField<DataTypes>
117117

118118
void buildDampingMatrix(core::behavior::DampingMatrix* /*matrix*/) final;
119119

120+
using Inherit1::getPotentialEnergy;
120121
SReal getPotentialEnergy(const core::MechanicalParams* /*mparams*/, const DataVecCoord& /* x */) const override;
121122
void draw(const core::visual::VisualParams* vparams) override;
122123

Sofa/Component/SolidMechanics/FEM/Elastic/src/sofa/component/solidmechanics/fem/elastic/TriangleFEMForceField.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ class TriangleFEMForceField : public BaseLinearElasticityFEMForceField<DataTypes
104104
void addKToMatrix(sofa::linearalgebra::BaseMatrix *mat, SReal k, unsigned int &offset) override; // compute and add all the element stiffnesses to the global stiffness matrix
105105
void buildStiffnessMatrix(core::behavior::StiffnessMatrix* matrix) override;
106106
void buildDampingMatrix(core::behavior::DampingMatrix* /*matrix*/) final;
107+
using Inherit1::getPotentialEnergy;
107108
SReal getPotentialEnergy(const core::MechanicalParams* /*mparams*/, const DataVecCoord& /* x */) const override
108109
{
109110
msg_warning() << "Method getPotentialEnergy not implemented yet.";

Sofa/Component/SolidMechanics/Testing/src/sofa/component/solidmechanics/testing/ForceFieldTestCreation.h

Lines changed: 165 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,7 @@
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>
4137
using 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+
"\nFailed 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+
"\nFailed 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-
"\nFailed seed number = " << this->seed;
370+
checkBuildStiffnessMatrix(&mparams, dX, changeOfForce);
294371
}
295372

296373

0 commit comments

Comments
 (0)