1 /* -------------------------------------------------------------------------- *
2 * OpenSim: Component.cpp *
3 * -------------------------------------------------------------------------- *
4 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation. *
5 * See http://opensim.stanford.edu and the NOTICE file for more information. *
6 * OpenSim is developed at Stanford University and supported by the US *
7 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA *
8 * through the Warrior Web program. *
9 * *
10 * Copyright (c) 2005-2017 Stanford University and the Authors *
11 * Author(s): Ajay Seth, Michael Sherman *
12 * Contributor(s): Ayman Habib *
13 * *
14 * Licensed under the Apache License, Version 2.0 (the "License"); you may *
15 * not use this file except in compliance with the License. You may obtain a *
16 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0. *
17 * *
18 * Unless required by applicable law or agreed to in writing, software *
19 * distributed under the License is distributed on an "AS IS" BASIS, *
20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
21 * See the License for the specific language governing permissions and *
22 * limitations under the License. *
23 * -------------------------------------------------------------------------- */
24
25 // INCLUDES
26 #include "Component.h"
27 #include "OpenSim/Common/IO.h"
28 #include "XMLDocument.h"
29 #include <unordered_map>
30 #include <set>
31 #include <regex>
32
33 using namespace SimTK;
34
35 namespace OpenSim {
36
37 //==============================================================================
38 // COMPONENT MEASURE
39 //==============================================================================
40 // Every OpenSim::Component is associated with a Simbody Measure of type
41 // ComponentMeasure defined here. This provides a full set of realize()
42 // methods for performing computations with System resources that are maintained
43 // at the Component base level, such as calculating state derivatives.
44
45 template <class T>
46 class ComponentMeasure : public SimTK::Measure_<T> {
47 public:
48 SimTK_MEASURE_HANDLE_PREAMBLE(ComponentMeasure, SimTK::Measure_<T>);
49
ComponentMeasure(SimTK::Subsystem & sub,const OpenSim::Component & mc)50 ComponentMeasure(SimTK::Subsystem& sub,
51 const OpenSim::Component& mc)
52 : SimTK::Measure_<T>(sub, new Implementation(mc),
53 SimTK::AbstractMeasure::SetHandle()) {}
54
55 SimTK_MEASURE_HANDLE_POSTSCRIPT(ComponentMeasure, SimTK::Measure_<T>);
56 };
57
58
59 template <class T>
60 class ComponentMeasure<T>::Implementation
61 : public SimTK::Measure_<T>::Implementation {
62 public:
63 // Don't allocate a value cache entry since this measure's value is
64 // just a dummy.
Implementation(const Component & c)65 explicit Implementation(const Component& c)
66 : SimTK::Measure_<T>::Implementation(0), _Component(c) {}
67
68 // Implementations of Measure_<T>::Implementation virtual methods.
69
cloneVirtual() const70 Implementation* cloneVirtual() const override final
71 { return new Implementation(*this); }
72
getNumTimeDerivativesVirtual() const73 int getNumTimeDerivativesVirtual() const override final {return 0;}
getDependsOnStageVirtual(int order) const74 SimTK::Stage getDependsOnStageVirtual(int order) const override final
75 { return SimTK::Stage::Empty; }
76
getUncachedValueVirtual(const SimTK::State & s,int derivOrder) const77 const T& getUncachedValueVirtual
78 (const SimTK::State& s, int derivOrder) const override final
79 { return this->getValueZero(); }
80
realizeMeasureTopologyVirtual(SimTK::State & s) const81 void realizeMeasureTopologyVirtual(SimTK::State& s) const override final
82 { _Component.extendRealizeTopology(s); }
realizeMeasureModelVirtual(SimTK::State & s) const83 void realizeMeasureModelVirtual(SimTK::State& s) const override final
84 { _Component.extendRealizeModel(s); }
realizeMeasureInstanceVirtual(const SimTK::State & s) const85 void realizeMeasureInstanceVirtual(const SimTK::State& s)
86 const override final
87 { _Component.extendRealizeInstance(s); }
realizeMeasureTimeVirtual(const SimTK::State & s) const88 void realizeMeasureTimeVirtual(const SimTK::State& s) const override final
89 { _Component.extendRealizeTime(s); }
realizeMeasurePositionVirtual(const SimTK::State & s) const90 void realizeMeasurePositionVirtual(const SimTK::State& s)
91 const override final
92 { _Component.extendRealizePosition(s); }
realizeMeasureVelocityVirtual(const SimTK::State & s) const93 void realizeMeasureVelocityVirtual(const SimTK::State& s)
94 const override final
95 { _Component.extendRealizeVelocity(s); }
realizeMeasureDynamicsVirtual(const SimTK::State & s) const96 void realizeMeasureDynamicsVirtual(const SimTK::State& s)
97 const override final
98 { _Component.extendRealizeDynamics(s); }
realizeMeasureAccelerationVirtual(const SimTK::State & s) const99 void realizeMeasureAccelerationVirtual(const SimTK::State& s)
100 const override final
101 { _Component.extendRealizeAcceleration(s); }
realizeMeasureReportVirtual(const SimTK::State & s) const102 void realizeMeasureReportVirtual(const SimTK::State& s)
103 const override final
104 { _Component.extendRealizeReport(s); }
105
106 private:
107 const Component& _Component;
108 };
109
110
111 //==============================================================================
112 // COMPONENT
113 //==============================================================================
Component()114 Component::Component() : Object()
115 {
116 constructProperty_components();
117 }
118
Component(const std::string & fileName,bool updFromXMLNode)119 Component::Component(const std::string& fileName, bool updFromXMLNode)
120 : Object(fileName, updFromXMLNode)
121 {
122 constructProperty_components();
123 }
124
Component(SimTK::Xml::Element & element)125 Component::Component(SimTK::Xml::Element& element) : Object(element)
126 {
127 constructProperty_components();
128 }
129
isComponentInOwnershipTree(const Component * subcomponent) const130 bool Component::isComponentInOwnershipTree(const Component* subcomponent) const {
131 //get to the root Component
132 const Component* root = this;
133 while (root->hasOwner()) {
134 root = &(root->getOwner());
135 }
136 // if the root has no immediate subcomponents do not bother
137 // checking if the subcomponent is in the ownership tree
138 if ((root->getNumImmediateSubcomponents() > 0)) {
139 auto components = root->getComponentList<Component>();
140 for (auto& c : components) {
141 if (subcomponent == &c) return true;
142 }
143 }
144 return false;
145 }
146
addComponent(Component * subcomponent)147 void Component::addComponent(Component* subcomponent)
148 {
149 OPENSIM_THROW_IF(isComponentInOwnershipTree(subcomponent),
150 ComponentAlreadyPartOfOwnershipTree,
151 subcomponent->getName(), getName());
152
153 updProperty_components().adoptAndAppendValue(subcomponent);
154 finalizeFromProperties();
155
156 prependComponentPathToConnecteePath(*subcomponent);
157
158 // allow the derived Component to perform secondary operations
159 // in response to the inclusion of the subcomponent
160 extendAddComponent(subcomponent);
161 }
162
prependComponentPathToConnecteePath(Component & subcomponent)163 void Component::prependComponentPathToConnecteePath(
164 Component& subcomponent) {
165 const std::string compPath = subcomponent.getAbsolutePathString();
166 const Component& root = subcomponent.getRoot();
167 for (auto& comp : subcomponent.updComponentList()) {
168 for (auto& it : comp._socketsTable) {
169 if (!root.hasComponent(it.second->getConnecteePath()))
170 it.second->prependComponentPathToConnecteePath(compPath);
171 }
172 for (auto& it : comp._inputsTable) {
173 it.second->prependComponentPathToConnecteePath(compPath);
174 }
175 }
176 }
177
finalizeFromProperties()178 void Component::finalizeFromProperties()
179 {
180 reset();
181
182 // last opportunity to modify Object names based on properties
183 if (!hasOwner()) {
184 // only call when Component is root since method is recursive
185 makeObjectNamesConsistentWithProperties();
186 }
187
188 // TODO use a flag to set whether we are lenient on having nameless
189 // Components. For backward compatibility we need to be able to
190 // handle nameless components so assign them their class name
191 // - aseth
192 if (getName().empty()) {
193 setName(IO::Lowercase(getConcreteClassName()));
194 }
195
196 OPENSIM_THROW_IF( getName().empty(), ComponentHasNoName,
197 getConcreteClassName() );
198
199 ComponentPath cp;
200 OPENSIM_THROW_IF( !cp.isLegalPathElement(getName()), InvalidComponentName,
201 getName(), cp.getInvalidChars(), getConcreteClassName());
202
203 for (auto& comp : _memberSubcomponents) {
204 comp->setOwner(*this);
205 }
206 for (auto& comp : _adoptedSubcomponents) {
207 comp->setOwner(*this);
208 }
209
210 // Provide sockets, inputs, and outputs with a pointer to its component
211 // (this) so that they can invoke the component's methods.
212 for (auto& it : _socketsTable) {
213 it.second->setOwner(*this);
214 // Let the Socket handle any errors in the connectee_name property.
215 it.second->checkConnecteePathProperty();
216 }
217 for (auto& it : _inputsTable) {
218 it.second->setOwner(*this);
219 // Let the Socket handle any errors in the connectee_name property.
220 it.second->checkConnecteePathProperty();
221 }
222 for (auto& it : _outputsTable) {
223 it.second->setOwner(*this);
224 }
225
226 markPropertiesAsSubcomponents();
227 componentsFinalizeFromProperties();
228
229 // The following block is used to ensure that deserialized names of
230 // Components are unique so they can be used to unambiguously locate
231 // and connect all loaded Components. If a duplicate is encountered,
232 // it is assigned a unique name.
233 auto subs = getImmediateSubcomponents();
234 std::set<std::string> names{};
235
236 // increment the count of duplicates to use as a unique suffix
237 int count{ 0 };
238 // temp variable to hold the unique name used to rename a duplicate
239 std::string uniqueName{};
240
241 for (auto& sub : subs) {
242 const std::string& name = sub->getName();
243
244 // reset duplicate count and search name
245 count = 0;
246 uniqueName = name;
247
248 // while the name is still not unique keep incrementing the count
249 while (names.find(uniqueName) != names.cend()) {
250 // In the future this should become an Exception
251 //OPENSIM_THROW(SubcomponentsWithDuplicateName, getName(), searchName);
252 // for now, rename the duplicately named subcomponent
253 // but first test the uniqueness of the name (the while condition)
254 uniqueName = name + "_" + std::to_string(count++);
255 }
256
257 if (count > 0) { // if a duplicate
258 // Warn of the problem
259 std::string msg = getConcreteClassName() + " '" + getName() +
260 "' has subcomponents with duplicate name '" + name + "'.\n" +
261 "The duplicate is being renamed to '" + uniqueName + "'.";
262 std::cout << msg << std::endl;
263
264 // Now rename the subcomponent with its verified unique name
265 Component* mutableSub = const_cast<Component *>(sub.get());
266 mutableSub->setName(uniqueName);
267 }
268
269 // keep track of unique names
270 names.insert(uniqueName);
271 }
272 // End of duplicate finding and renaming.
273
274 extendFinalizeFromProperties();
275 setObjectIsUpToDateWithProperties();
276 }
277
278 // Base class implementation of virtual method.
279 // Call finalizeFromProperties on all subcomponents
componentsFinalizeFromProperties() const280 void Component::componentsFinalizeFromProperties() const
281 {
282 for (auto& comp : _memberSubcomponents) {
283 const_cast<Component*>(comp.get())
284 ->finalizeFromProperties();
285 }
286 for (auto& comp : _propertySubcomponents) {
287 const_cast<Component*>(comp.get())
288 ->finalizeFromProperties();
289 }
290 for (auto& comp : _adoptedSubcomponents) {
291 const_cast<Component*>(comp.get())
292 ->finalizeFromProperties();
293 }
294 }
295
296 // Base class implementation of non-virtual finalizeConnections method.
finalizeConnections(Component & root)297 void Component::finalizeConnections(Component& root)
298 {
299 if (!isObjectUpToDateWithProperties()){
300 // if edits occur between construction and connect() this is
301 // the last chance to finalize before addToSystem.
302 finalizeFromProperties();
303 }
304
305 for (auto& it : _socketsTable) {
306 auto& socket = it.second;
307 try {
308 socket->finalizeConnection(root);
309 }
310 catch (const std::exception& x) {
311 OPENSIM_THROW_FRMOBJ(Exception, "Failed to connect Socket '" +
312 socket->getName() + "' of type " +
313 socket->getConnecteeTypeName() +
314 " (details: " + x.what() + ").");
315 }
316 }
317
318 for (auto& it : _inputsTable) {
319 auto& input = it.second;
320 try {
321 input->finalizeConnection(root);
322 }
323 catch (const std::exception& x) {
324 OPENSIM_THROW_FRMOBJ(Exception, "Failed to connect Input '" +
325 input->getName() + "' of type " + input->getConnecteeTypeName()
326 + " (details: " + x.what() + ").");
327 }
328 }
329
330 // Allow derived Components to handle/check their connections and also
331 // override the order in which its subcomponents are ordered when
332 // adding subcomponents to the System
333 extendFinalizeConnections(root);
334
335 // Allow subcomponents to form their connections
336 componentsFinalizeConnections(root);
337
338 // Forming connections changes the Socket which is a property
339 // Remark as upToDate.
340 setObjectIsUpToDateWithProperties();
341 }
342
343 // invoke connect on all (sub)components of this component
componentsFinalizeConnections(Component & root)344 void Component::componentsFinalizeConnections(Component& root)
345 {
346 // enable the subcomponents the opportunity to connect themselves
347 for (unsigned int i = 0; i<_memberSubcomponents.size(); ++i) {
348 _memberSubcomponents[i].upd()->finalizeConnections(root);
349 }
350 for(unsigned int i=0; i<_propertySubcomponents.size(); ++i){
351 _propertySubcomponents[i].get()->finalizeConnections(root);
352 }
353 for (unsigned int i = 0; i<_adoptedSubcomponents.size(); ++i) {
354 _adoptedSubcomponents[i].upd()->finalizeConnections(root);
355 }
356 }
357
clearConnections()358 void Component::clearConnections()
359 {
360 // First give the subcomponents the opportunity to disconnect themselves
361 for (unsigned int i = 0; i<_memberSubcomponents.size(); i++) {
362 _memberSubcomponents[i]->clearConnections();
363 }
364 for (unsigned int i = 0; i<_propertySubcomponents.size(); i++){
365 _propertySubcomponents[i]->clearConnections();
366 }
367 for (unsigned int i = 0; i<_adoptedSubcomponents.size(); i++) {
368 _adoptedSubcomponents[i]->clearConnections();
369 }
370
371 //Now cycle through and disconnect all sockets for this component
372 for (auto& it : _socketsTable) {
373 it.second->disconnect();
374 }
375
376 // Must also clear the input's connections.
377 for (auto& it : _inputsTable) {
378 it.second->disconnect();
379 }
380
381 //now clear all the stored system indices from this component
382 reset();
383 }
384
addToSystem(SimTK::MultibodySystem & system) const385 void Component::addToSystem(SimTK::MultibodySystem& system) const
386 {
387 // If being asked to be added to the same System that it is already
388 // a part, there is nothing to be done.
389 if (hasSystem() && (&getSystem() == &system)) {
390 return;
391 }
392 baseAddToSystem(system);
393 extendAddToSystem(system);
394 componentsAddToSystem(system);
395 extendAddToSystemAfterSubcomponents(system);
396 }
397
398 // Base class implementation of virtual method.
399 // Every Component owns an underlying SimTK::Measure
400 // which is a ComponentMeasure<T> and is added to the System's default
401 // subsystem. That measure is used only for the side effect of its realize()
402 // methods being called; its value is not used.
baseAddToSystem(SimTK::MultibodySystem & system) const403 void Component::baseAddToSystem(SimTK::MultibodySystem& system) const
404 {
405 if (!isObjectUpToDateWithProperties()) {
406 std::string msg = "Component " + getConcreteClassName() + "::" + getName();
407 msg += " cannot extendAddToSystem until it is up-to-date with its properties.";
408
409 throw Exception(msg);
410 }
411
412 // Clear cached list of all related StateVariables if any from a previous
413 // System.
414 _allStateVariables.clear();
415
416 // Briefly get write access to the Component to record some
417 // information associated with the System; that info is const after this.
418 Component* mutableThis = const_cast<Component *>(this);
419 mutableThis->_system = system;
420
421 // Allocate the ComponentMeasure, point it to this Component for
422 // making realize() calls, and add it to the system's default subsystem.
423 ComponentMeasure<double> mcMeasure(system.updDefaultSubsystem(), *this);
424 mutableThis->_simTKcomponentIndex = mcMeasure.getSubsystemMeasureIndex();
425 }
426
componentsAddToSystem(SimTK::MultibodySystem & system) const427 void Component::componentsAddToSystem(SimTK::MultibodySystem& system) const
428 {
429 // If _orderedSubcomponents is specified, then use this Component's
430 // specification for the order in which subcomponents are added. At a
431 // minimum the order for all immediate subcomponents must be specified.
432 if (_orderedSubcomponents.size() >= getNumImmediateSubcomponents()) {
433 for (const auto& compRef : _orderedSubcomponents) {
434 compRef->addToSystem(system);
435 }
436 }
437 else if (_orderedSubcomponents.size() == 0) {
438 // Otherwise, invoke on all immediate subcomponents in tree order
439 auto mySubcomponents = getImmediateSubcomponents();
440 for (const auto& compRef : mySubcomponents) {
441 compRef->addToSystem(system);
442 }
443 }
444 else {
445 OPENSIM_THROW_FRMOBJ(Exception,
446 "_orderedSubcomponents specified, but its size does not reflect the "
447 "the number of immediate subcomponents. Verify that you have included "
448 "all immediate subcomponents in the ordered list."
449 )
450 }
451 }
452
initStateFromProperties(SimTK::State & state) const453 void Component::initStateFromProperties(SimTK::State& state) const
454 {
455 extendInitStateFromProperties(state);
456 componentsInitStateFromProperties(state);
457 }
458
componentsInitStateFromProperties(SimTK::State & state) const459 void Component::componentsInitStateFromProperties(SimTK::State& state) const
460 {
461 for (unsigned int i = 0; i<_memberSubcomponents.size(); ++i)
462 _memberSubcomponents[i]->initStateFromProperties(state);
463 for (unsigned int i = 0; i<_propertySubcomponents.size(); ++i)
464 _propertySubcomponents[i]->initStateFromProperties(state);
465 for (unsigned int i = 0; i<_adoptedSubcomponents.size(); ++i)
466 _adoptedSubcomponents[i]->initStateFromProperties(state);
467 }
468
setPropertiesFromState(const SimTK::State & state)469 void Component::setPropertiesFromState(const SimTK::State& state)
470 {
471 extendSetPropertiesFromState(state);
472 componentsSetPropertiesFromState(state);
473 }
474
componentsSetPropertiesFromState(const SimTK::State & state)475 void Component::componentsSetPropertiesFromState(const SimTK::State& state)
476 {
477 for (unsigned int i = 0; i<_memberSubcomponents.size(); ++i)
478 _memberSubcomponents[i]->setPropertiesFromState(state);
479 for (unsigned int i = 0; i<_propertySubcomponents.size(); ++i)
480 _propertySubcomponents[i]->setPropertiesFromState(state);
481 for (unsigned int i = 0; i<_adoptedSubcomponents.size(); ++i)
482 _adoptedSubcomponents[i]->setPropertiesFromState(state);
483 }
484
485 // Base class implementation of virtual method. Note that we're not handling
486 // subcomponents here; this method gets called from extendRealizeAcceleration()
487 // which will be invoked for each (sub) component by its own ComponentMeasure.
computeStateVariableDerivatives(const SimTK::State & s) const488 void Component::computeStateVariableDerivatives(const SimTK::State& s) const
489 {
490 int nsv = getNumStateVariablesAddedByComponent();
491 if(nsv > 0){
492 int nasv = 0;
493 std::map<std::string, StateVariableInfo>::const_iterator it;
494 for(it = _namedStateVariableInfo.begin();
495 it != _namedStateVariableInfo.end(); ++it){
496 const StateVariable& sv = *it->second.stateVariable;
497 const AddedStateVariable *asv =
498 dynamic_cast<const AddedStateVariable *>(&sv);
499 if(asv) nasv++;
500 }
501 if(nasv > 0){
502 std::stringstream msg;
503 msg << "Component " + getConcreteClassName()+"::"+getName();
504 msg << " added " << nasv << " state variables and ";
505 msg << " must specify their derivatives." << std::endl;
506
507 throw Exception(msg.str());
508 }
509 }
510 }
511
512
513 void Component::
addModelingOption(const std::string & optionName,int maxFlagValue) const514 addModelingOption(const std::string& optionName, int maxFlagValue) const
515 {
516 // don't add modeling option if there is another state with the same
517 // name for this component
518 std::map<std::string, ModelingOptionInfo>::const_iterator it;
519 it = _namedModelingOptionInfo.find(optionName);
520 if(it != _namedModelingOptionInfo.end())
521 throw Exception("Component::addModelingOption: Modeling option '"
522 + optionName + "' already exists.");
523 // assign a "slot" for a modeling option by name
524 // modeling option index will be invalid by default
525 // upon allocation during realizeTopology the index will be set
526 _namedModelingOptionInfo[optionName] = ModelingOptionInfo(maxFlagValue);
527 }
528
addStateVariable(const std::string & stateVariableName,const SimTK::Stage & invalidatesStage,bool isHidden) const529 void Component::addStateVariable(const std::string& stateVariableName,
530 const SimTK::Stage& invalidatesStage,
531 bool isHidden) const
532 {
533 if( (invalidatesStage < Stage::Position) ||
534 (invalidatesStage > Stage::Dynamics)) {
535 throw Exception("Component::addStateVariable: invalidatesStage "
536 "must be Position, Velocity or Dynamics.");
537 }
538 // Allocate space for a new state variable
539 AddedStateVariable* asv =
540 new AddedStateVariable(stateVariableName, *this, invalidatesStage, isHidden);
541 // Add it to the Component and let it take ownership
542 addStateVariable(asv);
543 }
544
545
addStateVariable(Component::StateVariable * stateVariable) const546 void Component::addStateVariable(Component::StateVariable* stateVariable) const
547 {
548 const std::string& stateVariableName = stateVariable->getName();
549 // don't add state if there is another state variable with the same name
550 // for this component
551 std::map<std::string, StateVariableInfo>::const_iterator it;
552 it = _namedStateVariableInfo.find(stateVariableName);
553 if(it != _namedStateVariableInfo.end()){
554 throw Exception("Component::addStateVariable: State variable '" +
555 stateVariableName + "' already exists.");
556 }
557
558 int order = (int)_namedStateVariableInfo.size();
559
560 // assign a "slot" for a state variable by name
561 // state variable index will be invalid by default
562 // upon allocation during realizeTopology the index will be set
563 _namedStateVariableInfo[stateVariableName] =
564 StateVariableInfo(stateVariable, order);
565
566 const AddedStateVariable* asv =
567 dynamic_cast<const Component::AddedStateVariable *>(stateVariable);
568 // Now automatically add a cache variable to hold the derivative
569 // to enable a similar interface for setting and getting the derivatives
570 // based on the creator specified state name
571 if(asv){
572 addCacheVariable(stateVariableName+"_deriv", 0.0, Stage::Dynamics);
573 }
574
575 }
576
577
addDiscreteVariable(const std::string & discreteVariableName,SimTK::Stage invalidatesStage) const578 void Component::addDiscreteVariable(const std::string& discreteVariableName,
579 SimTK::Stage invalidatesStage) const
580 {
581 // don't add discrete var if there is another discrete variable with the
582 // same name for this component
583 std::map<std::string, DiscreteVariableInfo>::const_iterator it;
584 it = _namedDiscreteVariableInfo.find(discreteVariableName);
585 if(it != _namedDiscreteVariableInfo.end()){
586 throw Exception("Component::addDiscreteVariable: discrete variable '" +
587 discreteVariableName + "' already exists.");
588 }
589 // assign "slots" for the discrete variables by name
590 // discrete variable indices will be invalid by default
591 // upon allocation during realizeTopology the indices will be set
592 _namedDiscreteVariableInfo[discreteVariableName] =
593 DiscreteVariableInfo(invalidatesStage);
594 }
595
596 // Get the value of a ModelingOption flag for this Component.
597 int Component::
getModelingOption(const SimTK::State & s,const std::string & name) const598 getModelingOption(const SimTK::State& s, const std::string& name) const
599 {
600 std::map<std::string, ModelingOptionInfo>::const_iterator it;
601 it = _namedModelingOptionInfo.find(name);
602
603 if(it != _namedModelingOptionInfo.end()) {
604 SimTK::DiscreteVariableIndex dvIndex = it->second.index;
605 return SimTK::Value<int>::downcast(
606 getDefaultSubsystem().getDiscreteVariable(s, dvIndex)).get();
607 } else {
608 std::stringstream msg;
609 msg << "Component::getModelingOption: ERR- name '" << name
610 << "' not found.\n "
611 << "for component '"<< getName() << "' of type "
612 << getConcreteClassName();
613 throw Exception(msg.str(),__FILE__,__LINE__);
614 return -1;
615 }
616 }
617
618 // Set the value of a discrete variable allocated by this Component by name.
619 void Component::
setModelingOption(SimTK::State & s,const std::string & name,int flag) const620 setModelingOption(SimTK::State& s, const std::string& name, int flag) const
621 {
622 std::map<std::string, ModelingOptionInfo>::const_iterator it;
623 it = _namedModelingOptionInfo.find(name);
624
625 if(it != _namedModelingOptionInfo.end()) {
626 SimTK::DiscreteVariableIndex dvIndex = it->second.index;
627 if(flag > it->second.maxOptionValue){
628 std::stringstream msg;
629 msg << "Component::setModelingOption: "<< name
630 << " flag cannot exceed "<< it->second.maxOptionValue <<".\n ";
631 throw Exception(msg.str(),__FILE__,__LINE__);
632 }
633
634 SimTK::Value<int>::downcast(
635 getDefaultSubsystem().updDiscreteVariable(s, dvIndex)).upd() = flag;
636 } else {
637 std::stringstream msg;
638 msg << "Component::setModelingOption: modeling option " << name
639 << " not found.\n ";
640 throw Exception(msg.str(),__FILE__,__LINE__);
641 }
642 }
643
printComponentsMatching(const std::string & substring) const644 unsigned Component::printComponentsMatching(const std::string& substring) const
645 {
646 auto components = getComponentList();
647 components.setFilter(ComponentFilterAbsolutePathNameContainsString(substring));
648 unsigned count = 0;
649 for (const auto& comp : components) {
650 std::cout << comp.getAbsolutePathString() << std::endl;
651 ++count;
652 }
653 return count;
654 }
655
getNumStateVariables() const656 int Component::getNumStateVariables() const
657 {
658 // Must have already called initSystem.
659 OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
660
661 //Get the number of state variables added (or exposed) by this Component
662 int ns = getNumStateVariablesAddedByComponent();
663 // And then include the states of its subcomponents
664 for (unsigned int i = 0; i<_memberSubcomponents.size(); i++)
665 ns += _memberSubcomponents[i]->getNumStateVariables();
666
667 for(unsigned int i=0; i<_propertySubcomponents.size(); i++)
668 ns += _propertySubcomponents[i]->getNumStateVariables();
669
670 for (unsigned int i = 0; i<_adoptedSubcomponents.size(); i++)
671 ns += _adoptedSubcomponents[i]->getNumStateVariables();
672
673 return ns;
674 }
675
676
getOwner() const677 const Component& Component::getOwner() const
678 {
679 if (!hasOwner()) {
680 std::string msg = "Component '" + getName() + "'::getOwner(). " +
681 "Has no owner assigned.\n" +
682 "Make sure the component was added to the Model " +
683 "(or another component).";
684 throw Exception(msg);
685 }
686 return _owner.getRef();
687 }
688
hasOwner() const689 bool Component::hasOwner() const
690 {
691 return !_owner.empty();
692 }
693
getRoot() const694 const Component& Component::getRoot() const {
695 const Component* root = this;
696 while (root->hasOwner()) {
697 root = &root->getOwner();
698 }
699 return *root;
700 }
701
setOwner(const Component & owner)702 void Component::setOwner(const Component& owner)
703 {
704 if (&owner == this) {
705 std::string msg = "Component '" + getName() + "'::setOwner(). " +
706 "Attempted to set itself as its owner.";
707 throw Exception(msg);
708 }
709 else if (_owner.get() == &owner) {
710 return;
711 }
712
713 _owner.reset(&owner);
714 }
715
getAbsolutePathString() const716 std::string Component::getAbsolutePathString() const
717 {
718 if (!hasOwner()) return "/";
719 std::string absPathName("/" + getName());
720
721 const Component* up = this;
722
723 while (up && up->hasOwner()) {
724 up = &up->getOwner();
725 if (up->hasOwner())
726 absPathName.insert(0, "/" + up->getName());
727 }
728
729 return absPathName;
730
731 }
732
getAbsolutePath() const733 ComponentPath Component::getAbsolutePath() const
734 {
735 if (!hasOwner()) return ComponentPath({}, true);
736
737 std::vector<std::string> pathVec;
738 pathVec.push_back(getName());
739
740 const Component* up = this;
741
742 while (up && up->hasOwner()) {
743 up = &up->getOwner();
744 if (up->hasOwner())
745 pathVec.insert(pathVec.begin(), up->getName());
746 }
747
748 return ComponentPath(pathVec, true);
749 }
750
getRelativePathString(const Component & wrt) const751 std::string Component::getRelativePathString(const Component& wrt) const
752 {
753 return getRelativePath(wrt).toString();
754 }
755
getRelativePath(const Component & wrt) const756 ComponentPath Component::getRelativePath(const Component& wrt) const
757 {
758 ComponentPath thisP = getAbsolutePath();
759 ComponentPath wrtP = wrt.getAbsolutePath();
760
761 return thisP.formRelativePath(wrtP);
762 }
763
764 const Component::StateVariable* Component::
traverseToStateVariable(const std::string & pathName) const765 traverseToStateVariable(const std::string& pathName) const
766 {
767 // Must have already called initSystem.
768 OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
769
770 ComponentPath svPath(pathName);
771
772 const StateVariable* found = nullptr;
773 if (svPath.getNumPathLevels() == 1) {
774 // There was no slash. The state variable should be in this component.
775 auto it = _namedStateVariableInfo.find(pathName);
776 if (it != _namedStateVariableInfo.end()) {
777 return it->second.stateVariable.get();
778 }
779 } else if (svPath.getNumPathLevels() > 1) {
780 const auto& compPath = svPath.getParentPath();
781 const Component* comp = traversePathToComponent<Component>(compPath);
782 if (comp) {
783 // This is the leaf of the path:
784 const auto& varName = svPath.getComponentName();
785 found = comp->traverseToStateVariable(varName);
786 }
787 }
788 return found;
789 }
790
791 // Get the names of "continuous" state variables maintained by the Component and
792 // its subcomponents.
getStateVariableNames() const793 Array<std::string> Component::getStateVariableNames() const
794 {
795 // Must have already called initSystem.
796 OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
797
798 Array<std::string> stateNames = getStateVariableNamesAddedByComponent();
799
800 for (int i = 0; i < stateNames.size(); ++i) {
801 stateNames[i] = (getAbsolutePathString() + "/" + stateNames[i]);
802 }
803
804 for (auto& comp : getComponentList<Component>()) {
805 const std::string& pathName = comp.getAbsolutePathString();// *this);
806 Array<std::string> subStateNames =
807 comp.getStateVariableNamesAddedByComponent();
808 for (int i = 0; i < subStateNames.size(); ++i) {
809 stateNames.append(pathName + "/" + subStateNames[i]);
810 }
811 }
812
813 return stateNames;
814 }
815
816 // Get the value of a state variable allocated by this Component.
817 double Component::
getStateVariableValue(const SimTK::State & s,const std::string & name) const818 getStateVariableValue(const SimTK::State& s, const std::string& name) const
819 {
820 // Must have already called initSystem.
821 OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
822
823 // find the state variable with this component or its subcomponents
824 const StateVariable* rsv = traverseToStateVariable(name);
825 if (rsv) {
826 return rsv->getValue(s);
827 }
828
829 std::stringstream msg;
830 msg << "Component::getStateVariableValue: ERR- state named '" << name
831 << "' not found in " << getName() << " of type " << getConcreteClassName();
832 throw Exception(msg.str(),__FILE__,__LINE__);
833
834 return SimTK::NaN;
835 }
836
837 // Get the value of a state variable derivative computed by this Component.
838 double Component::
getStateVariableDerivativeValue(const SimTK::State & state,const std::string & name) const839 getStateVariableDerivativeValue(const SimTK::State& state,
840 const std::string& name) const
841 {
842 // Must have already called initSystem.
843 OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
844
845 computeStateVariableDerivatives(state);
846
847 std::map<std::string, StateVariableInfo>::const_iterator it;
848 it = _namedStateVariableInfo.find(name);
849
850 if(it != _namedStateVariableInfo.end()) {
851 return it->second.stateVariable->getDerivative(state);
852 }
853 else{
854 // otherwise find the component that variable belongs to
855 const StateVariable* rsv = traverseToStateVariable(name);
856 if (rsv) {
857 return rsv->getDerivative(state);
858 }
859 }
860
861 std::stringstream msg;
862 msg << "Component::getStateVariableDerivative: ERR- variable name '" << name
863 << "' not found.\n "
864 << getName() << " of type " << getConcreteClassName()
865 << " has " << getNumStateVariables() << " states.";
866 throw Exception(msg.str(),__FILE__,__LINE__);
867 return SimTK::NaN;
868 }
869
870 // Set the value of a state variable allocated by this Component given its name
871 // for this component.
872 void Component::
setStateVariableValue(State & s,const std::string & name,double value) const873 setStateVariableValue(State& s, const std::string& name, double value) const
874 {
875 // Must have already called initSystem.
876 OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
877
878 // find the state variable
879 const StateVariable* rsv = traverseToStateVariable(name);
880
881 if(rsv){ // find required rummaging through the state variable names
882 return rsv->setValue(s, value);
883 }
884
885 std::stringstream msg;
886 msg << "Component::setStateVariable: ERR- state named '" << name
887 << "' not found in " << getName() << " of type "
888 << getConcreteClassName() << ".\n";
889 throw Exception(msg.str(),__FILE__,__LINE__);
890 }
891
isAllStatesVariablesListValid() const892 bool Component::isAllStatesVariablesListValid() const
893 {
894 int nsv = getNumStateVariables();
895 // Consider the list of all StateVariables to be valid if all of
896 // the following conditions are true:
897 // 1. Component is up-to-date with its Properties
898 // 2. a System has been associated with the list of StateVariables
899 // 3. The list of all StateVariables is correctly sized (initialized)
900 // 4. The System associated with the StateVariables is the current System
901 // TODO: Enable the isObjectUpToDateWithProperties() check when computing
902 // the path of the GeomtryPath does not involve updating its PathPointSet.
903 // This change dirties the GeometryPath which is a property of a Muscle which
904 // is property of the Model. Therefore, during integration the Model is not
905 // up-to-date and this causes a rebuilding of the cached StateVariables list.
906 // See GeometryPath::computePath() for the corresponding TODO that must be
907 // addressed before we can re-enable the isObjectUpToDateWithProperties
908 // check.
909 // It has been verified that the adding Components will invalidate the state
910 // variables associated with the Model and force the list to be rebuilt.
911 bool valid = //isObjectUpToDateWithProperties() && // 1.
912 !_statesAssociatedSystem.empty() && // 2.
913 _allStateVariables.size() == nsv && // 3.
914 getSystem().isSameSystem(_statesAssociatedSystem.getRef()); // 4.
915
916 return valid;
917 }
918
919
920 // Get all values of the state variables allocated by this Component. Includes
921 // state variables allocated by its subcomponents.
922 SimTK::Vector Component::
getStateVariableValues(const SimTK::State & state) const923 getStateVariableValues(const SimTK::State& state) const
924 {
925 // Must have already called initSystem.
926 OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
927
928 int nsv = getNumStateVariables();
929 // if the StateVariables are invalid (see above) rebuild the list
930 if (!isAllStatesVariablesListValid()) {
931 _statesAssociatedSystem.reset(&getSystem());
932 _allStateVariables.clear();
933 _allStateVariables.resize(nsv);
934 Array<std::string> names = getStateVariableNames();
935 for (int i = 0; i < nsv; ++i)
936 _allStateVariables[i].reset(traverseToStateVariable(names[i]));
937 }
938
939 Vector stateVariableValues(nsv, SimTK::NaN);
940 for(int i=0; i<nsv; ++i){
941 stateVariableValues[i]= _allStateVariables[i]->getValue(state);
942 }
943
944 return stateVariableValues;
945 }
946
947 // Set all values of the state variables allocated by this Component. Includes
948 // state variables allocated by its subcomponents.
949 void Component::
setStateVariableValues(SimTK::State & state,const SimTK::Vector & values) const950 setStateVariableValues(SimTK::State& state,
951 const SimTK::Vector& values) const
952 {
953 // Must have already called initSystem.
954 OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
955
956 int nsv = getNumStateVariables();
957
958 SimTK_ASSERT(values.size() == nsv,
959 "Component::setStateVariableValues() number values does not match the "
960 "number of state variables.");
961
962 // if the StateVariables are invalid (see above) rebuild the list
963 if (!isAllStatesVariablesListValid()) {
964 _statesAssociatedSystem.reset(&getSystem());
965 _allStateVariables.clear();
966 _allStateVariables.resize(nsv);
967 Array<std::string> names = getStateVariableNames();
968 for (int i = 0; i < nsv; ++i)
969 _allStateVariables[i].reset(traverseToStateVariable(names[i]));
970 }
971
972 for(int i=0; i<nsv; ++i){
973 _allStateVariables[i]->setValue(state, values[i]);
974 }
975 }
976
977 // Set the derivative of a state variable computed by this Component by name.
978 void Component::
setStateVariableDerivativeValue(const State & state,const std::string & name,double value) const979 setStateVariableDerivativeValue(const State& state,
980 const std::string& name, double value) const
981 {
982 std::map<std::string, StateVariableInfo>::const_iterator it;
983 it = _namedStateVariableInfo.find(name);
984
985 if(it != _namedStateVariableInfo.end()) {
986 const StateVariable& sv = *it->second.stateVariable;
987 sv.setDerivative(state, value);
988 }
989 else{
990 std::stringstream msg;
991 msg << "Component::setStateVariableDerivative: ERR- name '" << name
992 << "' not found.\n "
993 << getName() << " of type " << getConcreteClassName()
994 << " has " << getNumStateVariables() << " states.";
995 throw Exception(msg.str(),__FILE__,__LINE__);
996 }
997 }
998
999 // Get the value of a discrete variable allocated by this Component by name.
1000 double Component::
getDiscreteVariableValue(const SimTK::State & s,const std::string & name) const1001 getDiscreteVariableValue(const SimTK::State& s, const std::string& name) const
1002 {
1003 // Must have already called initSystem.
1004 OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
1005
1006 std::map<std::string, DiscreteVariableInfo>::const_iterator it;
1007 it = _namedDiscreteVariableInfo.find(name);
1008
1009 if(it != _namedDiscreteVariableInfo.end()) {
1010 SimTK::DiscreteVariableIndex dvIndex = it->second.index;
1011 return SimTK::Value<double>::downcast(
1012 getDefaultSubsystem().getDiscreteVariable(s, dvIndex)).get();
1013 } else {
1014 std::stringstream msg;
1015 msg << "Component::getDiscreteVariable: ERR- name '" << name
1016 << "' not found.\n "
1017 << "for component '"<< getName() << "' of type "
1018 << getConcreteClassName();
1019 throw Exception(msg.str(),__FILE__,__LINE__);
1020 return SimTK::NaN;
1021 }
1022 }
1023
1024 // Set the value of a discrete variable allocated by this Component by name.
1025 void Component::
setDiscreteVariableValue(SimTK::State & s,const std::string & name,double value) const1026 setDiscreteVariableValue(SimTK::State& s, const std::string& name, double value) const
1027 {
1028 // Must have already called initSystem.
1029 OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
1030
1031 std::map<std::string, DiscreteVariableInfo>::const_iterator it;
1032 it = _namedDiscreteVariableInfo.find(name);
1033
1034 if(it != _namedDiscreteVariableInfo.end()) {
1035 SimTK::DiscreteVariableIndex dvIndex = it->second.index;
1036 SimTK::Value<double>::downcast(
1037 getDefaultSubsystem().updDiscreteVariable(s, dvIndex)).upd() = value;
1038 } else {
1039 std::stringstream msg;
1040 msg << "Component::setDiscreteVariable: ERR- name '" << name
1041 << "' not found.\n "
1042 << "for component '"<< getName() << "' of type "
1043 << getConcreteClassName();
1044 throw Exception(msg.str(),__FILE__,__LINE__);
1045 }
1046 }
1047
constructOutputForStateVariable(const std::string & name)1048 bool Component::constructOutputForStateVariable(const std::string& name)
1049 {
1050 auto func = [name](const Component* comp,
1051 const SimTK::State& s, const std::string&,
1052 double& result) -> void {
1053 result = comp->getStateVariableValue(s, name);
1054 };
1055 return constructOutput<double>(name, func, SimTK::Stage::Model);
1056 }
1057
1058 // helper method to specify the order of subcomponents.
setNextSubcomponentInSystem(const Component & sub) const1059 void Component::setNextSubcomponentInSystem(const Component& sub) const
1060 {
1061 auto it =
1062 std::find(_orderedSubcomponents.begin(), _orderedSubcomponents.end(),
1063 SimTK::ReferencePtr<const Component>(&sub));
1064 if (it == _orderedSubcomponents.end()) {
1065 _orderedSubcomponents.push_back(SimTK::ReferencePtr<const Component>(&sub));
1066 }
1067 }
1068
updateFromXMLNode(SimTK::Xml::Element & node,int versionNumber)1069 void Component::updateFromXMLNode(SimTK::Xml::Element& node, int versionNumber)
1070 {
1071 // During deserialization, some components' clone() may
1072 // finalizeFromProperties(). Upating from XML can then cause stale pointers.
1073 // We must make sure to clear any pointers to properties that may exist
1074 // in this component.
1075 reset();
1076 if (versionNumber < XMLDocument::getLatestVersion()) {
1077 if (versionNumber < 30500) {
1078 // In 3.3 and earlier, spaces in names were tolerated. Spaces are
1079 // no longer acceptable in Component names.
1080 if (node.hasAttribute("name")) {
1081 auto name = node.getRequiredAttribute("name").getValue();
1082 if (name.find_first_of("\n\t ") < std::string::npos) {
1083 std::cout << getConcreteClassName() << " name '" << name
1084 << "' contains whitespace. ";
1085 name.erase(
1086 std::remove_if(name.begin(), name.end(), ::isspace),
1087 name.end() );
1088 node.setAttributeValue("name", name);
1089 std::cout << "It was renamed '" << name << "'." << std::endl;
1090 }
1091 }
1092 else { // As 4.0 all Components must have a name. If none, assign one.
1093 // Note: in finalizeFromProperties(), the Component will ensure
1094 // that names are unique by travesing its list of subcomponents
1095 // and renaming any duplicates.
1096 node.setAttributeValue("name",
1097 IO::Lowercase(getConcreteClassName()));
1098 }
1099 }
1100 if (versionNumber < 30508) {
1101 // Here's an example of the change this function might make:
1102 // Previous: <connectors>
1103 // <Connector_PhysicalFrame_ name="parent">
1104 // <connectee_name>...</connectee_name>
1105 // </Connector_PhysicalFrame_>
1106 // </connectors>
1107 // New: <connector_parent_connectee_name>...
1108 // </connector_parent_connectee_name>
1109 //
1110 XMLDocument::updateConnectors30508(node);
1111 }
1112
1113 if(versionNumber < 30510) {
1114 // Before -- <connector_....> ... </connector_...>
1115 // After -- <socket_...> ... </socket_...>
1116 for(auto iter = node.element_begin();
1117 iter != node.element_end();
1118 ++iter) {
1119 std::string oldName{"connector"};
1120 std::string newName{"socket"};
1121 auto tagname = iter->getElementTag();
1122 auto pos = tagname.find(oldName);
1123 if(pos != std::string::npos) {
1124 tagname.replace(pos, oldName.length(), newName);
1125 iter->setElementTag(tagname);
1126 }
1127 }
1128
1129 }
1130 if (versionNumber <= 30516) {
1131 // Rename xml tags for socket_*_connectee_name to socket_*
1132 std::string connecteeNameString = "_connectee_name";
1133 for (auto iter = node.element_begin();
1134 iter != node.element_end();
1135 ++iter) {
1136 auto tagname = iter->getElementTag();
1137 if (std::regex_match(tagname, std::regex("(socket_|input_)(.*)(_connectee_name)"))) {
1138 auto pos = tagname.find(connecteeNameString);
1139 if (pos != std::string::npos) {
1140 tagname.replace(pos, connecteeNameString.length(), "");
1141 iter->setElementTag(tagname);
1142 }
1143 }
1144 else if (std::regex_match(tagname, std::regex("(input_)(.*)(_connectee_names)"))) {
1145 auto pos = tagname.find(connecteeNameString);
1146 if (pos != std::string::npos) {
1147 tagname.replace(pos, connecteeNameString.length()+1, "");
1148 iter->setElementTag(tagname);
1149 }
1150 }
1151 }
1152 }
1153 }
1154 Super::updateFromXMLNode(node, versionNumber);
1155 }
1156
1157 // mark components owned as properties as subcomponents
markPropertiesAsSubcomponents()1158 void Component::markPropertiesAsSubcomponents()
1159 {
1160 // Method can be invoked for either constructing a new Component
1161 // or the properties have been modified. In the latter case
1162 // we must make sure that pointers to old properties are cleared
1163 _propertySubcomponents.clear();
1164
1165 // Now mark properties that are Components as subcomponents
1166 //loop over all its properties
1167 for (int i = 0; i < getNumProperties(); ++i) {
1168 auto& prop = getPropertyByIndex(i);
1169 // check if property is of type Object
1170 if (prop.isObjectProperty()) {
1171 // a property is a list so cycle through its contents
1172 for (int j = 0; j < prop.size(); ++j) {
1173 const Object& obj = prop.getValueAsObject(j);
1174 // if the object is a Component mark it
1175 if (const Component* comp = dynamic_cast<const Component*>(&obj) ) {
1176 markAsPropertySubcomponent(comp);
1177 }
1178 else {
1179 // otherwise it may be a Set (of objects), and
1180 // would prefer to do something like this to test:
1181 // Set<Component>* objects = dynamic_cast<Set<Component>*>(obj)
1182 // Instead we can see if the object has a property called
1183 // "objects" which is a PropertyObjArray used by Set<T>.
1184 // knowing the object Type is useful for debugging
1185 // and it could be used to strengthen the test (e.g. scan
1186 // for "Set" in the type name).
1187 std::string objType = obj.getConcreteClassName();
1188 if (obj.hasProperty("objects")) {
1189 // get the PropertyObjArray if the object has one
1190 auto& objectsProp = obj.getPropertyByName("objects");
1191 // loop over the objects in the PropertyObjArray
1192 for (int k = 0; k < objectsProp.size(); ++k) {
1193 const Object& obj = objectsProp.getValueAsObject(k);
1194 // if the object is a Component mark it
1195 if (const Component* comp = dynamic_cast<const Component*>(&obj) )
1196 markAsPropertySubcomponent(comp);
1197 } // loop over objects and mark it if it is a component
1198 } // end if property is a Set with "objects" inside
1199 } // end of if/else property value is an Object or something else
1200 } // loop over the property list
1201 } // end if property is an Object
1202 } // loop over properties
1203 }
1204
1205 // mark a Component as a subcomponent of this one. If already a
1206 // subcomponent, it is not added to the list again.
markAsPropertySubcomponent(const Component * component)1207 void Component::markAsPropertySubcomponent(const Component* component)
1208 {
1209 // Only add if the component is not already a part of this Component
1210 SimTK::ReferencePtr<Component> compRef(const_cast<Component*>(component));
1211 auto it =
1212 std::find(_propertySubcomponents.begin(), _propertySubcomponents.end(), compRef);
1213 if ( it == _propertySubcomponents.end() ){
1214 // Must reconstruct the reference pointer in place in order
1215 // to invoke move constructor from SimTK::Array::push_back
1216 // otherwise it will copy and reset the Component pointer to null.
1217 _propertySubcomponents.push_back(
1218 SimTK::ReferencePtr<Component>(const_cast<Component*>(component)));
1219 }
1220 else{
1221 auto compPath = component->getAbsolutePathString();
1222 auto foundPath = it->get()->getAbsolutePathString();
1223 OPENSIM_THROW( ComponentAlreadyPartOfOwnershipTree,
1224 component->getName(), getName());
1225 }
1226
1227 compRef->setOwner(*this);
1228 }
1229
1230 // Include another Component as a subcomponent of this one. If already a
1231 // subcomponent, it is not added to the list again.
adoptSubcomponent(Component * subcomponent)1232 void Component::adoptSubcomponent(Component* subcomponent)
1233 {
1234 OPENSIM_THROW_IF(subcomponent->hasOwner(),
1235 ComponentAlreadyPartOfOwnershipTree,
1236 subcomponent->getName(), this->getName());
1237
1238 //get the top-level component
1239 const Component* top = this;
1240 while (top->hasOwner())
1241 top = &top->getOwner();
1242
1243 // cycle through all components from the top level component
1244 // down to verify the component is not already in the tree
1245 for (auto& comp : top->getComponentList<Component>()) {
1246 OPENSIM_THROW_IF(subcomponent->hasOwner(),
1247 ComponentAlreadyPartOfOwnershipTree,
1248 subcomponent->getName(), comp.getName());
1249 }
1250
1251 subcomponent->setOwner(*this);
1252 _adoptedSubcomponents.push_back(SimTK::ClonePtr<Component>(subcomponent));
1253 }
1254
1255 std::vector<SimTK::ReferencePtr<const Component>>
getImmediateSubcomponents() const1256 Component::getImmediateSubcomponents() const
1257 {
1258 std::vector<SimTK::ReferencePtr<const Component>> mySubcomponents;
1259 for (auto& compRef : _memberSubcomponents) {
1260 mySubcomponents.push_back(
1261 SimTK::ReferencePtr<const Component>(compRef.get()) );
1262 }
1263 for (auto& compRef : _propertySubcomponents) {
1264 mySubcomponents.push_back(
1265 SimTK::ReferencePtr<const Component>(compRef.get()) );
1266 }
1267 for (auto& compRef : _adoptedSubcomponents) {
1268 mySubcomponents.push_back(
1269 SimTK::ReferencePtr<const Component>(compRef.get()) );
1270 }
1271 return mySubcomponents;
1272 }
1273
1274
getNumMemberSubcomponents() const1275 size_t Component::getNumMemberSubcomponents() const
1276 {
1277 return _memberSubcomponents.size();
1278 }
1279
getNumPropertySubcomponents() const1280 size_t Component::getNumPropertySubcomponents() const
1281 {
1282 return _propertySubcomponents.size();
1283 }
1284
getNumAdoptedSubcomponents() const1285 size_t Component::getNumAdoptedSubcomponents() const
1286 {
1287 return _adoptedSubcomponents.size();
1288 }
1289
1290
1291
getStateIndex(const std::string & name) const1292 const int Component::getStateIndex(const std::string& name) const
1293 {
1294 std::map<std::string, StateVariableInfo>::const_iterator it;
1295 it = _namedStateVariableInfo.find(name);
1296
1297 if(it != _namedStateVariableInfo.end()) {
1298 return it->second.stateVariable->getVarIndex();
1299 } else {
1300 std::stringstream msg;
1301 msg << "Component::getStateVariableSystemIndex: ERR- name '"
1302 << name << "' not found.\n "
1303 << "for component '"<< getName() << "' of type "
1304 << getConcreteClassName();
1305 throw Exception(msg.str(),__FILE__,__LINE__);
1306 return SimTK::InvalidIndex;
1307 }
1308 }
1309
1310 SimTK::SystemYIndex Component::
getStateVariableSystemIndex(const std::string & stateVariableName) const1311 getStateVariableSystemIndex(const std::string& stateVariableName) const
1312 {
1313 //const SimTK::State& s = getSystem().getDefaultState();
1314
1315 std::map<std::string, StateVariableInfo>::const_iterator it;
1316 it = _namedStateVariableInfo.find(stateVariableName);
1317
1318 if(it != _namedStateVariableInfo.end()){
1319 return it->second.stateVariable->getSystemYIndex();
1320 }
1321
1322 // Otherwise we have to search through subcomponents
1323 SimTK::SystemYIndex yix;
1324
1325 for(unsigned int i = 0; i < _propertySubcomponents.size(); ++i) {
1326 yix = _propertySubcomponents[i]->getStateVariableSystemIndex(stateVariableName);
1327 if(yix.isValid()){
1328 return yix;
1329 }
1330 }
1331
1332 if(!(yix.isValid())){
1333 throw Exception(getConcreteClassName()
1334 + "::getStateVariableSystemIndex : state variable "
1335 + stateVariableName+" has an invalid index.");
1336 }
1337
1338 return yix;
1339 }
1340
1341 const SimTK::DiscreteVariableIndex Component::
getDiscreteVariableIndex(const std::string & name) const1342 getDiscreteVariableIndex(const std::string& name) const
1343 {
1344 std::map<std::string, DiscreteVariableInfo>::const_iterator it;
1345 it = _namedDiscreteVariableInfo.find(name);
1346
1347 return it->second.index;
1348 }
1349
1350 const SimTK::CacheEntryIndex Component::
getCacheVariableIndex(const std::string & name) const1351 getCacheVariableIndex(const std::string& name) const
1352 {
1353 std::map<std::string, CacheInfo>::const_iterator it;
1354 it = _namedCacheVariableInfo.find(name);
1355
1356 return it->second.index;
1357 }
1358
1359 Array<std::string> Component::
getStateVariableNamesAddedByComponent() const1360 getStateVariableNamesAddedByComponent() const
1361 {
1362 std::map<std::string, StateVariableInfo>::const_iterator it;
1363 it = _namedStateVariableInfo.begin();
1364
1365 Array<std::string> names("",(int)_namedStateVariableInfo.size());
1366
1367 while(it != _namedStateVariableInfo.end()){
1368 names[it->second.order] = it->first;
1369 it++;
1370 }
1371 return names;
1372 }
1373
1374 //------------------------------------------------------------------------------
1375 // REALIZE TOPOLOGY
1376 //------------------------------------------------------------------------------
1377 // This is the base class implementation of a virtual method that can be
1378 // overridden by derived model components, but they *must* invoke
1379 // Super::extendRealizeTopology() as the first line in the overriding method so
1380 // that this code is executed before theirs.
1381 // This method is invoked from the ComponentMeasure associated with this
1382 // Component.
1383 // Note that subcomponent realize() methods will be invoked by their own
1384 // ComponentMeasures, so we do not need to forward to subcomponents here.
extendRealizeTopology(SimTK::State & s) const1385 void Component::extendRealizeTopology(SimTK::State& s) const
1386 {
1387 const SimTK::Subsystem& subSys = getSystem().getDefaultSubsystem();
1388
1389 Component *mutableThis = const_cast<Component*>(this);
1390
1391 // Allocate Modeling Option
1392 if(_namedModelingOptionInfo.size()>0){
1393 std::map<std::string, ModelingOptionInfo>::iterator it;
1394 for (it = (mutableThis->_namedModelingOptionInfo).begin();
1395 it !=_namedModelingOptionInfo.end(); ++it)
1396 {
1397 ModelingOptionInfo& moi = it->second;
1398 moi.index = subSys.allocateDiscreteVariable
1399 (s, SimTK::Stage::Instance, new SimTK::Value<int>(0));
1400 }
1401 }
1402
1403 // Allocate Continuous State Variables
1404 if(_namedStateVariableInfo.size()>0){
1405 SimTK::Vector zInit(1, 0.0);
1406 std::map<std::string, StateVariableInfo>::iterator it;
1407 for (it = (mutableThis->_namedStateVariableInfo).begin();
1408 it != _namedStateVariableInfo.end(); ++it)
1409 {
1410 const StateVariable& sv = *it->second.stateVariable;
1411 const AddedStateVariable* asv
1412 = dynamic_cast<const AddedStateVariable *>(&sv);
1413
1414 if(asv){// add index information for added state variables
1415 // make mutable just to update system allocated index ONLY!
1416 AddedStateVariable* masv = const_cast<AddedStateVariable*>(asv);
1417 masv->setVarIndex(subSys.allocateZ(s, zInit));
1418 masv->setSubsystemIndex(getDefaultSubsystem().getMySubsystemIndex());
1419 }
1420 }
1421 }
1422
1423 // Allocate Discrete State Variables
1424 if(_namedDiscreteVariableInfo.size()>0){
1425 std::map<std::string, DiscreteVariableInfo>::iterator it;
1426 for (it = (mutableThis->_namedDiscreteVariableInfo).begin();
1427 it != _namedDiscreteVariableInfo.end(); ++it)
1428 {
1429 DiscreteVariableInfo& dvi = it->second;
1430 dvi.index = subSys.allocateDiscreteVariable
1431 (s, dvi.invalidatesStage, new SimTK::Value<double>(0.0));
1432 }
1433 }
1434
1435 // Allocate Cache Entry in the State
1436 if(_namedCacheVariableInfo.size()>0){
1437 std::map<std::string, CacheInfo>::iterator it;
1438 for (it = (mutableThis->_namedCacheVariableInfo).begin();
1439 it != _namedCacheVariableInfo.end(); ++it){
1440 CacheInfo& ci = it->second;
1441 ci.index = subSys.allocateLazyCacheEntry
1442 (s, ci.dependsOnStage, ci.prototype->clone());
1443 }
1444 }
1445 }
1446
1447
1448 //------------------------------------------------------------------------------
1449 // REALIZE ACCELERATION
1450 //------------------------------------------------------------------------------
1451 // Base class implementation of virtual method.
1452 // Collect this component's state variable derivatives.
extendRealizeAcceleration(const SimTK::State & s) const1453 void Component::extendRealizeAcceleration(const SimTK::State& s) const
1454 {
1455 // don't bother computing derivatives if the component has no state variables
1456 if(getNumStateVariablesAddedByComponent() > 0) {
1457 const SimTK::Subsystem& subSys = getDefaultSubsystem();
1458
1459 // evaluate and set component state derivative values (in cache)
1460 computeStateVariableDerivatives(s);
1461
1462 std::map<std::string, StateVariableInfo>::const_iterator it;
1463
1464 for (it = _namedStateVariableInfo.begin();
1465 it != _namedStateVariableInfo.end(); ++it)
1466 {
1467 const StateVariable& sv = *it->second.stateVariable;
1468 const AddedStateVariable* asv =
1469 dynamic_cast<const AddedStateVariable*>(&sv);
1470 if(asv)
1471 // set corresponding system derivative value from
1472 // cached value
1473 subSys.updZDot(s)[ZIndex(asv->getVarIndex())] =
1474 asv->getDerivative(s);
1475 }
1476 }
1477 }
1478
getSystem() const1479 const SimTK::MultibodySystem& Component::getSystem() const
1480 {
1481 OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
1482 return _system.getRef();
1483 }
1484
updSystem() const1485 SimTK::MultibodySystem& Component::updSystem() const
1486 {
1487 OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
1488 return _system.getRef();
1489 }
1490
1491 //------------------------------------------------------------------------------
1492 // OTHER REALIZE METHODS
1493 //------------------------------------------------------------------------------
1494 // Base class implementations of these virtual methods do nothing now but
1495 // could do something in the future. Users must still invoke Super::realizeXXX()
1496 // as the first line in their overrides to ensure future compatibility.
extendRealizeModel(SimTK::State & state) const1497 void Component::extendRealizeModel(SimTK::State& state) const {}
extendRealizeInstance(const SimTK::State & state) const1498 void Component::extendRealizeInstance(const SimTK::State& state) const {}
extendRealizeTime(const SimTK::State & state) const1499 void Component::extendRealizeTime(const SimTK::State& state) const {}
extendRealizePosition(const SimTK::State & state) const1500 void Component::extendRealizePosition(const SimTK::State& state) const {}
extendRealizeVelocity(const SimTK::State & state) const1501 void Component::extendRealizeVelocity(const SimTK::State& state) const {}
extendRealizeDynamics(const SimTK::State & state) const1502 void Component::extendRealizeDynamics(const SimTK::State& state) const {}
extendRealizeReport(const SimTK::State & state) const1503 void Component::extendRealizeReport(const SimTK::State& state) const {}
1504
1505
1506 //override virtual methods
getValue(const SimTK::State & state) const1507 double Component::AddedStateVariable::getValue(const SimTK::State& state) const
1508 {
1509 ZIndex zix(getVarIndex());
1510 if(getSubsysIndex().isValid() && zix.isValid()){
1511 const SimTK::Vector& z = getOwner().getDefaultSubsystem().getZ(state);
1512 return z[ZIndex(zix)];
1513 }
1514
1515 std::stringstream msg;
1516 msg << "Component::AddedStateVariable::getValue: ERR- variable '"
1517 << getName() << "' is invalid for component " << getOwner().getName()
1518 << " of type " << getOwner().getConcreteClassName() <<".";
1519 throw Exception(msg.str(),__FILE__,__LINE__);
1520 return SimTK::NaN;
1521 }
1522
setValue(SimTK::State & state,double value) const1523 void Component::AddedStateVariable::setValue(SimTK::State& state, double value) const
1524 {
1525 ZIndex zix(getVarIndex());
1526 if(getSubsysIndex().isValid() && zix.isValid()){
1527 SimTK::Vector& z = getOwner().getDefaultSubsystem().updZ(state);
1528 z[ZIndex(zix)] = value;
1529 return;
1530 }
1531
1532 std::stringstream msg;
1533 msg << "Component::AddedStateVariable::setValue: ERR- variable '"
1534 << getName() << "' is invalid for component " << getOwner().getName()
1535 << " of type " << getOwner().getConcreteClassName() <<".";
1536 throw Exception(msg.str(),__FILE__,__LINE__);
1537 }
1538
1539 double Component::AddedStateVariable::
getDerivative(const SimTK::State & state) const1540 getDerivative(const SimTK::State& state) const
1541 {
1542 return getOwner().getCacheVariableValue<double>(state, getName()+"_deriv");
1543 }
1544
1545 void Component::AddedStateVariable::
setDerivative(const SimTK::State & state,double deriv) const1546 setDerivative(const SimTK::State& state, double deriv) const
1547 {
1548 return getOwner().setCacheVariableValue<double>(state, getName()+"_deriv", deriv);
1549 }
1550
1551
printSocketInfo() const1552 void Component::printSocketInfo() const {
1553 std::cout << "Sockets for component " << getName()
1554 << " of type [" << getConcreteClassName()
1555 << "] along with connectee paths:";
1556 if (getNumSockets() == 0)
1557 std::cout << " none";
1558 std::cout << std::endl;
1559
1560 size_t maxlenTypeName{}, maxlenSockName{};
1561 for(const auto& sock : _socketsTable) {
1562 maxlenTypeName = std::max(maxlenTypeName,
1563 sock.second->getConnecteeTypeName().length());
1564 maxlenSockName = std::max(maxlenSockName,
1565 sock.second->getName().length());
1566 }
1567 maxlenTypeName += 4;
1568 maxlenSockName += 1;
1569
1570 for (const auto& it : _socketsTable) {
1571 const auto& socket = it.second;
1572 std::cout << std::string(maxlenTypeName -
1573 socket->getConnecteeTypeName().length(), ' ')
1574 << "[" << socket->getConnecteeTypeName() << "]"
1575 << std::string(maxlenSockName -
1576 socket->getName().length(), ' ')
1577 << socket->getName() << " : ";
1578 if (socket->getNumConnectees() == 0) {
1579 std::cout << "no connectees" << std::endl;
1580 } else {
1581 for (unsigned i = 0; i < socket->getNumConnectees(); ++i) {
1582 std::cout << socket->getConnecteePath(i) << " ";
1583 }
1584 std::cout << std::endl;
1585 }
1586 }
1587 std::cout << std::endl;
1588 }
1589
printInputInfo() const1590 void Component::printInputInfo() const {
1591 std::cout << "Inputs for component " << getName() << " of type ["
1592 << getConcreteClassName() << "] along with connectee paths:";
1593 if (getNumInputs() == 0)
1594 std::cout << " none";
1595 std::cout << std::endl;
1596
1597 size_t maxlenTypeName{}, maxlenInputName{};
1598 for(const auto& input : _inputsTable) {
1599 maxlenTypeName = std::max(maxlenTypeName,
1600 input.second->getConnecteeTypeName().length());
1601 maxlenInputName = std::max(maxlenInputName,
1602 input.second->getName().length());
1603 }
1604 maxlenTypeName += 4;
1605 maxlenInputName += 1;
1606
1607 for (const auto& it : _inputsTable) {
1608 const auto& input = it.second;
1609 std::cout << std::string(maxlenTypeName -
1610 input->getConnecteeTypeName().length(), ' ')
1611 << "[" << input->getConnecteeTypeName() << "]"
1612 << std::string(maxlenInputName -
1613 input->getName().length(), ' ')
1614 << input->getName() << " : ";
1615 if (input->getNumConnectees() == 0 ||
1616 (input->getNumConnectees() == 1 && input->getConnecteePath().empty())) {
1617 std::cout << "no connectees" << std::endl;
1618 } else {
1619 for (unsigned i = 0; i < input->getNumConnectees(); ++i) {
1620 std::cout << input->getConnecteePath(i) << " ";
1621 // TODO as is, requires the input connections to be satisfied.
1622 // std::cout << " (alias: " << input.getAlias(i) << ") ";
1623 }
1624 std::cout << std::endl;
1625 }
1626 }
1627 std::cout << std::endl;
1628 }
1629
printSubcomponentInfo() const1630 void Component::printSubcomponentInfo() const {
1631 printSubcomponentInfo<Component>();
1632 }
1633
printOutputInfo(const bool includeDescendants) const1634 void Component::printOutputInfo(const bool includeDescendants) const {
1635
1636 // Do not display header for Components with no outputs.
1637 if (getNumOutputs() > 0) {
1638 const std::string msg = "Outputs from " + getAbsolutePathString() +
1639 " [" + getConcreteClassName() + "]";
1640 std::cout << msg << "\n" << std::string(msg.size(), '=') << std::endl;
1641
1642 const auto& outputs = getOutputs();
1643 size_t maxlen{};
1644 for(const auto& output : outputs)
1645 maxlen = std::max(maxlen, output.second->getTypeName().length());
1646 maxlen += 2;
1647
1648 for(const auto& output : outputs) {
1649 const auto& name = output.second->getTypeName();
1650 std::cout << std::string(maxlen - name.length(), ' ');
1651 std::cout << "[" << name << "] " << output.first << std::endl;
1652 }
1653 std::cout << std::endl;
1654 }
1655
1656 if (includeDescendants) {
1657 for (const Component& thisComp : getComponentList<Component>()) {
1658 // getComponentList() returns all descendants (i.e.,
1659 // children, grandchildren, etc.) so set includeDescendants=false
1660 // when calling on thisComp.
1661 thisComp.printOutputInfo(false);
1662 }
1663 }
1664 }
1665
initComponentTreeTraversal(const Component & root) const1666 void Component::initComponentTreeTraversal(const Component &root) const {
1667 // Going down the tree, this node is followed by all its children.
1668 // The last child's successor (next) is the parent's successor.
1669
1670 const size_t nmsc = _memberSubcomponents.size();
1671 const size_t npsc = _propertySubcomponents.size();
1672 const size_t nasc = _adoptedSubcomponents.size();
1673
1674 if (!hasOwner()) {
1675 // If this isn't the root component and it has no owner, then
1676 // this is an orphan component and we likely failed to call
1677 // finalizeFromProperties() on the root OR this is a clone that
1678 // has not been added to the root (in which case would have an owner).
1679 if (this != &root) {
1680 OPENSIM_THROW(ComponentIsAnOrphan, getName(),
1681 getConcreteClassName());
1682 }
1683 // if the root (have no owner) and have no components
1684 else if (!(nmsc + npsc + nasc)) {
1685 OPENSIM_THROW(ComponentIsRootWithNoSubcomponents,
1686 getName(), getConcreteClassName());
1687 }
1688 }
1689
1690 const Component* last = nullptr;
1691 for (unsigned int i = 0; i < nmsc; i++) {
1692 if (i == nmsc - 1) {
1693 _memberSubcomponents[i]->_nextComponent = _nextComponent.get();
1694 last = _memberSubcomponents[i].get();
1695 }
1696 else {
1697 _memberSubcomponents[i]->_nextComponent =
1698 _memberSubcomponents[i + 1].get();
1699 last = _memberSubcomponents[i + 1].get();
1700 }
1701 }
1702 if (npsc) {
1703 if (last)
1704 last->_nextComponent = _propertySubcomponents[0].get();
1705
1706 for (unsigned int i = 0; i < npsc; i++) {
1707 if (i == npsc - 1) {
1708 _propertySubcomponents[i]->_nextComponent =
1709 _nextComponent.get();
1710 last = _propertySubcomponents[i].get();
1711 }
1712 else {
1713 _propertySubcomponents[i]->_nextComponent =
1714 _propertySubcomponents[i + 1].get();
1715 last = _propertySubcomponents[i + 1].get();
1716 }
1717 }
1718 }
1719 if (nasc) {
1720 if (last)
1721 last->_nextComponent = _adoptedSubcomponents[0].get();
1722
1723 for (unsigned int i = 0; i <nasc; i++) {
1724 if (i == nasc - 1) {
1725 _adoptedSubcomponents[i]->_nextComponent = _nextComponent.get();
1726 }
1727 else {
1728 _adoptedSubcomponents[i]->_nextComponent
1729 = _adoptedSubcomponents[i + 1].get();
1730 }
1731 }
1732 }
1733
1734 // recurse to handle children of subcomponents
1735 for (unsigned int i = 0; i < nmsc; ++i) {
1736 _memberSubcomponents[i]->initComponentTreeTraversal(root);
1737 }
1738 for (unsigned int i = 0; i < npsc; ++i) {
1739 _propertySubcomponents[i]->initComponentTreeTraversal(root);
1740 }
1741 for (unsigned int i = 0; i < nasc; ++i) {
1742 _adoptedSubcomponents[i]->initComponentTreeTraversal(root);
1743 }
1744 }
1745
1746
clearStateAllocations()1747 void Component::clearStateAllocations()
1748 {
1749 _namedModelingOptionInfo.clear();
1750 _namedStateVariableInfo.clear();
1751 _namedDiscreteVariableInfo.clear();
1752 _namedCacheVariableInfo.clear();
1753 }
1754
reset()1755 void Component::reset()
1756 {
1757 _system.reset();
1758 _simTKcomponentIndex.invalidate();
1759 clearStateAllocations();
1760
1761 _propertySubcomponents.clear();
1762 _adoptedSubcomponents.clear();
1763 resetSubcomponentOrder();
1764 }
1765
warnBeforePrint() const1766 void Component::warnBeforePrint() const {
1767 if (!isObjectUpToDateWithProperties()) return;
1768 std::string message;
1769 auto checkIfConnecteePathIsSet =
1770 [](const Component& comp, std::string& message) {
1771 for (const auto& it : comp._socketsTable) {
1772 const auto& socket = it.second;
1773 if (socket->isConnected() &&
1774 ((socket->isListSocket() &&
1775 socket->getNumConnectees() == 0) ||
1776 (!socket->isListSocket() &&
1777 socket->getConnecteePath().empty()))) {
1778 // TODO: Improve this condition by making sure the connectee
1779 // name is correct.
1780 message += " Socket '" + socket->getName() + "' in " +
1781 comp.getConcreteClassName() + " at " +
1782 comp.getAbsolutePathString() + "\n";
1783 }
1784 }
1785 };
1786 if (getNumImmediateSubcomponents() == 0) {
1787 checkIfConnecteePathIsSet(*this, message);
1788 } else {
1789 for (const auto& comp : getComponentList()) {
1790 checkIfConnecteePathIsSet(comp, message);
1791 }
1792 }
1793 if (!message.empty()) {
1794 std::stringstream buffer;
1795 buffer << "Warning in " << getConcreteClassName()
1796 << "::print(): The following connections are not finalized "
1797 "and will not appear in the resulting XML file. "
1798 "Call finalizeConnections() before print().\n"
1799 "To ignore, set the debug level to at least 1 "
1800 "(e.g, by calling Object::setDebugLevel(1)) first.\n"
1801 << message << std::endl;
1802 OPENSIM_THROW_FRMOBJ(Exception, buffer.str());
1803 }
1804 }
1805
1806 } // end of namespace OpenSim
1807