Simbody
3.4 (development)
|
00001 #ifndef SimTK_SIMBODY_ASSEMBLER_H_ 00002 #define SimTK_SIMBODY_ASSEMBLER_H_ 00003 00004 /* -------------------------------------------------------------------------- * 00005 * Simbody(tm) * 00006 * -------------------------------------------------------------------------- * 00007 * This is part of the SimTK biosimulation toolkit originating from * 00008 * Simbios, the NIH National Center for Physics-Based Simulation of * 00009 * Biological Structures at Stanford, funded under the NIH Roadmap for * 00010 * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody. * 00011 * * 00012 * Portions copyright (c) 2010-12 Stanford University and the Authors. * 00013 * Authors: Michael Sherman * 00014 * Contributors: * 00015 * * 00016 * Licensed under the Apache License, Version 2.0 (the "License"); you may * 00017 * not use this file except in compliance with the License. You may obtain a * 00018 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0. * 00019 * * 00020 * Unless required by applicable law or agreed to in writing, software * 00021 * distributed under the License is distributed on an "AS IS" BASIS, * 00022 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 00023 * See the License for the specific language governing permissions and * 00024 * limitations under the License. * 00025 * -------------------------------------------------------------------------- */ 00026 00027 #include "SimTKcommon.h" 00028 #include "simbody/internal/common.h" 00029 #include "simbody/internal/MultibodySystem.h" 00030 #include "simbody/internal/SimbodyMatterSubsystem.h" 00031 00032 #include <set> 00033 #include <map> 00034 #include <cassert> 00035 #include <cmath> 00036 00037 namespace SimTK { 00038 00039 SimTK_DEFINE_UNIQUE_INDEX_TYPE(AssemblyConditionIndex); 00040 00041 class AssemblyCondition; 00042 00148 class SimTK_SIMBODY_EXPORT Assembler : public Study { 00149 typedef std::set<MobilizedBodyIndex> LockedMobilizers; 00150 typedef std::set<MobilizerQIndex> QSet; 00151 typedef std::map<MobilizedBodyIndex, QSet> LockedQs; 00152 typedef std::map<MobilizerQIndex, Vec2> QRanges; 00153 typedef std::map<MobilizedBodyIndex, QRanges> RestrictedQs; 00154 public: 00155 00158 SimTK_DEFINE_UNIQUE_LOCAL_INDEX_TYPE(Assembler,FreeQIndex); 00159 00163 class AssembleFailed; 00167 class TrackFailed; 00168 00186 explicit Assembler(const MultibodySystem& system); 00187 00197 Assembler& setErrorTolerance(Real tolerance=0) { 00198 SimTK_ERRCHK1_ALWAYS(0 <= tolerance, 00199 "Assembler::setTolerance()", "The requested error tolerance %g" 00200 " is illegal; we require 0 <= tolerance, with 0 indicating that" 00201 " the default tolerance (accuracy/10) is to be used.", tolerance); 00202 this->tolerance = tolerance; 00203 return *this; 00204 } 00209 Real getErrorToleranceInUse() const { 00210 return tolerance > 0 ? tolerance 00211 : (accuracy > 0 ? accuracy/10 : Real(0.1)/OODefaultAccuracy); 00212 } 00213 00223 Assembler& setAccuracy(Real accuracy=0) { 00224 SimTK_ERRCHK2_ALWAYS(0 <= accuracy && accuracy < 1, 00225 "Assembler::setAccuracy()", "The requested accuracy %g is illegal;" 00226 " we require 0 <= accuracy < 1, with 0 indicating that the default" 00227 " accuracy (%g) is to be used.", Real(1)/OODefaultAccuracy, accuracy); 00228 this->accuracy = accuracy; 00229 return *this; 00230 } 00233 Real getAccuracyInUse() const 00234 { return accuracy > 0 ? accuracy : Real(1)/OODefaultAccuracy; } 00235 00236 00243 Assembler& setSystemConstraintsWeight(Real weight) 00244 { assert(systemConstraints.isValid()); 00245 setAssemblyConditionWeight(systemConstraints,weight); 00246 return *this; } 00247 00251 Real getSystemConstraintsWeight() const 00252 { assert(systemConstraints.isValid()); 00253 return getAssemblyConditionWeight(systemConstraints); } 00254 00261 Assembler& setAssemblyConditionWeight(AssemblyConditionIndex condition, 00262 Real weight) { 00263 SimTK_INDEXCHECK_ALWAYS(condition, conditions.size(), 00264 "Assembler::setAssemblyConditionWeight()"); 00265 SimTK_ERRCHK1_ALWAYS(weight >= 0, "Assembler::setAssemblyConditionWeight()", 00266 "Illegal weight %g; weight must be nonnegative.", weight); 00267 uninitialize(); 00268 weights[condition] = weight; 00269 return *this; 00270 } 00271 00278 Real getAssemblyConditionWeight(AssemblyConditionIndex condition) const { 00279 SimTK_INDEXCHECK_ALWAYS(condition, conditions.size(), 00280 "Assembler::getAssemblyConditionWeight()"); 00281 return weights[condition]; 00282 } 00283 00288 AssemblyConditionIndex 00289 adoptAssemblyError(AssemblyCondition* p); 00298 AssemblyConditionIndex 00299 adoptAssemblyGoal(AssemblyCondition* p, Real weight=1); 00300 00301 00307 Assembler& setInternalState(const State& state) { 00308 uninitialize(); 00309 getMatterSubsystem().convertToEulerAngles(state, internalState); 00310 system.realizeModel(internalState); 00311 return *this; 00312 } 00319 void initialize() const; 00322 void initialize(const State& state) 00323 { setInternalState(state); initialize(); } 00330 00337 Real assemble(); 00338 00347 Real track(Real frameTime = -1); 00348 00355 Real assemble(State& state) { 00356 setInternalState(state); 00357 Real achievedCost = assemble(); // throws if it fails 00358 updateFromInternalState(state); 00359 return achievedCost; 00360 } 00361 00362 00366 Real calcCurrentGoal() const; 00375 Real calcCurrentErrorNorm() const; 00376 00377 00382 void updateFromInternalState(State& state) const { 00383 system.realizeModel(state); // allocates q's if they haven't been yet 00384 if (!getMatterSubsystem().getUseEulerAngles(state)) { 00385 State tempState; 00386 getMatterSubsystem().convertToQuaternions(getInternalState(), 00387 tempState); 00388 state.updQ() = tempState.getQ(); 00389 } else 00390 state.updQ() = getInternalState().getQ(); 00391 } 00401 00405 void lockMobilizer(MobilizedBodyIndex mbx) 00406 { uninitialize(); userLockedMobilizers.insert(mbx); } 00412 void unlockMobilizer(MobilizedBodyIndex mbx) 00413 { uninitialize(); userLockedMobilizers.erase(mbx); } 00414 00426 void lockQ(MobilizedBodyIndex mbx, MobilizerQIndex qx) 00427 { uninitialize(); userLockedQs[mbx].insert(qx); } 00428 00433 void unlockQ(MobilizedBodyIndex mbx, MobilizerQIndex qx) 00434 { LockedQs::iterator p = userLockedQs.find(mbx); 00435 if (p == userLockedQs.end()) return; 00436 QSet& qs = p->second; 00437 if (qs.erase(qx)) { // returns 0 if nothing erased 00438 uninitialize(); 00439 if (qs.empty()) 00440 userLockedQs.erase(p); // remove the whole mobilized body 00441 } 00442 } 00443 00449 void restrictQ(MobilizedBodyIndex mbx, MobilizerQIndex qx, 00450 Real lowerBound, Real upperBound) 00451 { SimTK_ERRCHK2_ALWAYS(lowerBound <= upperBound, "Assembler::restrictQ()", 00452 "The given range [%g,%g] is illegal because the lower bound is" 00453 " greater than the upper bound.", lowerBound, upperBound); 00454 if (lowerBound == -Infinity && upperBound == Infinity) 00455 { unrestrictQ(mbx,qx); return; } 00456 uninitialize(); 00457 userRestrictedQs[mbx][qx] = Vec2(lowerBound,upperBound); 00458 } 00459 00460 00465 void unrestrictQ(MobilizedBodyIndex mbx, MobilizerQIndex qx) 00466 { RestrictedQs::iterator p = userRestrictedQs.find(mbx); 00467 if (p == userRestrictedQs.end()) return; 00468 QRanges& qranges = p->second; 00469 if (qranges.erase(qx)) { // returns 0 if nothing erased 00470 uninitialize(); 00471 if (qranges.empty()) 00472 userRestrictedQs.erase(p); // remove the whole mobilized body 00473 } 00474 } 00487 int getNumGoalEvals() const; 00489 int getNumErrorEvals() const; 00491 int getNumGoalGradientEvals() const; 00493 int getNumErrorJacobianEvals() const; 00496 int getNumAssemblySteps() const; 00499 int getNumInitializations() const; 00503 void resetStats() const; 00511 00514 void setForceNumericalGradient(bool yesno) 00515 { forceNumericalGradient = yesno; } 00518 void setForceNumericalJacobian(bool yesno) 00519 { forceNumericalJacobian = yesno; } 00520 00526 void setUseRMSErrorNorm(bool yesno) 00527 { useRMSErrorNorm = yesno; } 00531 bool isUsingRMSErrorNorm() const {return useRMSErrorNorm;} 00532 00537 void uninitialize() const; 00540 bool isInitialized() const {return alreadyInitialized;} 00541 00548 const State& getInternalState() const {return internalState;} 00549 00553 void addReporter(const EventReporter& reporter) { 00554 reporters.push_back(&reporter); 00555 } 00556 00560 int getNumFreeQs() const 00561 { return freeQ2Q.size(); } 00562 00566 QIndex getQIndexOfFreeQ(FreeQIndex freeQIndex) const 00567 { return freeQ2Q[freeQIndex]; } 00568 00573 FreeQIndex getFreeQIndexOfQ(QIndex qx) const 00574 { return q2FreeQ[qx]; } 00575 00578 Vec2 getFreeQBounds(FreeQIndex freeQIndex) const { 00579 if (!lower.size()) return Vec2(-Infinity, Infinity); 00580 else return Vec2(lower[freeQIndex], upper[freeQIndex]); 00581 } 00582 00586 const MultibodySystem& getMultibodySystem() const 00587 { return system; } 00590 const SimbodyMatterSubsystem& getMatterSubsystem() const 00591 { return system.getMatterSubsystem(); } 00596 ~Assembler(); 00597 00598 00599 00600 //------------------------------------------------------------------------------ 00601 private: // methods 00602 //------------------------------------------------------------------------------ 00603 void setInternalStateFromFreeQs(const Vector& freeQs) { 00604 assert(freeQs.size() == getNumFreeQs()); 00605 Vector& q = internalState.updQ(); 00606 for (FreeQIndex fx(0); fx < getNumFreeQs(); ++fx) 00607 q[getQIndexOfFreeQ(fx)] = freeQs[fx]; 00608 system.realize(internalState, Stage::Position); 00609 } 00610 00611 Vector getFreeQsFromInternalState() const { 00612 Vector freeQs(getNumFreeQs()); 00613 const Vector& q = internalState.getQ(); 00614 for (FreeQIndex fx(0); fx < getNumFreeQs(); ++fx) 00615 freeQs[fx] = q[getQIndexOfFreeQ(fx)]; 00616 return freeQs; 00617 } 00618 00619 void reinitializeWithExtraQsLocked 00620 (const Array_<QIndex>& toBeLocked) const; 00621 00622 00623 00624 //------------------------------------------------------------------------------ 00625 private: // data members 00626 //------------------------------------------------------------------------------ 00627 const MultibodySystem& system; 00628 Array_<const EventReporter*> reporters; // just references; don't delete 00629 00630 // These members affect the behavior of the assembly algorithm. 00631 static const int OODefaultAccuracy = 1000; // 1/accuracy if acc==0 00632 Real accuracy; // 0 means use 1/OODefaultAccuracy 00633 Real tolerance; // 0 means use accuracy/10 00634 bool forceNumericalGradient; // ignore analytic gradient methods 00635 bool forceNumericalJacobian; // ignore analytic Jacobian methods 00636 bool useRMSErrorNorm; // what norm defines success? 00637 00638 // Changes to any of these data members set isInitialized()=false. 00639 State internalState; 00640 00641 // These are the mobilizers that were set in lockMobilizer(). They are 00642 // separate from those involved in individually-locked q's. 00643 LockedMobilizers userLockedMobilizers; 00644 // These are locks placed on individual q's; they are independent of the 00645 // locked mobilizer settings. 00646 LockedQs userLockedQs; 00647 // These are range restrictions placed on individual q's. 00648 RestrictedQs userRestrictedQs; 00649 00650 // These are (condition,weight) pairs with weight==Infinity meaning 00651 // constraint; weight==0 meaning currently ignored; and any other 00652 // positive weight meaning a goal. 00653 Array_<AssemblyCondition*,AssemblyConditionIndex> 00654 conditions; 00655 Array_<Real,AssemblyConditionIndex> weights; 00656 00657 // We always have an assembly condition for the Constraints which are 00658 // enabled in the System; this is the index which can be used to 00659 // retrieve that condition. The default weight is Infinity. 00660 AssemblyConditionIndex systemConstraints; 00661 00662 00663 // These are filled in when the Assembler is initialized. 00664 mutable bool alreadyInitialized; 00665 00666 // These are extra q's we removed for numerical reasons. 00667 mutable Array_<QIndex> extraQsLocked; 00668 00669 // These represent restrictions on the independent variables (q's). 00670 mutable std::set<QIndex> lockedQs; 00671 mutable Array_<FreeQIndex,QIndex> q2FreeQ; // nq of these 00672 mutable Array_<QIndex,FreeQIndex> freeQ2Q; // nfreeQ of these 00673 // 0 length if no bounds; otherwise, index by FreeQIndex. 00674 mutable Vector lower, upper; 00675 00676 // These represent the active assembly conditions. 00677 mutable Array_<AssemblyConditionIndex> errors; 00678 mutable Array_<int> nTermsPerError; 00679 mutable Array_<AssemblyConditionIndex> goals; 00680 00681 class AssemblerSystem; // local class 00682 mutable AssemblerSystem* asmSys; 00683 mutable Optimizer* optimizer; 00684 00685 mutable int nAssemblySteps; // count assemble() and track() calls 00686 mutable int nInitializations; // # times we had to reinitialize 00687 00688 friend class AssemblerSystem; 00689 }; 00690 00691 00692 00693 //------------------------------------------------------------------------------ 00694 // ASSEMBLY CONDITION 00695 //------------------------------------------------------------------------------ 00702 class SimTK_SIMBODY_EXPORT AssemblyCondition { 00703 public: 00704 00707 explicit AssemblyCondition(const String& name) 00708 : name(name), assembler(0) {} 00709 00711 virtual ~AssemblyCondition() {} 00712 00719 virtual int initializeCondition() const {return 0;} 00720 00723 virtual void uninitializeCondition() const {} 00724 00733 virtual int calcErrors(const State& state, Vector& err) const 00734 { return -1; } 00735 00745 virtual int calcErrorJacobian(const State& state, Matrix& jacobian) const 00746 { return -1; } 00747 00755 virtual int getNumErrors(const State& state) const 00756 { Vector err; 00757 const int status = calcErrors(state, err); 00758 if (status == 0) 00759 return err.size(); 00760 SimTK_ERRCHK1_ALWAYS(status != -1, "AssemblyCondition::getNumErrors()", 00761 "The default implementation of getNumErrors() depends on" 00762 " calcErrors() but that method was not implemented for assembly" 00763 " condition '%s'.", name.c_str()); 00764 SimTK_ERRCHK2_ALWAYS(status == 0, "AssemblyCondition::getNumErrors()", 00765 "The default implementation of getNumErrors() uses calcErrors()" 00766 " which returned status %d (assembly condition '%s').", 00767 status, name.c_str()); 00768 return -1; // NOTREACHED 00769 } 00770 00775 virtual int calcGoal(const State& state, Real& goal) const 00776 { static Vector err; 00777 const int status = calcErrors(state, err); 00778 if (status == 0) 00779 { goal = err.normSqr() / std::max(1,err.size()); 00780 return 0; } 00781 SimTK_ERRCHK1_ALWAYS(status != -1, "AssemblyCondition::calcGoal()", 00782 "The default implementation of calcGoal() depends on calcErrors()" 00783 " but that method was not implemented for assembly condition '%s'.", 00784 name.c_str()); 00785 SimTK_ERRCHK2_ALWAYS(status == 0, "AssemblyCondition::calcGoal()", 00786 "The default implementation of calcGoal() uses calcErrors() which" 00787 " returned status %d (assembly condition '%s').", 00788 status, name.c_str()); 00789 return -1; // NOTREACHED 00790 } 00791 00798 virtual int calcGoalGradient(const State& state, Vector& gradient) const 00799 { return -1; } 00800 00802 const char* getName() const {return name.c_str();} 00803 00806 bool isInAssembler() const {return assembler != 0;} 00810 const Assembler& getAssembler() const 00811 { assert(assembler); return *assembler;} 00815 AssemblyConditionIndex getAssemblyConditionIndex() const 00816 { return myAssemblyConditionIndex; } 00817 00818 //------------------------------------------------------------------------------ 00819 protected: 00820 //------------------------------------------------------------------------------ 00821 // These are useful when writing concrete AssemblyConditions. 00822 00825 int getNumFreeQs() const {return getAssembler().getNumFreeQs();} 00829 QIndex getQIndexOfFreeQ(Assembler::FreeQIndex fx) const 00830 { return getAssembler().getQIndexOfFreeQ(fx); } 00834 Assembler::FreeQIndex getFreeQIndexOfQ(QIndex qx) const 00835 { return getAssembler().getFreeQIndexOfQ(qx); } 00837 const MultibodySystem& getMultibodySystem() const 00838 { return getAssembler().getMultibodySystem(); } 00841 const SimbodyMatterSubsystem& getMatterSubsystem() const 00842 { return getMultibodySystem().getMatterSubsystem(); } 00843 00846 void initializeAssembler() const { 00847 // The Assembler will in turn invoke initializeCondition(). 00848 if (isInAssembler()) getAssembler().initialize(); 00849 else initializeCondition(); 00850 } 00851 00855 void uninitializeAssembler() const { 00856 // The Assembler will in turn invoke uninitializeCondition(). 00857 if (isInAssembler()) getAssembler().uninitialize(); 00858 else uninitializeCondition(); 00859 } 00860 00861 //------------------------------------------------------------------------------ 00862 private: 00863 //------------------------------------------------------------------------------ 00864 // This method is used by the Assembler when the AssemblyCondition object 00865 // is adopted. 00866 friend class Assembler; 00867 void setAssembler(const Assembler& assembler, AssemblyConditionIndex acx) { 00868 assert(!this->assembler); 00869 this->assembler = &assembler; 00870 this->myAssemblyConditionIndex = acx; 00871 } 00872 00873 String name; // assembly condition name 00874 const Assembler* assembler; 00875 AssemblyConditionIndex myAssemblyConditionIndex; 00876 }; 00877 00878 00879 00880 //------------------------------------------------------------------------------ 00881 // Q VALUE 00882 //------------------------------------------------------------------------------ 00886 class QValue : public AssemblyCondition { 00887 public: 00891 QValue(MobilizedBodyIndex mbx, MobilizerQIndex qx, 00892 Real value) 00893 : AssemblyCondition("QValue"), 00894 mobodIndex(mbx), qIndex(qx), value(value) {} 00895 00898 Real getValue() const {return value;} 00901 void setValue(Real newValue) {value=newValue;} 00902 00903 // For constraint: 00904 int getNumEquations(const State&) const {return 1;} 00905 int calcErrors(const State& state, Vector& error) const { 00906 const SimbodyMatterSubsystem& matter = getMatterSubsystem(); 00907 const MobilizedBody& mobod = matter.getMobilizedBody(mobodIndex); 00908 error.resize(1); 00909 error[0] = mobod.getOneQ(state, qIndex) - value; 00910 return 0; 00911 } 00912 // Error jacobian is a zero-row except for a 1 in this q's entry (if 00913 // this q is free). 00914 int calcErrorJacobian(const State& state, Matrix& J) const { 00915 const SimbodyMatterSubsystem& matter = getMatterSubsystem(); 00916 const MobilizedBody& mobod = matter.getMobilizedBody(mobodIndex); 00917 J.resize(1, getNumFreeQs()); 00918 J = 0; // will have at most one non-zero 00919 00920 // Find the FreeQIndex corresponding to this q. 00921 const QIndex thisIx = QIndex(mobod.getFirstQIndex(state)+qIndex); 00922 const Assembler::FreeQIndex thisFreeIx = getFreeQIndexOfQ(thisIx); 00923 00924 // If this q isn't free then there is no way to affect the error 00925 // so the Jacobian stays all-zero. 00926 if (thisFreeIx.isValid()) 00927 J(0,thisFreeIx) = 1; 00928 00929 return 0; 00930 } 00931 00932 // For goal: goal = (q-value)^2 / 2 (the /2 is for gradient beauty) 00933 int calcGoal(const State& state, Real& goal) const { 00934 const SimbodyMatterSubsystem& matter = getMatterSubsystem(); 00935 const MobilizedBody& mobod = matter.getMobilizedBody(mobodIndex); 00936 goal = square(mobod.getOneQ(state, qIndex) - value) / 2; 00937 return 0; 00938 } 00939 // Return a gradient with only this q's entry non-zero (if 00940 // this q is free). 00941 int calcGoalGradient(const State& state, Vector& grad) const { 00942 const SimbodyMatterSubsystem& matter = getMatterSubsystem(); 00943 const MobilizedBody& mobod = matter.getMobilizedBody(mobodIndex); 00944 grad.resize(getNumFreeQs()); 00945 grad = 0; // will have at most one non-zero 00946 00947 // Find the FreeQIndex corresponding to this q. 00948 const QIndex thisIx = QIndex(mobod.getFirstQIndex(state)+qIndex); 00949 const Assembler::FreeQIndex thisFreeIx = getFreeQIndexOfQ(thisIx); 00950 00951 // If this q isn't free then there is no way to affect the goal 00952 // so the gradient stays all-zero. 00953 if (thisFreeIx.isValid()) 00954 grad[thisFreeIx] = mobod.getOneQ(state, qIndex) - value; 00955 00956 return 0; 00957 } 00958 00959 private: 00960 MobilizedBodyIndex mobodIndex; 00961 MobilizerQIndex qIndex; 00962 Real value; 00963 }; 00964 00965 00966 00967 //------------------------------------------------------------------------------ 00968 // MARKERS 00969 //------------------------------------------------------------------------------ 01012 class SimTK_SIMBODY_EXPORT Markers : public AssemblyCondition { 01013 01014 // This is a private class used in the implementation below but not 01015 // accessible through the API. 01016 struct Marker { 01017 Marker(const String& name, MobilizedBodyIndex bodyB, 01018 const Vec3& markerInB, Real weight = 1) 01019 : name(name), bodyB(bodyB), markerInB(markerInB), weight(weight) 01020 { assert(weight >= 0); } 01021 01022 Marker(MobilizedBodyIndex bodyB, const Vec3& markerInB, Real weight=1) 01023 : name(""), bodyB(bodyB), markerInB(markerInB), weight(weight) 01024 { assert(weight >= 0); } 01025 01026 String name; 01027 MobilizedBodyIndex bodyB; 01028 Vec3 markerInB; 01029 Real weight; 01030 }; 01031 01032 public: 01033 01035 SimTK_DEFINE_UNIQUE_LOCAL_INDEX_TYPE(Markers,MarkerIx); 01037 SimTK_DEFINE_UNIQUE_LOCAL_INDEX_TYPE(Markers,ObservationIx); 01038 01039 01040 01041 //------------------------------------------------------------------------------ 01047 01051 Markers() : AssemblyCondition("Markers") {} 01052 01072 MarkerIx addMarker(const String& name, MobilizedBodyIndex bodyB, 01073 const Vec3& markerInB, Real weight=1) 01074 { SimTK_ERRCHK1_ALWAYS(isFinite(weight) && weight >= 0, 01075 "Markers::addMarker()", "Illegal marker weight %g.", weight); 01076 uninitializeAssembler(); 01077 // Forget any previously-established observation/marker correspondence. 01078 observation2marker.clear(); marker2observation.clear(); 01079 observations.clear(); 01080 const MarkerIx ix(markers.size()); 01081 String nm = String::trimWhiteSpace(name); 01082 if (nm.empty()) 01083 nm = String("_UNNAMED_") + String(ix); 01084 01085 std::pair< std::map<String,MarkerIx>::iterator, bool > 01086 found = markersByName.insert(std::make_pair(nm,ix)); 01087 SimTK_ERRCHK2_ALWAYS(found.second, // true if insertion was done 01088 "Markers::addMarker()", 01089 "Marker name '%s' was already use for Marker %d.", 01090 nm.c_str(), (int)found.first->second); 01091 01092 markers.push_back(Marker(nm,bodyB,markerInB,weight)); 01093 return ix; 01094 } 01095 01100 MarkerIx addMarker(MobilizedBodyIndex bodyB, const Vec3& markerInB, 01101 Real weight=1) 01102 { return addMarker("", bodyB, markerInB, weight); } 01103 01104 01125 void defineObservationOrder(const Array_<MarkerIx>& observationOrder) { 01126 uninitializeAssembler(); 01127 if (observationOrder.empty()) { 01128 observation2marker.resize(markers.size()); 01129 for (MarkerIx mx(0); mx < markers.size(); ++mx) 01130 observation2marker[ObservationIx(mx)] = mx; 01131 } else 01132 observation2marker = observationOrder; 01133 marker2observation.clear(); 01134 // We might need to grow this more, but this is an OK starting guess. 01135 marker2observation.resize(observation2marker.size()); // all invalid 01136 for (ObservationIx ox(0); ox < observation2marker.size(); ++ox) { 01137 const MarkerIx mx = observation2marker[ox]; 01138 if (!mx.isValid()) continue; 01139 01140 if (marker2observation.size() <= mx) 01141 marker2observation.resize(mx+1); 01142 SimTK_ERRCHK4_ALWAYS(!marker2observation[mx].isValid(), 01143 "Markers::defineObservationOrder()", 01144 "An attempt was made to associate Marker %d (%s) with" 01145 " Observations %d and %d; only one Observation per Marker" 01146 " is permitted.", 01147 (int)mx, getMarkerName(mx).c_str(), 01148 (int)marker2observation[mx], (int)ox); 01149 01150 marker2observation[mx] = ox; 01151 } 01152 // Make room for marker observations. 01153 observations.clear(); 01154 observations.resize(observation2marker.size(),Vec3(NaN)); 01155 } 01156 01162 void defineObservationOrder(const Array_<String>& observationOrder) 01163 { Array_<MarkerIx> markerIxs(observationOrder.size()); 01164 for (ObservationIx ox(0); ox < observationOrder.size(); ++ox) 01165 markerIxs[ox] = getMarkerIx(observationOrder[ox]); 01166 defineObservationOrder(markerIxs); } 01167 01169 // no copy required 01170 void defineObservationOrder(const std::vector<String>& observationOrder) 01171 { defineObservationOrder(ArrayViewConst_<String>(observationOrder)); } 01172 01173 01175 // must copy 01176 void defineObservationOrder(const Array_<std::string>& observationOrder) 01177 { const Array_<String> observations(observationOrder); // copy 01178 defineObservationOrder(observations); } 01179 01181 // must copy 01182 void defineObservationOrder(const std::vector<std::string>& observationOrder) 01183 { const Array_<String> observations(observationOrder); // copy 01184 defineObservationOrder(observations); } 01185 01187 void defineObservationOrder(int n, const char* const observationOrder[]) 01188 { Array_<MarkerIx> markerIxs(n); 01189 for (ObservationIx ox(0); ox < n; ++ox) 01190 markerIxs[ox] = getMarkerIx(String(observationOrder[ox])); 01191 defineObservationOrder(markerIxs); } 01196 //------------------------------------------------------------------------------ 01203 01206 int getNumMarkers() const {return markers.size();} 01207 01211 const String& getMarkerName(MarkerIx ix) 01212 { return markers[ix].name; } 01216 const MarkerIx getMarkerIx(const String& name) 01217 { std::map<String,MarkerIx>::const_iterator p = markersByName.find(name); 01218 return p == markersByName.end() ? MarkerIx() : p->second; } 01219 01222 Real getMarkerWeight(MarkerIx mx) 01223 { return markers[mx].weight; } 01224 01226 MobilizedBodyIndex getMarkerBody(MarkerIx mx) const 01227 { return markers[mx].bodyB; } 01228 01230 const Vec3& getMarkerStation(MarkerIx mx) const 01231 { return markers[mx].markerInB; } 01232 01238 int getNumObservations() const {return observation2marker.size();} 01239 01245 ObservationIx getObservationIxForMarker(MarkerIx mx) const 01246 { return marker2observation[mx]; } 01247 01250 bool hasObservation(MarkerIx mx) const 01251 { return getObservationIxForMarker(mx).isValid(); } 01252 01258 MarkerIx getMarkerIxForObservation(ObservationIx ox) const 01259 { return observation2marker[ox]; } 01260 01263 bool hasMarker(ObservationIx ox) const 01264 { return getMarkerIxForObservation(ox).isValid();} 01265 01270 const Array_<MarkerIx>& getMarkersOnBody(MobilizedBodyIndex mbx) { 01271 static const Array_<MarkerIx> empty; 01272 SimTK_ERRCHK_ALWAYS(isInAssembler(), "Markers::getMarkersOnBody()", 01273 "This method can't be called until the Markers object has been" 01274 " adopted by an Assembler."); 01275 initializeAssembler(); 01276 PerBodyMarkers::const_iterator bodyp = bodiesWithMarkers.find(mbx); 01277 return bodyp == bodiesWithMarkers.end() ? empty : bodyp->second; 01278 } 01283 //------------------------------------------------------------------------------ 01289 01293 void moveOneObservation(ObservationIx ox, const Vec3& observation) 01294 { SimTK_ERRCHK_ALWAYS(!observations.empty(), "Assembler::moveOneObservation()", 01295 "There are currently no observations defined. Either the Assembler" 01296 " needs to be initialized to get the default observation order, or you" 01297 " should call defineObservationOrder() explicitly."); 01298 SimTK_ERRCHK2_ALWAYS(ox.isValid() && ox < observations.size(), 01299 "Assembler::moveOneObservation()", "ObservationIx %d is invalid or" 01300 " out of range; there are %d observations currently defined. Use" 01301 " defineObservationOrder() to specify the set of observations and how" 01302 " they correspond to markers.", 01303 (int)ox, (int)observations.size()); 01304 observations[ox] = observation; 01305 } 01306 01316 void moveAllObservations(const Array_<Vec3>& observations) 01317 { SimTK_ERRCHK2_ALWAYS((int)observations.size() == (int)observation2marker.size(), 01318 "Markers::moveAllObservations()", 01319 "Number of observations provided (%d) differs from the number of" 01320 " observations (%d) last defined with defineObservationOrder().", 01321 observations.size(), observation2marker.size()); 01322 this->observations = observations; } 01323 01333 void changeMarkerWeight(MarkerIx mx, Real weight) { 01334 SimTK_ERRCHK1_ALWAYS(isFinite(weight) && weight >= 0, 01335 "Markers::changeMarkerWeight()", "Illegal marker weight %g.", weight); 01336 01337 Marker& marker = markers[mx]; 01338 if (marker.weight == weight) 01339 return; 01340 01341 if (marker.weight == 0 || weight == 0) 01342 uninitializeAssembler(); // qualitative change 01343 01344 marker.weight = weight; 01345 } 01346 01351 const Vec3& getObservation(ObservationIx ox) const {return observations[ox];} 01358 const Array_<Vec3,ObservationIx>& getAllObservations() const 01359 { return observations; } 01360 01364 Vec3 findCurrentMarkerLocation(MarkerIx mx) const; 01365 01375 Real findCurrentMarkerError(MarkerIx mx) const 01376 { return std::sqrt(findCurrentMarkerErrorSquared(mx)); } 01377 01385 Real findCurrentMarkerErrorSquared(MarkerIx mx) const { 01386 const ObservationIx ox = getObservationIxForMarker(mx); 01387 if (!ox.isValid()) return 0; // no observation for this marker 01388 const Vec3& loc = getObservation(ox); 01389 if (!loc.isFinite()) return 0; // NaN in observation; error is ignored 01390 return (findCurrentMarkerLocation(mx) - loc).normSqr(); 01391 } 01396 //------------------------------------------------------------------------------ 01400 int calcErrors(const State& state, Vector& err) const; 01401 int calcErrorJacobian(const State& state, Matrix& jacobian) const; 01402 int getNumErrors(const State& state) const; 01403 int calcGoal(const State& state, Real& goal) const; 01404 int calcGoalGradient(const State& state, Vector& grad) const; 01405 int initializeCondition() const; 01406 void uninitializeCondition() const; 01409 //------------------------------------------------------------------------------ 01410 private: 01411 //------------------------------------------------------------------------------ 01412 const Marker& getMarker(MarkerIx i) const {return markers[i];} 01413 Marker& updMarker(MarkerIx i) {uninitializeAssembler(); return markers[i];} 01414 01415 // data members 01416 01417 // Marker definition. Any change here except a quantitative change to the 01418 // marker's weight uninitializes the Assembler. 01419 Array_<Marker,MarkerIx> markers; 01420 std::map<String,MarkerIx> markersByName; 01421 01422 // Observation-marker corresondence specification. Any change here 01423 // uninitializes the Assembler. 01424 Array_<MarkerIx,ObservationIx> observation2marker; 01425 01426 // For convience in mapping from a marker to its corresponding observation. 01427 // ObservationIx will be invalid if a particular marker has no associated 01428 // observation. 01429 Array_<ObservationIx,MarkerIx> marker2observation; 01430 01431 // This is the current set of marker location observations, one per entry in 01432 // the observation2marker array. Changing the values here does not uninitialize 01433 // the Assembler. 01434 Array_<Vec3,ObservationIx> observations; 01435 01436 // After initialize, this groups the markers by body and weeds out 01437 // any zero-weighted markers. TODO: skip low-weighted markers, at 01438 // least at the start of the assembly. 01439 typedef std::map<MobilizedBodyIndex,Array_<MarkerIx> > PerBodyMarkers; 01440 mutable PerBodyMarkers bodiesWithMarkers; 01441 }; 01442 01443 } // namespace SimTK 01444 01445 #endif // SimTK_SIMBODY_ASSEMBLER_H_