1 #ifndef SimTK_SIMBODY_ASSEMBLER_H_
2 #define SimTK_SIMBODY_ASSEMBLER_H_
149 typedef std::set<MobilizedBodyIndex> LockedMobilizers;
150 typedef std::set<MobilizerQIndex> QSet;
151 typedef std::map<MobilizedBodyIndex, QSet> LockedQs;
152 typedef std::map<MobilizerQIndex, Vec2> QRanges;
153 typedef std::map<MobilizedBodyIndex, QRanges> RestrictedQs;
163 class AssembleFailed;
199 "Assembler::setTolerance()",
"The requested error tolerance %g"
200 " is illegal; we require 0 <= tolerance, with 0 indicating that"
201 " the default tolerance (accuracy/10) is to be used.", tolerance);
202 this->tolerance = tolerance;
210 return tolerance > 0 ? tolerance
211 : (accuracy > 0 ? accuracy/10 :
Real(0.1)/OODefaultAccuracy);
225 "Assembler::setAccuracy()",
"The requested accuracy %g is illegal;"
226 " we require 0 <= accuracy < 1, with 0 indicating that the default"
227 " accuracy (%g) is to be used.",
Real(1)/OODefaultAccuracy, accuracy);
228 this->accuracy = accuracy;
234 {
return accuracy > 0 ? accuracy :
Real(1)/OODefaultAccuracy; }
244 { assert(systemConstraints.isValid());
245 setAssemblyConditionWeight(systemConstraints,weight);
251 Real getSystemConstraintsWeight()
const
252 { assert(systemConstraints.isValid());
253 return getAssemblyConditionWeight(systemConstraints); }
261 Assembler& setAssemblyConditionWeight(AssemblyConditionIndex condition,
264 "Assembler::setAssemblyConditionWeight()");
266 "Illegal weight %g; weight must be nonnegative.", weight);
268 weights[condition] = weight;
280 "Assembler::getAssemblyConditionWeight()");
281 return weights[condition];
288 AssemblyConditionIndex
298 AssemblyConditionIndex
309 getMatterSubsystem().convertToEulerAngles(state, internalState);
319 void initialize()
const;
356 setInternalState(state);
357 Real achievedCost = assemble();
358 updateFromInternalState(state);
366 Real calcCurrentGoal()
const;
375 Real calcCurrentErrorNorm()
const;
384 if (!getMatterSubsystem().getUseEulerAngles(state)) {
386 getMatterSubsystem().convertToQuaternions(getInternalState(),
390 state.
updQ() = getInternalState().getQ();
406 { uninitialize(); userLockedMobilizers.insert(mbx); }
413 { uninitialize(); userLockedMobilizers.erase(mbx); }
427 { uninitialize(); userLockedQs[mbx].insert(qx); }
434 { LockedQs::iterator p = userLockedQs.find(mbx);
435 if (p == userLockedQs.end())
return;
436 QSet& qs = p->second;
440 userLockedQs.erase(p);
452 "The given range [%g,%g] is illegal because the lower bound is"
453 " greater than the upper bound.", lowerBound, upperBound);
455 { unrestrictQ(mbx,qx);
return; }
457 userRestrictedQs[mbx][qx] =
Vec2(lowerBound,upperBound);
466 { RestrictedQs::iterator p = userRestrictedQs.find(mbx);
467 if (p == userRestrictedQs.end())
return;
468 QRanges& qranges = p->second;
469 if (qranges.erase(qx)) {
472 userRestrictedQs.erase(p);
487 int getNumGoalEvals()
const;
489 int getNumErrorEvals()
const;
491 int getNumGoalGradientEvals()
const;
493 int getNumErrorJacobianEvals()
const;
496 int getNumAssemblySteps()
const;
499 int getNumInitializations()
const;
503 void resetStats()
const;
514 void setForceNumericalGradient(
bool yesno)
515 { forceNumericalGradient = yesno; }
518 void setForceNumericalJacobian(
bool yesno)
519 { forceNumericalJacobian = yesno; }
526 void setUseRMSErrorNorm(
bool yesno)
527 { useRMSErrorNorm = yesno; }
537 void uninitialize()
const;
554 reporters.push_back(&reporter);
560 int getNumFreeQs()
const
561 {
return freeQ2Q.size(); }
566 QIndex getQIndexOfFreeQ(FreeQIndex freeQIndex)
const
567 {
return freeQ2Q[freeQIndex]; }
573 FreeQIndex getFreeQIndexOfQ(
QIndex qx)
const
574 {
return q2FreeQ[qx]; }
580 else return Vec2(lower[freeQIndex], upper[freeQIndex]);
603 void setInternalStateFromFreeQs(
const Vector& freeQs) {
604 assert(freeQs.
size() == getNumFreeQs());
605 Vector& q = internalState.updQ();
606 for (FreeQIndex fx(0); fx < getNumFreeQs(); ++fx)
607 q[getQIndexOfFreeQ(fx)] = freeQs[fx];
611 Vector getFreeQsFromInternalState()
const {
612 Vector freeQs(getNumFreeQs());
613 const Vector& q = internalState.getQ();
614 for (FreeQIndex fx(0); fx < getNumFreeQs(); ++fx)
615 freeQs[fx] = q[getQIndexOfFreeQ(fx)];
619 void reinitializeWithExtraQsLocked
620 (
const Array_<QIndex>& toBeLocked)
const;
627 const MultibodySystem& system;
628 Array_<const EventReporter*> reporters;
631 static const int OODefaultAccuracy = 1000;
634 bool forceNumericalGradient;
635 bool forceNumericalJacobian;
636 bool useRMSErrorNorm;
643 LockedMobilizers userLockedMobilizers;
646 LockedQs userLockedQs;
648 RestrictedQs userRestrictedQs;
653 Array_<AssemblyCondition*,AssemblyConditionIndex>
655 Array_<Real,AssemblyConditionIndex> weights;
660 AssemblyConditionIndex systemConstraints;
664 mutable bool alreadyInitialized;
667 mutable Array_<QIndex> extraQsLocked;
670 mutable std::set<QIndex> lockedQs;
671 mutable Array_<FreeQIndex,QIndex> q2FreeQ;
672 mutable Array_<QIndex,FreeQIndex> freeQ2Q;
674 mutable Vector lower, upper;
677 mutable Array_<AssemblyConditionIndex> errors;
678 mutable Array_<int> nTermsPerError;
679 mutable Array_<AssemblyConditionIndex> goals;
681 class AssemblerSystem;
682 mutable AssemblerSystem* asmSys;
683 mutable Optimizer* optimizer;
685 mutable int nAssemblySteps;
686 mutable int nInitializations;
688 friend class AssemblerSystem;
708 : name(name), assembler(0) {}
745 virtual int calcErrorJacobian(
const State& state,
Matrix& jacobian)
const
755 virtual int getNumErrors(
const State& state)
const
757 const int status = calcErrors(state, err);
761 "The default implementation of getNumErrors() depends on"
762 " calcErrors() but that method was not implemented for assembly"
763 " condition '%s'.", name.c_str());
765 "The default implementation of getNumErrors() uses calcErrors()"
766 " which returned status %d (assembly condition '%s').",
767 status, name.c_str());
775 virtual int calcGoal(
const State& state,
Real& goal)
const
777 const int status = calcErrors(state, err);
782 "The default implementation of calcGoal() depends on calcErrors()"
783 " but that method was not implemented for assembly condition '%s'.",
786 "The default implementation of calcGoal() uses calcErrors() which"
787 " returned status %d (assembly condition '%s').",
788 status, name.c_str());
798 virtual int calcGoalGradient(
const State& state,
Vector& gradient)
const
802 const char*
getName()
const {
return name.c_str();}
811 { assert(assembler);
return *assembler;}
815 AssemblyConditionIndex getAssemblyConditionIndex()
const
816 {
return myAssemblyConditionIndex; }
829 QIndex getQIndexOfFreeQ(Assembler::FreeQIndex fx)
const
830 {
return getAssembler().getQIndexOfFreeQ(fx); }
834 Assembler::FreeQIndex getFreeQIndexOfQ(
QIndex qx)
const
835 {
return getAssembler().getFreeQIndexOfQ(qx); }
838 {
return getAssembler().getMultibodySystem(); }
842 {
return getMultibodySystem().getMatterSubsystem(); }
848 if (isInAssembler()) getAssembler().initialize();
849 else initializeCondition();
857 if (isInAssembler()) getAssembler().uninitialize();
858 else uninitializeCondition();
867 void setAssembler(
const Assembler& assembler, AssemblyConditionIndex acx) {
868 assert(!this->assembler);
869 this->assembler = &assembler;
870 this->myAssemblyConditionIndex = acx;
875 AssemblyConditionIndex myAssemblyConditionIndex;
894 mobodIndex(mbx), qIndex(qx), value(value) {}
909 error[0] = mobod.
getOneQ(state, qIndex) - value;
926 if (thisFreeIx.isValid())
953 if (thisFreeIx.isValid())
954 grad[thisFreeIx] = mobod.
getOneQ(state, qIndex) - value;
1018 const Vec3& markerInB,
Real weight = 1)
1019 : name(name), bodyB(bodyB), markerInB(markerInB), weight(weight)
1020 { assert(weight >= 0); }
1023 : name(
""), bodyB(bodyB), markerInB(markerInB), weight(weight)
1024 { assert(weight >= 0); }
1073 const Vec3& markerInB,
Real weight=1)
1075 "Markers::addMarker()",
"Illegal marker weight %g.", weight);
1076 uninitializeAssembler();
1078 observation2marker.clear(); marker2observation.clear();
1079 observations.clear();
1080 const MarkerIx ix(markers.size());
1085 std::pair< std::map<String,MarkerIx>::iterator,
bool >
1086 found = markersByName.insert(std::make_pair(nm,ix));
1088 "Markers::addMarker()",
1089 "Marker name '%s' was already use for Marker %d.",
1090 nm.c_str(), (int)found.first->second);
1092 markers.push_back(Marker(nm,bodyB,markerInB,weight));
1102 {
return addMarker(
"", bodyB, markerInB, weight); }
1126 uninitializeAssembler();
1127 if (observationOrder.
empty()) {
1128 observation2marker.resize(markers.size());
1129 for (MarkerIx mx(0); mx < markers.size(); ++mx)
1130 observation2marker[ObservationIx(mx)] = mx;
1132 observation2marker = observationOrder;
1133 marker2observation.
clear();
1135 marker2observation.resize(observation2marker.size());
1136 for (ObservationIx ox(0); ox < observation2marker.size(); ++ox) {
1137 const MarkerIx mx = observation2marker[ox];
1138 if (!mx.isValid())
continue;
1140 if (marker2observation.size() <= mx)
1141 marker2observation.resize(mx+1);
1143 "Markers::defineObservationOrder()",
1144 "An attempt was made to associate Marker %d (%s) with"
1145 " Observations %d and %d; only one Observation per Marker"
1147 (
int)mx, getMarkerName(mx).c_str(),
1148 (
int)marker2observation[mx], (
int)ox);
1150 marker2observation[mx] = ox;
1153 observations.clear();
1154 observations.resize(observation2marker.size(),
Vec3(
NaN));
1164 for (ObservationIx ox(0); ox < observationOrder.
size(); ++ox)
1165 markerIxs[ox] = getMarkerIx(observationOrder[ox]);
1166 defineObservationOrder(markerIxs); }
1170 void defineObservationOrder(
const std::vector<String>& observationOrder)
1178 defineObservationOrder(observations); }
1182 void defineObservationOrder(
const std::vector<std::string>& observationOrder)
1184 defineObservationOrder(observations); }
1187 void defineObservationOrder(
int n,
const char*
const observationOrder[])
1189 for (ObservationIx ox(0); ox < n; ++ox)
1190 markerIxs[ox] = getMarkerIx(
String(observationOrder[ox]));
1191 defineObservationOrder(markerIxs); }
1212 {
return markers[ix].name; }
1216 const MarkerIx getMarkerIx(
const String& name)
1217 { std::map<String,MarkerIx>::const_iterator p = markersByName.find(name);
1218 return p == markersByName.end() ? MarkerIx() : p->second; }
1223 {
return markers[mx].weight; }
1227 {
return markers[mx].bodyB; }
1230 const Vec3& getMarkerStation(MarkerIx mx)
const
1231 {
return markers[mx].markerInB; }
1245 ObservationIx getObservationIxForMarker(MarkerIx mx)
const
1246 {
return marker2observation[mx]; }
1250 bool hasObservation(MarkerIx mx)
const
1251 {
return getObservationIxForMarker(mx).isValid(); }
1258 MarkerIx getMarkerIxForObservation(ObservationIx ox)
const
1259 {
return observation2marker[ox]; }
1263 bool hasMarker(ObservationIx ox)
const
1264 {
return getMarkerIxForObservation(ox).isValid();}
1273 "This method can't be called until the Markers object has been"
1274 " adopted by an Assembler.");
1275 initializeAssembler();
1276 PerBodyMarkers::const_iterator bodyp = bodiesWithMarkers.find(mbx);
1277 return bodyp == bodiesWithMarkers.end() ? empty : bodyp->second;
1293 void moveOneObservation(ObservationIx ox,
const Vec3& observation)
1295 "There are currently no observations defined. Either the Assembler"
1296 " needs to be initialized to get the default observation order, or you"
1297 " should call defineObservationOrder() explicitly.");
1299 "Assembler::moveOneObservation()",
"ObservationIx %d is invalid or"
1300 " out of range; there are %d observations currently defined. Use"
1301 " defineObservationOrder() to specify the set of observations and how"
1302 " they correspond to markers.",
1303 (int)ox, (
int)observations.size());
1304 observations[ox] = observation;
1318 "Markers::moveAllObservations()",
1319 "Number of observations provided (%d) differs from the number of"
1320 " observations (%d) last defined with defineObservationOrder().",
1321 observations.
size(), observation2marker.size());
1322 this->observations = observations; }
1335 "Markers::changeMarkerWeight()",
"Illegal marker weight %g.", weight);
1337 Marker& marker = markers[mx];
1338 if (marker.weight == weight)
1341 if (marker.weight == 0 || weight == 0)
1342 uninitializeAssembler();
1344 marker.weight = weight;
1359 {
return observations; }
1364 Vec3 findCurrentMarkerLocation(MarkerIx mx)
const;
1375 Real findCurrentMarkerError(MarkerIx mx)
const
1376 {
return std::sqrt(findCurrentMarkerErrorSquared(mx)); }
1386 const ObservationIx ox = getObservationIxForMarker(mx);
1387 if (!ox.isValid())
return 0;
1388 const Vec3& loc = getObservation(ox);
1390 return (findCurrentMarkerLocation(mx) - loc).normSqr();
1400 int calcErrors(
const State& state,
Vector& err)
const;
1401 int calcErrorJacobian(
const State& state,
Matrix& jacobian)
const;
1402 int getNumErrors(
const State& state)
const;
1403 int calcGoal(
const State& state,
Real& goal)
const;
1404 int calcGoalGradient(
const State& state,
Vector& grad)
const;
1405 int initializeCondition()
const;
1406 void uninitializeCondition()
const;
1412 const Marker& getMarker(MarkerIx i)
const {
return markers[i];}
1413 Marker& updMarker(MarkerIx i) {uninitializeAssembler();
return markers[i];}
1419 Array_<Marker,MarkerIx> markers;
1420 std::map<String,MarkerIx> markersByName;
1424 Array_<MarkerIx,ObservationIx> observation2marker;
1429 Array_<ObservationIx,MarkerIx> marker2observation;
1434 Array_<Vec3,ObservationIx> observations;
1439 typedef std::map<MobilizedBodyIndex,Array_<MarkerIx> > PerBodyMarkers;
1440 mutable PerBodyMarkers bodiesWithMarkers;
1445 #endif // SimTK_SIMBODY_ASSEMBLER_H_