forked from opensim-org/opensim-core
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathFunctionBasedPath.cpp
More file actions
321 lines (266 loc) · 12 KB
/
Copy pathFunctionBasedPath.cpp
File metadata and controls
321 lines (266 loc) · 12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
#include "FunctionBasedPath.h"
#include <OpenSim/Common/Component.h>
#include <OpenSim/Simulation/Model/Model.h>
#include <OpenSim/Simulation/SimbodyEngine/Coordinate.h>
#include <sstream>
namespace {
OpenSim::Exception InvalidFunctionError(std::string parentPath, char const* funcName)
{
std::stringstream ss;
ss << parentPath << ':' << funcName << ": cannot call underlying function: it has not been initialized yet: ensure you have assigned a function to this FunctionBasedPath and finalized its properites";
return OpenSim::Exception{ss.str()};
}
// stub function that will throw an exception with an information message if called
//
// used as a stand-in for the object state where the caller has allocated a FunctionBasedPath
// but hasn't set its underlying function yet.
class ThrowingSimTKFunction final : public SimTK::Function {
std::string m_ParentPath;
public:
explicit ThrowingSimTKFunction(const std::string& parentPath) :
m_ParentPath{parentPath}
{
}
double calcValue(const SimTK::Vector&) const override
{
throw InvalidFunctionError(m_ParentPath, __func__);
}
double calcDerivative(const SimTK::Array_<int>&, const SimTK::Vector&) const override
{
throw InvalidFunctionError(m_ParentPath, __func__);
}
int getArgumentSize() const override
{
throw InvalidFunctionError(m_ParentPath, __func__);
}
int getMaxDerivativeOrder() const override
{
throw InvalidFunctionError(m_ParentPath, __func__);
}
};
// stub function that will throw an exception with an information message if called
//
// used as a stand-in for the object state where the caller has allocated a FunctionBasedPath
// but hasn't set its underlying function yet.
class ThrowingOpenSimFunction final : public OpenSim::Function {
OpenSim_DECLARE_CONCRETE_OBJECT(ThrowingOpenSimFunction, OpenSim::Function)
std::string m_ParentPath;
public:
explicit ThrowingOpenSimFunction(const std::string& parentPath) :
m_ParentPath{parentPath}
{
}
double calcValue(const SimTK::Vector&) const override
{
throw InvalidFunctionError(m_ParentPath, __func__);
}
double calcDerivative(const std::vector<int>&, const SimTK::Vector&) const override
{
throw InvalidFunctionError(m_ParentPath, __func__);
}
int getArgumentSize() const override
{
throw InvalidFunctionError(m_ParentPath, __func__);
}
int getMaxDerivativeOrder() const override
{
throw InvalidFunctionError(m_ParentPath, __func__);
}
SimTK::Function* createSimTKFunction() const override
{
return new ThrowingSimTKFunction{m_ParentPath};
}
};
void ValidatePropertiesOrThrow(const OpenSim::FunctionBasedPath& parent,
const OpenSim::Property<OpenSim::Function>& funcProp,
const OpenSim::Property<std::string>& coordPathsProp)
{
if (funcProp.size() == 0) {
throw OpenSim::Exception(__FILE__, __LINE__, __func__, parent, "A FunctionBasedPath's function property has not been set");
}
if (funcProp.getValue().getMaxDerivativeOrder() < 1) {
throw OpenSim::Exception(__FILE__, __LINE__, __func__, parent, "The function provided to a FunctionBasedPath is not differentiable: it cannot be used as a path function");
}
if (funcProp.getValue().getArgumentSize() != coordPathsProp.size()) {
std::stringstream ss;
ss << "The number of coordinate paths provided (" << coordPathsProp.size() << ") does not match the number of arguments the function, " << funcProp.getValue().getName() << ", takes (" << funcProp.getValue().getArgumentSize() << ")";
throw OpenSim::Exception(__FILE__, __LINE__, __func__, parent, ss.str());
}
int nCoords = coordPathsProp.size();
for (int i = 0; i < nCoords; ++i) {
const std::string& coordPath = coordPathsProp.getValue(i);
if (coordPath.empty()) {
throw OpenSim::Exception(__FILE__, __LINE__, __func__, parent, "An empty coordinate string was provided to a FunctionBasedPath: all coordinate strings must be absolute paths to coordinates within the model");
}
}
}
}
OpenSim::FunctionBasedPath::FunctionBasedPath() : GeometryPath{}
{
constructProperty_PathFunction(ThrowingOpenSimFunction{this->getAbsolutePathString()});
constructProperty_Coordinates();
}
OpenSim::FunctionBasedPath::FunctionBasedPath(const FunctionBasedPath&) = default;
OpenSim::FunctionBasedPath::FunctionBasedPath(const OpenSim::Function& func, std::vector<std::string> coordAbsPaths)
{
constructProperty_PathFunction(func);
constructProperty_Coordinates();
for (std::string& coordAbsPath : coordAbsPaths) {
updProperty_Coordinates().appendValue(std::move(coordAbsPath));
}
ValidatePropertiesOrThrow(*this, getProperty_PathFunction(), getProperty_Coordinates());
}
OpenSim::FunctionBasedPath::~FunctionBasedPath() noexcept = default;
OpenSim::FunctionBasedPath& OpenSim::FunctionBasedPath::operator=(const FunctionBasedPath&) = default;
SimTK::Vec3 OpenSim::FunctionBasedPath::getColor(const SimTK::State& s) const
{
return getCacheVariableValue(s, _colorCV);
}
void OpenSim::FunctionBasedPath::setColor(const SimTK::State& s, const SimTK::Vec3& color) const
{
setCacheVariableValue(s, _colorCV, color);
}
double OpenSim::FunctionBasedPath::getLength(const SimTK::State& s) const
{
if (isCacheVariableValid(s, _lengthCV)) {
return getCacheVariableValue(s, _lengthCV);
}
const SimTK::Vector& args = calcFunctionArguments(s);
double v = getProperty_PathFunction().getValue().calcValue(args);
setCacheVariableValue(s, _lengthCV, v);
return v;
}
double OpenSim::FunctionBasedPath::getLengtheningSpeed(const SimTK::State& s) const
{
if (isCacheVariableValid(s, _speedCV)) {
return getCacheVariableValue(s, _speedCV);
}
// the lengthening speed is the sum of: pathDeriv (w.r.t. coord) * coordinateSpeed
_derivativeOrderBuffer.resize(1);
const SimTK::Vector& args = calcFunctionArguments(s);
int nCoords = getProperty_Coordinates().size();
double acc = 0.0;
for (int i = 0; i < nCoords; ++i) {
const std::string& coordAbsPath = get_Coordinates(i);
const OpenSim::Coordinate& coord = getRoot().getComponent<OpenSim::Coordinate>(coordAbsPath);
_derivativeOrderBuffer[0] = i;
double deriv = get_PathFunction().calcDerivative(_derivativeOrderBuffer, args);
double coordSpeed = coord.getSpeedValue(s);
acc = acc + deriv*coordSpeed;
}
setCacheVariableValue(s, _speedCV, acc);
return acc;
}
void OpenSim::FunctionBasedPath::addInEquivalentForces(const SimTK::State& state, double tension, SimTK::Vector_<SimTK::SpatialVec>&, SimTK::Vector& mobilityForces) const
{
const SimTK::SimbodyMatterSubsystem& matter = getModel().getMatterSubsystem();
const SimTK::Vector args = calcFunctionArguments(state);
_derivativeOrderBuffer.resize(1);
int nCoords = getProperty_Coordinates().size();
for (int i = 0; i < nCoords; ++i) {
const std::string& coordAbsPath = get_Coordinates(i);
const OpenSim::Coordinate& coord = getRoot().getComponent<OpenSim::Coordinate>(coordAbsPath);
_derivativeOrderBuffer[0] = i;
double momentArm = get_PathFunction().calcDerivative(_derivativeOrderBuffer, args);
double torque = -tension*momentArm;
matter.addInMobilityForce(state,
SimTK::MobilizedBodyIndex(coord.getBodyIndex()),
SimTK::MobilizerUIndex(coord.getMobilizerQIndex()),
torque,
mobilityForces);
}
}
double OpenSim::FunctionBasedPath::computeMomentArm(const SimTK::State& st, const Coordinate& coord) const
{
// the moment arm of a path with respect to a coordinate is the path's
// length derivative with respect to the coordinate
int coordIndex = indexOfCoordinate(coord);
if (coordIndex == -1) {
// the provided coordinate does not affect this path, so it
// has no moment arm w.r.t. it
return 0.0;
}
_derivativeOrderBuffer.resize(1);
_derivativeOrderBuffer[0] = coordIndex;
const SimTK::Vector& args = calcFunctionArguments(st);
return getProperty_PathFunction().getValue().calcDerivative(_derivativeOrderBuffer, args);
}
void OpenSim::FunctionBasedPath::extendFinalizeFromProperties()
{
ValidatePropertiesOrThrow(*this, getProperty_PathFunction(), getProperty_Coordinates());
}
void OpenSim::FunctionBasedPath::extendAddToSystem(SimTK::MultibodySystem& system) const
{
Super::extendAddToSystem(system);
// Allocate cache entries to save the current length and speed(=d/dt length)
// of the path in the cache. Length depends only on q's so will be valid
// after Position stage, speed requires u's also so valid at Velocity stage.
this->_lengthCV = addCacheVariable("length", 0.0, SimTK::Stage::Position);
this->_speedCV = addCacheVariable("speed", 0.0, SimTK::Stage::Velocity);
// We consider this cache entry valid any time after it has been created
// and first marked valid, and we won't ever invalidate it.
this->_colorCV = addCacheVariable("color", get_Appearance().get_color(), SimTK::Stage::Topology);
}
void OpenSim::FunctionBasedPath::extendInitStateFromProperties(SimTK::State& s) const
{
Super::extendInitStateFromProperties(s);
markCacheVariableValid(s, _colorCV);
}
void OpenSim::FunctionBasedPath::extendFinalizeConnections(OpenSim::Component& root)
{
// populate pointer-based coordinate lookups
//
// the reason this isn't done in `extendFinalizeFromProperties` is because the
// not-yet-property-finalized Model hasn't necessarily "connected" to the
// coordinates that the coordinate files refer to, so the implementation
// can't lookup the `OpenSim::Coordinate*` pointers during that phase
// Allow (model) component to include its own subcomponents
// before calling the base method which automatically invokes
// connect all the subcomponents.
{
Model* model = dynamic_cast<Model*>(&root);
if (model) {
connectToModel(*model);
}
}
int nCoords = getProperty_Coordinates().size();
for (int i = 0; i < nCoords; ++i) {
root.getComponent(get_Coordinates(i)); // should throw if missing
}
}
const SimTK::Vector& OpenSim::FunctionBasedPath::calcFunctionArguments(const SimTK::State& st) const
{
int nargs = getProperty_Coordinates().size();
_functionArgsBuffer.resize(nargs);
for (int i = 0; i < nargs; ++i) {
// HACK: this lookup is horrible
const std::string& coordName = get_Coordinates(i);
const OpenSim::Coordinate& coord = getRoot().getComponent<OpenSim::Coordinate>(coordName);
_functionArgsBuffer[i] = coord.getValue(st);
}
return _functionArgsBuffer;
}
int OpenSim::FunctionBasedPath::indexOfCoordinate(const Coordinate& c) const
{
std::string absPath = c.getAbsolutePathString();
int nCoords = getProperty_Coordinates().size();
for (int i = 0; i < nCoords; ++i) {
const std::string& coordAbsPath = get_Coordinates(i);
if (coordAbsPath == absPath) {
return i;
}
}
return -1;
}
/* TODO: in extendFinalizeConnections
*
// ensure that the OpenSim::Coordinate* pointers held in Impl are up-to-date
//
// the pointers are there to reduce runtime path lookups
static void Impl_SetCoordinatePointersFromCoordinatePaths(JorisFBP& impl,
OpenSim::Component const& c) {
for (size_t i = 0; i < impl.coords.size(); ++i) {
impl.coords[i] = &c.getComponent<OpenSim::Coordinate>(impl.coordAbsPaths[i]);
}
}
*/