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