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) 2008-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 "SimTKcommon.h"
25 
26 #include "simbody/internal/common.h"
27 #include "simbody/internal/GeneralContactSubsystem.h"
28 #include "simbody/internal/MobilizedBody.h"
29 #include "ElasticFoundationForceImpl.h"
30 #include <map>
31 #include <set>
32 
33 namespace SimTK {
34 
35 SimTK_INSERT_DERIVED_HANDLE_DEFINITIONS(ElasticFoundationForce, ElasticFoundationForceImpl, Force);
36 
ElasticFoundationForce(GeneralForceSubsystem & forces,GeneralContactSubsystem & contacts,ContactSetIndex set)37 ElasticFoundationForce::ElasticFoundationForce(GeneralForceSubsystem& forces, GeneralContactSubsystem& contacts, ContactSetIndex set) :
38         Force(new ElasticFoundationForceImpl(contacts, set)) {
39     updImpl().setForceSubsystem(forces, forces.adoptForce(*this));
40 }
41 
setBodyParameters(ContactSurfaceIndex bodyIndex,Real stiffness,Real dissipation,Real staticFriction,Real dynamicFriction,Real viscousFriction)42 void ElasticFoundationForce::setBodyParameters
43    (ContactSurfaceIndex bodyIndex, Real stiffness, Real dissipation,
44     Real staticFriction, Real dynamicFriction, Real viscousFriction) {
45     updImpl().setBodyParameters(bodyIndex, stiffness, dissipation, staticFriction, dynamicFriction, viscousFriction);
46 }
47 
getTransitionVelocity() const48 Real ElasticFoundationForce::getTransitionVelocity() const {
49     return getImpl().transitionVelocity;
50 }
51 
setTransitionVelocity(Real v)52 void ElasticFoundationForce::setTransitionVelocity(Real v) {
53     updImpl().transitionVelocity = v;
54 }
55 
ElasticFoundationForceImpl(GeneralContactSubsystem & subsystem,ContactSetIndex set)56 ElasticFoundationForceImpl::ElasticFoundationForceImpl
57    (GeneralContactSubsystem& subsystem, ContactSetIndex set) :
58         subsystem(subsystem), set(set), transitionVelocity(Real(0.01)) {
59 }
60 
setBodyParameters(ContactSurfaceIndex bodyIndex,Real stiffness,Real dissipation,Real staticFriction,Real dynamicFriction,Real viscousFriction)61 void ElasticFoundationForceImpl::setBodyParameters
62    (ContactSurfaceIndex bodyIndex, Real stiffness, Real dissipation,
63     Real staticFriction, Real dynamicFriction, Real viscousFriction) {
64     SimTK_APIARGCHECK1(bodyIndex >= 0 && bodyIndex < subsystem.getNumBodies(set), "ElasticFoundationForceImpl", "setBodyParameters",
65             "Illegal body index: %d", (int)bodyIndex);
66     SimTK_APIARGCHECK1(subsystem.getBodyGeometry(set, bodyIndex).getTypeId()
67                         == ContactGeometry::TriangleMesh::classTypeId(),
68         "ElasticFoundationForceImpl", "setBodyParameters",
69         "Body %d is not a triangle mesh", (int)bodyIndex);
70     parameters[bodyIndex] =
71         Parameters(stiffness, dissipation, staticFriction, dynamicFriction,
72                    viscousFriction);
73     const ContactGeometry::TriangleMesh& mesh =
74         ContactGeometry::TriangleMesh::getAs
75                 (subsystem.getBodyGeometry(set, bodyIndex));
76     Parameters& param = parameters[bodyIndex];
77     param.springPosition.resize(mesh.getNumFaces());
78     param.springNormal.resize(mesh.getNumFaces());
79     param.springArea.resize(mesh.getNumFaces());
80     Vec2 uv(Real(1./3.), Real(1./3.));
81     for (int i = 0; i < (int) param.springPosition.size(); i++) {
82         param.springPosition[i] =
83            (mesh.getVertexPosition(mesh.getFaceVertex(i, 0))
84             +mesh.getVertexPosition(mesh.getFaceVertex(i, 1))
85             +mesh.getVertexPosition(mesh.getFaceVertex(i, 2)))/3;
86         param.springNormal[i] = -mesh.findNormalAtPoint(i, uv);
87         param.springArea[i] = mesh.getFaceArea(i);
88     }
89     subsystem.invalidateSubsystemTopologyCache();
90 }
91 
calcForce(const State & state,Vector_<SpatialVec> & bodyForces,Vector_<Vec3> & particleForces,Vector & mobilityForces) const92 void ElasticFoundationForceImpl::calcForce
93    (const State& state, Vector_<SpatialVec>& bodyForces,
94     Vector_<Vec3>& particleForces, Vector& mobilityForces) const
95 {
96     const Array_<Contact>& contacts = subsystem.getContacts(state, set);
97     Real& pe = Value<Real>::updDowncast
98                 (subsystem.updCacheEntry(state, energyCacheIndex));
99     pe = 0.0;
100     for (int i = 0; i < (int) contacts.size(); i++) {
101         std::map<ContactSurfaceIndex, Parameters>::const_iterator iter1 =
102             parameters.find(contacts[i].getSurface1());
103         std::map<ContactSurfaceIndex, Parameters>::const_iterator iter2 =
104             parameters.find(contacts[i].getSurface2());
105 
106         // If there are two meshes, scale each one's contributions by 50%.
107         Real areaScale = (iter1==parameters.end() || iter2==parameters.end())
108                          ? Real(1) : Real(0.5);
109 
110         if (iter1 != parameters.end()) {
111             const TriangleMeshContact& contact =
112                 static_cast<const TriangleMeshContact&>(contacts[i]);
113             processContact(state, contact.getSurface1(),
114                 contact.getSurface2(), iter1->second,
115                 contact.getSurface1Faces(), areaScale, bodyForces, pe);
116         }
117 
118         if (iter2 != parameters.end()) {
119             const TriangleMeshContact& contact =
120                 static_cast<const TriangleMeshContact&>(contacts[i]);
121             processContact(state, contact.getSurface2(),
122                 contact.getSurface1(), iter2->second,
123                 contact.getSurface2Faces(), areaScale, bodyForces, pe);
124         }
125     }
126 }
127 
processContact(const State & state,ContactSurfaceIndex meshIndex,ContactSurfaceIndex otherBodyIndex,const Parameters & param,const std::set<int> & insideFaces,Real areaScale,Vector_<SpatialVec> & bodyForces,Real & pe) const128 void ElasticFoundationForceImpl::processContact
129    (const State& state,
130     ContactSurfaceIndex meshIndex, ContactSurfaceIndex otherBodyIndex,
131     const Parameters& param, const std::set<int>& insideFaces,
132     Real areaScale, Vector_<SpatialVec>& bodyForces, Real& pe) const
133 {
134     const ContactGeometry& otherObject = subsystem.getBodyGeometry(set, otherBodyIndex);
135     const MobilizedBody& body1 = subsystem.getBody(set, meshIndex);
136     const MobilizedBody& body2 = subsystem.getBody(set, otherBodyIndex);
137     const Transform t1g = body1.getBodyTransform(state)*subsystem.getBodyTransform(set, meshIndex); // mesh to ground
138     const Transform t2g = body2.getBodyTransform(state)*subsystem.getBodyTransform(set, otherBodyIndex); // other object to ground
139     const Transform t12 = ~t2g*t1g; // mesh to other object
140 
141     // Loop over all the springs, and evaluate the force from each one.
142 
143     for (std::set<int>::const_iterator iter = insideFaces.begin();
144                                        iter != insideFaces.end(); ++iter) {
145         int face = *iter;
146         UnitVec3 normal;
147         bool inside;
148         Vec3 nearestPoint = otherObject.findNearestPoint(t12*param.springPosition[face], inside, normal);
149         if (!inside)
150             continue;
151 
152         // Find how much the spring is displaced.
153 
154         nearestPoint = t2g*nearestPoint;
155         const Vec3 springPosInGround = t1g*param.springPosition[face];
156         const Vec3 displacement = nearestPoint-springPosInGround;
157         const Real distance = displacement.norm();
158         if (distance == 0.0)
159             continue;
160         const Vec3 forceDir = displacement/distance;
161 
162         // Calculate the relative velocity of the two bodies at the contact point.
163 
164         const Vec3 station1 = body1.findStationAtGroundPoint(state, nearestPoint);
165         const Vec3 station2 = body2.findStationAtGroundPoint(state, nearestPoint);
166         const Vec3 v1 = body1.findStationVelocityInGround(state, station1);
167         const Vec3 v2 = body2.findStationVelocityInGround(state, station2);
168         const Vec3 v = v2-v1;
169         const Real vnormal = dot(v, forceDir);
170         const Vec3 vtangent = v-vnormal*forceDir;
171 
172         // Calculate the damping force.
173 
174         const Real area = areaScale * param.springArea[face];
175         const Real f = param.stiffness*area*distance*(1+param.dissipation*vnormal);
176         Vec3 force = (f > 0 ? f*forceDir : Vec3(0));
177 
178         // Calculate the friction force.
179 
180         const Real vslip = vtangent.norm();
181         if (f > 0 && vslip != 0) {
182             const Real vrel = vslip/transitionVelocity;
183             const Real ffriction =
184                 f*(std::min(vrel, Real(1))
185                  *(param.dynamicFriction+2*(param.staticFriction-param.dynamicFriction)
186                  /(1+vrel*vrel))+param.viscousFriction*vslip);
187             force += ffriction*vtangent/vslip;
188         }
189 
190         body1.applyForceToBodyPoint(state, station1, force, bodyForces);
191         body2.applyForceToBodyPoint(state, station2, -force, bodyForces);
192         pe += param.stiffness*area*displacement.normSqr()/2;
193     }
194 }
195 
calcPotentialEnergy(const State & state) const196 Real ElasticFoundationForceImpl::calcPotentialEnergy(const State& state) const {
197     return Value<Real>::downcast
198             (subsystem.getCacheEntry(state, energyCacheIndex));
199 }
200 
realizeTopology(State & state) const201 void ElasticFoundationForceImpl::realizeTopology(State& state) const {
202     energyCacheIndex = subsystem.allocateCacheEntry
203                         (state, Stage::Dynamics, new Value<Real>());
204 }
205 
206 
207 } // namespace SimTK
208 
209