1 /* -------------------------------------------------------------------------- *
2  *                               Simbody(tm)                                  *
3  * -------------------------------------------------------------------------- *
4  * This is part of the SimTK biosimulation toolkit originating from           *
5  * Simbios, the NIH National Center for Physics-Based Simulation of           *
6  * Biological Structures at Stanford, funded under the NIH Roadmap for        *
7  * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody.  *
8  *                                                                            *
9  * Portions copyright (c) 2007-12 Stanford University and the Authors.        *
10  * Authors: Peter Eastman                                                     *
11  * Contributors:                                                              *
12  *                                                                            *
13  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
14  * not use this file except in compliance with the License. You may obtain a  *
15  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
16  *                                                                            *
17  * Unless required by applicable law or agreed to in writing, software        *
18  * distributed under the License is distributed on an "AS IS" BASIS,          *
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
20  * See the License for the specific language governing permissions and        *
21  * limitations under the License.                                             *
22  * -------------------------------------------------------------------------- */
23 
24 #include "SimTKsimbody.h"
25 
26 using namespace SimTK;
27 using namespace std;
28 
29 const int NUM_BODIES = 10;
30 const Real BOND_LENGTH = 0.5;
31 
32 #define ASSERT(cond) {SimTK_ASSERT_ALWAYS(cond, "Assertion failed");}
33 
34 #define ASSERT_EQUAL(val1, val2) {ASSERT(std::abs(val1-val2) < 1e-10);}
35 
verifyForces(const Force & force,const State & state,Vector_<SpatialVec> bodyForces,Vector_<Vec3> particleForces,Vector mobilityForces)36 void verifyForces(const Force& force, const State& state, Vector_<SpatialVec> bodyForces, Vector_<Vec3> particleForces, Vector mobilityForces) {
37     Vector_<SpatialVec> actualBodyForces(bodyForces.size());
38     Vector_<Vec3> actualParticleForces(particleForces.size());
39     Vector actualMobilityForces(mobilityForces.size());
40     force.calcForceContribution(state, actualBodyForces, actualParticleForces,
41                                 actualMobilityForces);
42 
43     for (int i = 0; i < bodyForces.size(); ++i)
44         ASSERT((bodyForces[i]-actualBodyForces[i]).norm() < 1e-10);
45     for (int i = 0; i < particleForces.size(); ++i)
46         ASSERT((particleForces[i]-actualParticleForces[i]).norm() < 1e-10);
47     for (int i = 0; i < mobilityForces.size(); ++i)
48         ASSERT(std::abs(mobilityForces[i]-actualMobilityForces[i]) < 1e-10);
49 }
50 
51 class MyForceImpl : public Force::Custom::Implementation {
52 public:
53     mutable bool hasRealized[Stage::Report+1];
MyForceImpl()54     MyForceImpl() {
55         for (int i = 0; i < Stage::NValid; i++)
56             hasRealized[i] = false;
57     }
calcForce(const State & state,Vector_<SpatialVec> & bodyForces,Vector_<Vec3> & particleForces,Vector & mobilityForces) const58     void calcForce(const State& state, Vector_<SpatialVec>& bodyForces, Vector_<Vec3>& particleForces, Vector& mobilityForces) const override {
59         for (int i = 0; i < mobilityForces.size(); ++i)
60             mobilityForces[i] += i;
61     }
calcPotentialEnergy(const State & state) const62     Real calcPotentialEnergy(const State& state) const override {
63         return 0.0;
64     }
realizeTopology(State & state) const65     void realizeTopology(State& state) const override {
66         hasRealized[Stage::Topology] = true;
67     }
realizeModel(State & state) const68     void realizeModel(State& state) const override {
69         hasRealized[Stage::Model] = true;
70     }
realizeInstance(const State & state) const71     void realizeInstance(const State& state) const override {
72         hasRealized[Stage::Instance] = true;
73     }
realizeTime(const State & state) const74     void realizeTime(const State& state) const override {
75         hasRealized[Stage::Time] = true;
76     }
realizePosition(const State & state) const77     void realizePosition(const State& state) const override {
78         hasRealized[Stage::Position] = true;
79     }
realizeVelocity(const State & state) const80     void realizeVelocity(const State& state) const override {
81         hasRealized[Stage::Velocity] = true;
82     }
realizeDynamics(const State & state) const83     void realizeDynamics(const State& state) const override {
84         hasRealized[Stage::Dynamics] = true;
85     }
realizeAcceleration(const State & state) const86     void realizeAcceleration(const State& state) const override {
87         hasRealized[Stage::Acceleration] = true;
88     }
realizeReport(const State & state) const89     void realizeReport(const State& state) const override {
90         hasRealized[Stage::Report] = true;
91     }
92 };
93 
94 /**
95  * Test all of the standard Force subclasses, and make sure they generate correct forces.
96  */
97 
testStandardForces()98 void testStandardForces() {
99 
100     // Create a system consisting of a chain of bodies.
101 
102     MultibodySystem system;
103     SimbodyMatterSubsystem matter(system);
104     GeneralForceSubsystem forces(system);
105     Body::Rigid body(MassProperties(1.0, Vec3(0), Inertia(1)));
106     for (int i = 0; i < NUM_BODIES; ++i) {
107         MobilizedBody& parent = matter.updMobilizedBody(MobilizedBodyIndex(matter.getNumBodies()-1));
108         MobilizedBody::Gimbal b(parent, Transform(Vec3(0)), body, Transform(Vec3(BOND_LENGTH, 0, 0)));
109     }
110 
111     // Add one of each type of force.
112 
113     MobilizedBody& body1 = matter.updMobilizedBody(MobilizedBodyIndex(1));
114     MobilizedBody& body9 = matter.updMobilizedBody(MobilizedBodyIndex(9));
115     Force::ConstantForce constantForce(forces, body1, Vec3(0), Vec3(1, 2, 3));
116     Force::ConstantTorque constantTorque(forces, body1, Vec3(1, 2, 3));
117     Force::GlobalDamper globalDamper(forces, matter, 2.0);
118     Force::MobilityConstantForce mobilityConstantForce(forces, body1, 1, 2.0);
119     Force::MobilityLinearDamper mobilityLinearDamper(forces, body1, 1, 2.0);
120     Force::MobilityLinearSpring mobilityLinearSpring(forces, body1, 1, 2.0, 1.0);
121     Force::TwoPointConstantForce twoPointConstantForce(forces, body1, Vec3(0), body9, Vec3(0), 2.0);
122     Force::TwoPointLinearDamper twoPointLinearDamper(forces, body1, Vec3(0), body9, Vec3(0), 2.0);
123     Force::TwoPointLinearSpring twoPointLinearSpring(forces, body1, Vec3(0), body9, Vec3(0), 2.0, 0.5);
124     Force::UniformGravity uniformGravity(forces, matter, Vec3(0, -2.0, 0));
125     Force::Custom custom(forces, new MyForceImpl());
126 
127     // Create a random state for it.
128 
129     system.realizeTopology();
130     State state = system.getDefaultState();
131     Random::Uniform random;
132     for (int i = 0; i < state.getNY(); ++i)
133         state.updY()[i] = random.getValue();
134     system.realize(state, Stage::Velocity);
135     Vec3 pos1 = body1.getBodyOriginLocation(state);
136     Vec3 pos9 = body9.getBodyOriginLocation(state);
137     Vec3 delta19 = pos9-pos1;
138 
139     // Calculate each force component and see if it is correct.
140 
141     Vector_<SpatialVec> bodyForces(matter.getNumBodies());
142     Vector_<Vec3> particleForces(0);
143     Vector mobilityForces(state.getNU());
144     Real pe = 0;
145 
146     // Check ConstantForce
147 
148     bodyForces = SpatialVec(Vec3(0), Vec3(0));
149     particleForces = Vec3(0);
150     mobilityForces = 0;
151     bodyForces[1][1] = Vec3(1, 2, 3);
152     verifyForces(constantForce, state, bodyForces, particleForces, mobilityForces);
153 
154     // Check ConstantTorque
155 
156     bodyForces = SpatialVec(Vec3(0), Vec3(0));
157     particleForces = Vec3(0);
158     mobilityForces = 0;
159     bodyForces[1][0] = Vec3(1, 2, 3);
160     verifyForces(constantTorque, state, bodyForces, particleForces, mobilityForces);
161 
162     // Check GlobalDamper
163 
164     bodyForces = SpatialVec(Vec3(0), Vec3(0));
165     particleForces = Vec3(0);
166     mobilityForces = -2.0*state.getU();
167     verifyForces(globalDamper, state, bodyForces, particleForces, mobilityForces);
168 
169     // Check MobilityConstantForce
170 
171     bodyForces = SpatialVec(Vec3(0), Vec3(0));
172     particleForces = Vec3(0);
173     mobilityForces = 0;
174     body1.updOneFromUPartition(state, 1, mobilityForces) = 2.0;
175     verifyForces(mobilityConstantForce, state, bodyForces, particleForces, mobilityForces);
176 
177     // Check MobilityLinearDamper
178 
179     bodyForces = SpatialVec(Vec3(0), Vec3(0));
180     particleForces = Vec3(0);
181     mobilityForces = 0;
182     body1.updOneFromUPartition(state, 1, mobilityForces) = -2.0*body1.getOneU(state, 1);
183     verifyForces(mobilityLinearDamper, state, bodyForces, particleForces, mobilityForces);
184 
185     // Check MobilityLinearSpring
186 
187     bodyForces = SpatialVec(Vec3(0), Vec3(0));
188     particleForces = Vec3(0);
189     mobilityForces = 0;
190     body1.updOneFromUPartition(state, 1, mobilityForces) = -2.0*(body1.getOneQ(state, 1)-1.0);
191     verifyForces(mobilityLinearSpring, state, bodyForces, particleForces, mobilityForces);
192 
193     // Check TwoPointConstantForce
194 
195     bodyForces = SpatialVec(Vec3(0), Vec3(0));
196     particleForces = Vec3(0);
197     mobilityForces = 0;
198     bodyForces[1][1] = -2.0*delta19.normalize();
199     bodyForces[9][1] = 2.0*delta19.normalize();
200     verifyForces(twoPointConstantForce, state, bodyForces, particleForces, mobilityForces);
201 
202     // Check TwoPointLinearDamper
203 
204     bodyForces = SpatialVec(Vec3(0), Vec3(0));
205     particleForces = Vec3(0);
206     mobilityForces = 0;
207     Vec3 v19 = body9.getBodyOriginVelocity(state)-body1.getBodyOriginVelocity(state);
208     Real twoPointLinearDamperForce = 2.0*dot(v19, delta19.normalize());
209     bodyForces[1][1] = twoPointLinearDamperForce*delta19.normalize();
210     bodyForces[9][1] = -twoPointLinearDamperForce*delta19.normalize();
211     verifyForces(twoPointLinearDamper, state, bodyForces, particleForces, mobilityForces);
212 
213     // Check TwoPointLinearSpring
214 
215     bodyForces = SpatialVec(Vec3(0), Vec3(0));
216     particleForces = Vec3(0);
217     mobilityForces = 0;
218     Real twoPointLinearSpringForce = 2.0*(delta19.norm()-0.5);
219     bodyForces[1][1] = twoPointLinearSpringForce*delta19.normalize();
220     bodyForces[9][1] = -twoPointLinearSpringForce*delta19.normalize();
221     verifyForces(twoPointLinearSpring, state, bodyForces, particleForces, mobilityForces);
222 
223     // Check UniformGravity
224 
225     bodyForces = SpatialVec(Vec3(0), Vec3(0, -2.0, 0));
226     bodyForces[0] = SpatialVec(Vec3(0), Vec3(0));
227     particleForces = Vec3(0);
228     mobilityForces = 0;
229     verifyForces(uniformGravity, state, bodyForces, particleForces, mobilityForces);
230 
231     // Check Custom
232 
233     bodyForces = SpatialVec(Vec3(0), Vec3(0));
234     particleForces = Vec3(0);
235     for (int i = 0; i < mobilityForces.size(); ++i)
236         mobilityForces[i] = i;
237     verifyForces(custom, state, bodyForces, particleForces, mobilityForces);
238 }
239 
240 /**
241  * Test the standard conservative forces to make sure they really conserve energy.
242  */
243 
testEnergyConservation()244 void testEnergyConservation() {
245 
246     // Create a system consisting of a chain of bodies.
247 
248     MultibodySystem system;
249     SimbodyMatterSubsystem matter(system);
250     GeneralForceSubsystem forces(system);
251     Body::Rigid body(MassProperties(1.0, Vec3(0), Inertia(1)));
252     for (int i = 0; i < NUM_BODIES; ++i) {
253         MobilizedBody& parent = matter.updMobilizedBody(MobilizedBodyIndex(matter.getNumBodies()-1));
254         MobilizedBody::Gimbal b(parent, Transform(Vec3(0)), body, Transform(Vec3(BOND_LENGTH, 0, 0)));
255     }
256 
257     // Add one of each type of conservative force.
258 
259     MobilizedBody& body1 = matter.updMobilizedBody(MobilizedBodyIndex(1));
260     MobilizedBody& body9 = matter.updMobilizedBody(MobilizedBodyIndex(9));
261     Force::MobilityLinearSpring mobilityLinearSpring(forces, body1, 1, 0.1, 1.0);
262     Force::TwoPointLinearSpring twoPointLinearSpring(forces, body1, Vec3(0), body9, Vec3(0), 1.0, 4.0);
263     Force::UniformGravity uniformGravity(forces, matter, Vec3(0, -1.0, 0));
264 
265     // Create a random initial state for it.
266 
267     system.realizeTopology();
268     State state = system.getDefaultState();
269     Random::Uniform random;
270     for (int i = 0; i < state.getNY(); ++i)
271         state.updY()[i] = random.getValue();
272 
273     // Simulate it for a while and see if the energy changes.
274 
275     system.realize(state, Stage::Dynamics);
276     Real initialEnergy = system.calcEnergy(state);
277     RungeKuttaMersonIntegrator integ(system);
278     integ.setAccuracy(1e-4);
279     TimeStepper ts(system, integ);
280     ts.initialize(state);
281     ts.stepTo(10.0);
282     system.realize(state, Stage::Dynamics);
283     Real finalEnergy = system.calcEnergy(ts.getState());
284     ASSERT(std::abs(initialEnergy/finalEnergy-1.0) < 0.005);
285 }
286 
287 /**
288  * Make sure that all the "realize" methods on a custom force actually get called.
289  */
290 
testCustomRealization()291 void testCustomRealization() {
292     MultibodySystem system;
293     SimbodyMatterSubsystem matter(system);
294     GeneralForceSubsystem forces(system);
295     MyForceImpl* impl = new MyForceImpl();
296     Force::Custom custom(forces, impl);
297     State state = system.realizeTopology();
298     for (Stage j = Stage::Model; j <= Stage::Report; j++) {
299         system.realize(state, j);
300         for (Stage i = Stage::Topology; i <= Stage::Report; i++)
301             ASSERT(impl->hasRealized[i] == (i <= j));
302     }
303 }
304 
305 /**
306  * Test enabling and disabling forces.
307  */
308 
testDisabling()309 void testDisabling() {
310     MultibodySystem system;
311     SimbodyMatterSubsystem matter(system);
312     GeneralForceSubsystem forces(system);
313     Body::Rigid body(MassProperties(1.0, Vec3(0), Inertia(1)));
314     MobilizedBody::Free body1(matter.updGround(), Vec3(0), body, Vec3(0));
315     MobilizedBody::Free body2(matter.updGround(), Vec3(0), body, Vec3(0));
316     Force::TwoPointLinearSpring spring(forces, body1, Vec3(0), body2, Vec3(0), 2.0, 0.5);
317     Force::UniformGravity gravity(forces, matter, Vec3(0, -2.0, 0));
318 
319     // Create an initial state.
320 
321     State state = system.realizeTopology();
322     body1.setQToFitTranslation(state, Vec3(0, 1, 0));
323     body2.setQToFitTranslation(state, Vec3(1, 1, 0));
324 
325     // These are the contribution of each force to the energy and to the force on body1.
326 
327     Real springEnergy = 0.5*2.0*0.5*0.5;
328     SpatialVec springForce(Vec3(0), Vec3(2.0*0.5, 0, 0));
329     Real gravityEnergy = 2*2.0;
330     SpatialVec gravityForce(Vec3(0), Vec3(0, -2.0, 0));
331 
332     // Verify the force and energy for each combination of the forces being enabled or disabled.
333 
334     system.realize(state, Stage::Dynamics);
335     ASSERT_EQUAL(springEnergy+gravityEnergy, system.calcEnergy(state));
336     ASSERT((springForce+gravityForce-system.getRigidBodyForces(state, Stage::Dynamics)[1]).norm() < 1e-10);
337     ASSERT(!forces.isForceDisabled(state, gravity.getForceIndex()));
338     ASSERT(!forces.isForceDisabled(state, spring.getForceIndex()));
339     forces.setForceIsDisabled(state, spring.getForceIndex(), true);
340     system.realize(state, Stage::Dynamics);
341     ASSERT_EQUAL(gravityEnergy, system.calcEnergy(state));
342     ASSERT((gravityForce-system.getRigidBodyForces(state, Stage::Dynamics)[1]).norm() < 1e-10);
343     ASSERT(!forces.isForceDisabled(state, gravity.getForceIndex()));
344     ASSERT(forces.isForceDisabled(state, spring.getForceIndex()));
345     forces.setForceIsDisabled(state, gravity.getForceIndex(), true);
346     system.realize(state, Stage::Dynamics);
347     ASSERT_EQUAL(0, system.calcEnergy(state));
348     ASSERT((system.getRigidBodyForces(state, Stage::Dynamics)[1]).norm() < 1e-10);
349     ASSERT(forces.isForceDisabled(state, gravity.getForceIndex()));
350     ASSERT(forces.isForceDisabled(state, spring.getForceIndex()));
351     forces.setForceIsDisabled(state, spring.getForceIndex(), false);
352     system.realize(state, Stage::Dynamics);
353     ASSERT_EQUAL(springEnergy, system.calcEnergy(state));
354     ASSERT((springForce-system.getRigidBodyForces(state, Stage::Dynamics)[1]).norm() < 1e-10);
355     ASSERT(forces.isForceDisabled(state, gravity.getForceIndex()));
356     ASSERT(!forces.isForceDisabled(state, spring.getForceIndex()));
357 }
358 
main()359 int main() {
360     try {
361         testStandardForces();
362         testEnergyConservation();
363         testCustomRealization();
364         testDisabling();
365     }
366     catch(const std::exception& e) {
367         cout << "exception: " << e.what() << endl;
368         return 1;
369     }
370     cout << "Done" << endl;
371     return 0;
372 }
373