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