1 /* -------------------------------------------------------------------------- *
2  *                      OpenSim:  HuntCrossleyForce.cpp                       *
3  * -------------------------------------------------------------------------- *
4  * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
5  * See http://opensim.stanford.edu and the NOTICE file for more information.  *
6  * OpenSim is developed at Stanford University and supported by the US        *
7  * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
8  * through the Warrior Web program.                                           *
9  *                                                                            *
10  * Copyright (c) 2005-2017 Stanford University and the Authors                *
11  * Author(s): Peter Eastman                                                   *
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 "HuntCrossleyForce.h"
25 #include "ContactGeometry.h"
26 #include "Model.h"
27 
28 #include "simbody/internal/HuntCrossleyForce.h"
29 
30 namespace OpenSim {
31 
32 //==============================================================================
33 //                          HUNT CROSSLEY FORCE
34 //==============================================================================
35 // Uses default (compiler-generated) destructor, copy constructor, copy
36 // assignment operator.
37 
38 // Default constructor.
HuntCrossleyForce()39 HuntCrossleyForce::HuntCrossleyForce()
40 {
41     constructProperties();
42 }
43 
44 // Take over ownership of supplied object.
HuntCrossleyForce(ContactParameters * params)45 HuntCrossleyForce::HuntCrossleyForce(ContactParameters* params)
46 {
47     constructProperties();
48     addContactParameters(params);
49 }
50 
51 
extendAddToSystem(SimTK::MultibodySystem & system) const52 void HuntCrossleyForce::extendAddToSystem(SimTK::MultibodySystem& system) const
53 {
54     Super::extendAddToSystem(system);
55 
56     const ContactParametersSet& contactParametersSet =
57         get_contact_parameters();
58     const double& transitionVelocity = get_transition_velocity();
59 
60     SimTK::GeneralContactSubsystem& contacts = system.updContactSubsystem();
61     SimTK::ContactSetIndex set = contacts.createContactSet();
62     SimTK::HuntCrossleyForce force(_model->updForceSubsystem(), contacts, set);
63     force.setTransitionVelocity(transitionVelocity);
64     for (int i = 0; i < contactParametersSet.getSize(); ++i)
65     {
66         ContactParameters& params = contactParametersSet.get(i);
67         for (int j = 0; j < params.getGeometry().size(); ++j) {
68             // TODO: Dependency of HuntCrossleyForce on ContactGeometry
69             // should be handled by Sockets.
70             const ContactGeometry* contactGeom = nullptr;
71             if (getModel().hasComponent<ContactGeometry>(params.getGeometry()[j]))
72                 contactGeom = &getModel().getComponent<ContactGeometry>(
73                     params.getGeometry()[j]);
74             else
75                 contactGeom = &getModel().getComponent<ContactGeometry>(
76                     "./contactgeometryset/" + params.getGeometry()[j]);
77 
78             const ContactGeometry& geom = *contactGeom;
79             // B: base Frame (Body or Ground)
80             // F: PhysicalFrame that this ContactGeometry is connected to
81             // P: the frame defined (relative to F) by the location and
82             //    orientation properties.
83             const auto& X_BF = geom.getFrame().findTransformInBaseFrame();
84             const auto& X_FP = geom.getTransform();
85             const auto X_BP = X_BF * X_FP;
86             contacts.addBody(set, geom.getFrame().getMobilizedBody(),
87                     geom.createSimTKContactGeometry(), X_BP);
88             force.setBodyParameters(
89                     SimTK::ContactSurfaceIndex(contacts.getNumBodies(set)-1),
90                     params.getStiffness(), params.getDissipation(),
91                     params.getStaticFriction(), params.getDynamicFriction(),
92                     params.getViscousFriction());
93         }
94     }
95 
96     // Beyond the const Component get the index so we can access the
97     // SimTK::Force later.
98     HuntCrossleyForce* mutableThis = const_cast<HuntCrossleyForce *>(this);
99     mutableThis->_index = force.getForceIndex();
100 }
101 
constructProperties()102 void HuntCrossleyForce::constructProperties()
103 {
104     constructProperty_contact_parameters(ContactParametersSet());
105     constructProperty_transition_velocity(0.01);
106 }
107 
108 HuntCrossleyForce::ContactParametersSet& HuntCrossleyForce::
updContactParametersSet()109 updContactParametersSet()
110 {
111     return upd_contact_parameters();
112 }
113 
114 const HuntCrossleyForce::ContactParametersSet& HuntCrossleyForce::
getContactParametersSet()115 getContactParametersSet()
116 {
117     return get_contact_parameters();
118 }
119 
120 void HuntCrossleyForce::
addContactParameters(HuntCrossleyForce::ContactParameters * params)121 addContactParameters(HuntCrossleyForce::ContactParameters* params)
122 {
123     updContactParametersSet().adoptAndAppend(params);
124 }
125 
getTransitionVelocity() const126 double HuntCrossleyForce::getTransitionVelocity() const
127 {
128     return get_transition_velocity();
129 }
130 
setTransitionVelocity(double velocity)131 void HuntCrossleyForce::setTransitionVelocity(double velocity)
132 {
133     set_transition_velocity(velocity);
134 }
135 /**
136  * The following set of functions are introduced for convenience to get/set values in HuntCrossleyForce::ContactParameters
137  * and for access in Matlab without exposing HuntCrossleyForce::ContactParameters. pending refactoring contact forces
138  */
getStiffness()139 double HuntCrossleyForce::getStiffness()  {
140     if (get_contact_parameters().getSize()==0)
141             updContactParametersSet().adoptAndAppend(
142                     new HuntCrossleyForce::ContactParameters());
143     return get_contact_parameters().get(0).getStiffness();
144 };
setStiffness(double stiffness)145 void HuntCrossleyForce::setStiffness(double stiffness) {
146     if (get_contact_parameters().getSize()==0)
147         updContactParametersSet().adoptAndAppend(
148                 new HuntCrossleyForce::ContactParameters());
149     upd_contact_parameters()[0].setStiffness(stiffness);
150 };
getDissipation()151 double HuntCrossleyForce::getDissipation()   {
152     if (get_contact_parameters().getSize()==0)
153             updContactParametersSet().adoptAndAppend(
154                     new HuntCrossleyForce::ContactParameters());
155     return get_contact_parameters().get(0).getDissipation();
156 };
setDissipation(double dissipation)157 void HuntCrossleyForce::setDissipation(double dissipation) {
158         if (get_contact_parameters().getSize()==0)
159             updContactParametersSet().adoptAndAppend(
160                     new HuntCrossleyForce::ContactParameters());
161         upd_contact_parameters()[0].setDissipation(dissipation);
162 };
getStaticFriction()163 double HuntCrossleyForce::getStaticFriction()  {
164     if (get_contact_parameters().getSize()==0)
165             updContactParametersSet().adoptAndAppend(
166                     new HuntCrossleyForce::ContactParameters());
167     return get_contact_parameters().get(0).getStaticFriction();
168 };
setStaticFriction(double friction)169 void HuntCrossleyForce::setStaticFriction(double friction) {
170     if (get_contact_parameters().getSize()==0)
171         updContactParametersSet().adoptAndAppend(
172                 new HuntCrossleyForce::ContactParameters());
173     upd_contact_parameters()[0].setStaticFriction(friction);
174 };
getDynamicFriction()175 double HuntCrossleyForce::getDynamicFriction()   {
176     if (get_contact_parameters().getSize()==0)
177             updContactParametersSet().adoptAndAppend(
178                     new HuntCrossleyForce::ContactParameters());
179     return get_contact_parameters().get(0).getDynamicFriction();
180 };
setDynamicFriction(double friction)181 void HuntCrossleyForce::setDynamicFriction(double friction) {
182     if (get_contact_parameters().getSize()==0)
183         updContactParametersSet().adoptAndAppend(
184                 new HuntCrossleyForce::ContactParameters());
185     upd_contact_parameters()[0].setDynamicFriction(friction);
186 };
getViscousFriction()187 double HuntCrossleyForce::getViscousFriction()   {
188     if (get_contact_parameters().getSize()==0)
189         updContactParametersSet().adoptAndAppend(
190                 new HuntCrossleyForce::ContactParameters());
191     return get_contact_parameters().get(0).getViscousFriction();
192 };
setViscousFriction(double friction)193 void HuntCrossleyForce::setViscousFriction(double friction) {
194     if (get_contact_parameters().getSize()==0)
195         updContactParametersSet().adoptAndAppend(
196                 new HuntCrossleyForce::ContactParameters());
197     upd_contact_parameters()[0].setViscousFriction(friction);
198 };
199 
addGeometry(const std::string & name)200 void HuntCrossleyForce::addGeometry(const std::string& name)
201 {
202     if (get_contact_parameters().getSize()==0)
203         updContactParametersSet().adoptAndAppend(
204                 new HuntCrossleyForce::ContactParameters());
205     upd_contact_parameters()[0].addGeometry(name);
206 }
207 //==============================================================================
208 //                HUNT CROSSLEY FORCE :: CONTACT PARAMETERS
209 //==============================================================================
210 
211 // Default constructor.
ContactParameters()212 HuntCrossleyForce::ContactParameters::ContactParameters()
213 {
214     constructProperties();
215 }
216 
217 // Constructor specifying material properties.
ContactParameters(double stiffness,double dissipation,double staticFriction,double dynamicFriction,double viscousFriction)218 HuntCrossleyForce::ContactParameters::ContactParameters
219    (double stiffness, double dissipation, double staticFriction,
220     double dynamicFriction, double viscousFriction)
221 {
222     constructProperties();
223     set_stiffness(stiffness);
224     set_dissipation(dissipation);
225     set_static_friction(staticFriction);
226     set_dynamic_friction(dynamicFriction);
227     set_viscous_friction(viscousFriction);
228 }
229 
230 
constructProperties()231 void HuntCrossleyForce::ContactParameters::constructProperties()
232 {
233     constructProperty_geometry(); // a list of strings
234     constructProperty_stiffness(0.0);
235     constructProperty_dissipation(0.0);
236     constructProperty_static_friction(0.0);
237     constructProperty_dynamic_friction(0.0);
238     constructProperty_viscous_friction(0.0);
239 }
240 
getGeometry() const241 const Property<std::string>& HuntCrossleyForce::ContactParameters::getGeometry() const
242 {
243     return getProperty_geometry();
244 }
245 
updGeometry()246 Property<std::string>& HuntCrossleyForce::ContactParameters::updGeometry()
247 {
248     return updProperty_geometry();
249 }
250 
addGeometry(const std::string & name)251 void HuntCrossleyForce::ContactParameters::addGeometry(const std::string& name)
252 {
253     updGeometry().appendValue(name);
254 }
255 
getStiffness() const256 double HuntCrossleyForce::ContactParameters::getStiffness() const
257 {
258     return get_stiffness();
259 }
260 
setStiffness(double stiffness)261 void HuntCrossleyForce::ContactParameters::setStiffness(double stiffness)
262 {
263     set_stiffness(stiffness);
264 }
265 
getDissipation() const266 double HuntCrossleyForce::ContactParameters::getDissipation() const
267 {
268     return get_dissipation();
269 }
270 
setDissipation(double dissipation)271 void HuntCrossleyForce::ContactParameters::setDissipation(double dissipation)
272 {
273     set_dissipation(dissipation);
274 }
275 
getStaticFriction() const276 double HuntCrossleyForce::ContactParameters::getStaticFriction() const
277 {
278     return get_static_friction();
279 }
280 
setStaticFriction(double friction)281 void HuntCrossleyForce::ContactParameters::setStaticFriction(double friction)
282 {
283     set_static_friction(friction);
284 }
285 
getDynamicFriction() const286 double HuntCrossleyForce::ContactParameters::getDynamicFriction() const
287 {
288     return get_dynamic_friction();
289 }
290 
setDynamicFriction(double friction)291 void HuntCrossleyForce::ContactParameters::setDynamicFriction(double friction)
292 {
293     set_dynamic_friction(friction);
294 }
295 
getViscousFriction() const296 double HuntCrossleyForce::ContactParameters::getViscousFriction() const
297 {
298     return get_viscous_friction();
299 }
300 
setViscousFriction(double friction)301 void HuntCrossleyForce::ContactParameters::setViscousFriction(double friction)
302 {
303     set_viscous_friction(friction);
304 }
305 
setNull()306 void HuntCrossleyForce::ContactParametersSet::setNull()
307 {
308     setAuthors("Peter Eastman");
309 }
310 
ContactParametersSet()311 HuntCrossleyForce::ContactParametersSet::ContactParametersSet()
312 {
313     setNull();
314 }
315 
316 
317 //=============================================================================
318 // Reporting
319 //=============================================================================
320 /* Provide names of the quantities (column labels) of the force value(s)
321  * to be reported. */
getRecordLabels() const322 OpenSim::Array<std::string> HuntCrossleyForce::getRecordLabels() const
323 {
324     OpenSim::Array<std::string> labels("");
325 
326     const ContactParametersSet& contactParametersSet =
327         get_contact_parameters();
328 
329     for (int i = 0; i < contactParametersSet.getSize(); ++i)
330     {
331         ContactParameters& params = contactParametersSet.get(i);
332         for (int j = 0; j < params.getGeometry().size(); ++j)
333         {
334             // TODO: Dependency of HuntCrossleyForce on ContactGeometry
335             // should be handled by Sockets.
336             const ContactGeometry* contactGeom = nullptr;
337             if (getModel().hasComponent<ContactGeometry>(params.getGeometry()[j]))
338                 contactGeom = &getModel().getComponent<ContactGeometry>(
339                     params.getGeometry()[j]);
340             else
341                 contactGeom = &getModel().getComponent<ContactGeometry>(
342                     "./contactgeometryset/" + params.getGeometry()[j]);
343 
344             const ContactGeometry& geom = *contactGeom;
345             std::string frameName = geom.getFrame().getName();
346             labels.append(getName()+"."+frameName+".force.X");
347             labels.append(getName()+"."+frameName+".force.Y");
348             labels.append(getName()+"."+frameName+".force.Z");
349             labels.append(getName()+"."+frameName+".torque.X");
350             labels.append(getName()+"."+frameName+".torque.Y");
351             labels.append(getName()+"."+frameName+".torque.Z");
352         }
353     }
354 
355     return labels;
356 }
357 /**
358  * Provide the value(s) to be reported that correspond to the labels
359  */
360 OpenSim::Array<double> HuntCrossleyForce::
getRecordValues(const SimTK::State & state) const361 getRecordValues(const SimTK::State& state) const
362 {
363     OpenSim::Array<double> values(1);
364 
365     const ContactParametersSet& contactParametersSet =
366         get_contact_parameters();
367 
368     const auto& forceSubsys = _model->getForceSubsystem();
369     const SimTK::Force& abstractForce = forceSubsys.getForce(_index);
370     const auto& simtkForce = (SimTK::HuntCrossleyForce &)(abstractForce);
371 
372     SimTK::Vector_<SimTK::SpatialVec> bodyForces(0);
373     SimTK::Vector_<SimTK::Vec3> particleForces(0);
374     SimTK::Vector mobilityForces(0);
375 
376     //get the net force added to the system contributed by the Spring
377     simtkForce.calcForceContribution(state, bodyForces, particleForces,
378                                      mobilityForces);
379 
380     for (int i = 0; i < contactParametersSet.getSize(); ++i)
381     {
382         ContactParameters& params = contactParametersSet.get(i);
383         for (int j = 0; j < params.getGeometry().size(); ++j)
384         {
385             // TODO: Dependency of HuntCrossleyForce on ContactGeometry
386             // should be handled by Sockets.
387             const ContactGeometry* contactGeom = nullptr;
388             if (getModel().hasComponent<ContactGeometry>(params.getGeometry()[j]))
389                 contactGeom = &getModel().getComponent<ContactGeometry>(
390                     params.getGeometry()[j]);
391             else
392                 contactGeom = &getModel().getComponent<ContactGeometry>(
393                     "./contactgeometryset/" + params.getGeometry()[j]);
394 
395             const ContactGeometry& geom = *contactGeom;
396 
397             const auto& mbi = geom.getFrame().getMobilizedBodyIndex();
398             const auto& thisBodyForce = bodyForces(mbi);
399             SimTK::Vec3 forces = thisBodyForce[1];
400             SimTK::Vec3 torques = thisBodyForce[0];
401 
402             values.append(3, &forces[0]);
403             values.append(3, &torques[0]);
404         }
405     }
406 
407     return values;
408 }
409 
410 }// end of namespace OpenSim
411