-
Notifications
You must be signed in to change notification settings - Fork 331
/
Copy pathComponent.cpp
1587 lines (1381 loc) · 60 KB
/
Component.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* -------------------------------------------------------------------------- *
* OpenSim: Component.cpp *
* -------------------------------------------------------------------------- *
* The OpenSim API is a toolkit for musculoskeletal modeling and simulation. *
* See http://opensim.stanford.edu and the NOTICE file for more information. *
* OpenSim is developed at Stanford University and supported by the US *
* National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA *
* through the Warrior Web program. *
* *
* Copyright (c) 2005-2017 Stanford University and the Authors *
* Author(s): Ajay Seth, Michael Sherman *
* Contributor(s): Ayman Habib *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain a *
* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* -------------------------------------------------------------------------- */
// INCLUDES
#include "Component.h"
#include "OpenSim/Common/IO.h"
#include "XMLDocument.h"
using namespace SimTK;
namespace OpenSim {
//==============================================================================
// COMPONENT MEASURE
//==============================================================================
// Every OpenSim::Component is associated with a Simbody Measure of type
// ComponentMeasure defined here. This provides a full set of realize()
// methods for performing computations with System resources that are maintained
// at the Component base level, such as calculating state derivatives.
template <class T>
class ComponentMeasure : public SimTK::Measure_<T> {
public:
SimTK_MEASURE_HANDLE_PREAMBLE(ComponentMeasure, SimTK::Measure_<T>);
ComponentMeasure(SimTK::Subsystem& sub,
const OpenSim::Component& mc)
: SimTK::Measure_<T>(sub, new Implementation(mc),
SimTK::AbstractMeasure::SetHandle()) {}
SimTK_MEASURE_HANDLE_POSTSCRIPT(ComponentMeasure, SimTK::Measure_<T>);
};
template <class T>
class ComponentMeasure<T>::Implementation
: public SimTK::Measure_<T>::Implementation {
public:
// Don't allocate a value cache entry since this measure's value is
// just a dummy.
explicit Implementation(const Component& c)
: SimTK::Measure_<T>::Implementation(0), _Component(c) {}
// Implementations of Measure_<T>::Implementation virtual methods.
Implementation* cloneVirtual() const override final
{ return new Implementation(*this); }
int getNumTimeDerivativesVirtual() const override final {return 0;}
SimTK::Stage getDependsOnStageVirtual(int order) const override final
{ return SimTK::Stage::Empty; }
const T& getUncachedValueVirtual
(const SimTK::State& s, int derivOrder) const override final
{ return this->getValueZero(); }
void realizeMeasureTopologyVirtual(SimTK::State& s) const override final
{ _Component.extendRealizeTopology(s); }
void realizeMeasureModelVirtual(SimTK::State& s) const override final
{ _Component.extendRealizeModel(s); }
void realizeMeasureInstanceVirtual(const SimTK::State& s)
const override final
{ _Component.extendRealizeInstance(s); }
void realizeMeasureTimeVirtual(const SimTK::State& s) const override final
{ _Component.extendRealizeTime(s); }
void realizeMeasurePositionVirtual(const SimTK::State& s)
const override final
{ _Component.extendRealizePosition(s); }
void realizeMeasureVelocityVirtual(const SimTK::State& s)
const override final
{ _Component.extendRealizeVelocity(s); }
void realizeMeasureDynamicsVirtual(const SimTK::State& s)
const override final
{ _Component.extendRealizeDynamics(s); }
void realizeMeasureAccelerationVirtual(const SimTK::State& s)
const override final
{ _Component.extendRealizeAcceleration(s); }
void realizeMeasureReportVirtual(const SimTK::State& s)
const override final
{ _Component.extendRealizeReport(s); }
private:
const Component& _Component;
};
//==============================================================================
// COMPONENT
//==============================================================================
Component::Component() : Object()
{
constructProperty_components();
}
Component::Component(const std::string& fileName, bool updFromXMLNode)
: Object(fileName, updFromXMLNode)
{
constructProperty_components();
}
Component::Component(SimTK::Xml::Element& element) : Object(element)
{
constructProperty_components();
}
void Component::addComponent(Component* subcomponent)
{
//get to the root Component
const Component* root = this;
while (root->hasParent()) {
root = &(root->getParent());
}
auto components = root->getComponentList<Component>();
for (auto& c : components) {
if (subcomponent == &c) {
OPENSIM_THROW( ComponentAlreadyPartOfOwnershipTree,
subcomponent->getName(), getName());
}
}
updProperty_components().adoptAndAppendValue(subcomponent);
finalizeFromProperties();
// allow the derived Component to perform secondary operations
// in response to the inclusion of the subcomponent
extendAddComponent(subcomponent);
}
void Component::finalizeFromProperties()
{
reset();
// TODO use a flag to set whether we are lenient on having nameless
// Components. For backward compatibility we need to be able to
// handle nameless components so assign them their class name
// - aseth
if (getName().empty()) {
setName(IO::Lowercase(getConcreteClassName()) + "_");
}
OPENSIM_THROW_IF( getName().empty(), ComponentHasNoName,
getConcreteClassName() );
for (auto& comp : _memberSubcomponents) {
comp->setParent(*this);
}
for (auto& comp : _adoptedSubcomponents) {
comp->setParent(*this);
}
// Provide sockets, inputs, and outputs with a pointer to its component
// (this) so that they can invoke the component's methods.
for (auto& it : _socketsTable) {
it.second->setOwner(*this);
// Let the Socket handle any errors in the connectee_name property.
it.second->checkConnecteeNameProperty();
}
for (auto& it : _inputsTable) {
it.second->setOwner(*this);
// Let the Socket handle any errors in the connectee_name property.
it.second->checkConnecteeNameProperty();
}
for (auto& it : _outputsTable) {
it.second->setOwner(*this);
}
markPropertiesAsSubcomponents();
componentsFinalizeFromProperties();
extendFinalizeFromProperties();
setObjectIsUpToDateWithProperties();
}
// Base class implementation of virtual method.
// Call finalizeFromProperties on all subcomponents
void Component::componentsFinalizeFromProperties() const
{
for (auto& comp : _memberSubcomponents) {
const_cast<Component*>(comp.get())
->finalizeFromProperties();
}
for (auto& comp : _propertySubcomponents) {
const_cast<Component*>(comp.get())
->finalizeFromProperties();
}
for (auto& comp : _adoptedSubcomponents) {
const_cast<Component*>(comp.get())
->finalizeFromProperties();
}
}
// Base class implementation of non-virtual finalizeConnections method.
void Component::finalizeConnections(Component &root)
{
if (!isObjectUpToDateWithProperties()){
// if edits occur between construction and connect() this is
// the last chance to finalize before addToSystem.
finalizeFromProperties();
}
for (auto& it : _socketsTable) {
auto& socket = it.second;
socket->disconnect();
try {
socket->findAndConnect(root);
}
catch (const std::exception& x) {
OPENSIM_THROW_FRMOBJ(Exception, "Failed to connect Socket '" +
socket->getName() + "' of type " +
socket->getConnecteeTypeName() +
" (details: " + x.what() + ").");
}
}
for (auto& it : _inputsTable) {
auto& input = it.second;
if (!input->isListSocket() && input->getConnecteeName(0).empty()) {
// TODO When we support verbose/debug logging we should include
// message about unspecified Outputs but generally this OK
// if the Input's value is not required.
/**
std::cout << getConcreteClassName() << "'" << getName() << "'";
std::cout << "::connect() Input<" << input.getConnecteeTypeName();
std::cout << ">`" << input.getName();
std::cout << "' Output has not been specified." << std::endl;
*/
continue;
}
input->disconnect();
try {
input->findAndConnect(root);
}
catch (const std::exception& x) {
OPENSIM_THROW_FRMOBJ(Exception, "Failed to connect Input '" +
input->getName() + "' of type " + input->getConnecteeTypeName()
+ " (details: " + x.what() + ").");
}
}
// Allow derived Components to handle/check their connections and also
// override the order in which its subcomponents are ordered when
// adding subcomponents to the System
extendConnect(root);
// Allow subcomponents to form their connections
componentsConnect(root);
// Forming connections changes the Socket which is a property
// Remark as upToDate.
setObjectIsUpToDateWithProperties();
}
// invoke connect on all (sub)components of this component
void Component::componentsConnect(Component& root)
{
// enable the subcomponents the opportunity to connect themselves
for (unsigned int i = 0; i<_memberSubcomponents.size(); ++i) {
_memberSubcomponents[i].upd()->finalizeConnections(root);
}
for(unsigned int i=0; i<_propertySubcomponents.size(); ++i){
_propertySubcomponents[i].get()->finalizeConnections(root);
}
for (unsigned int i = 0; i<_adoptedSubcomponents.size(); ++i) {
_adoptedSubcomponents[i].upd()->finalizeConnections(root);
}
}
void Component::clearConnections()
{
// First give the subcomponents the opportunity to disconnect themselves
for (unsigned int i = 0; i<_memberSubcomponents.size(); i++) {
_memberSubcomponents[i]->clearConnections();
}
for (unsigned int i = 0; i<_propertySubcomponents.size(); i++){
_propertySubcomponents[i]->clearConnections();
}
for (unsigned int i = 0; i<_adoptedSubcomponents.size(); i++) {
_adoptedSubcomponents[i]->clearConnections();
}
//Now cycle through and disconnect all sockets for this component
for (auto& it : _socketsTable) {
it.second->disconnect();
}
// Must also clear the input's connections.
for (auto& it : _inputsTable) {
it.second->disconnect();
}
//now clear all the stored system indices from this component
reset();
}
void Component::addToSystem(SimTK::MultibodySystem& system) const
{
// If being asked to be added to the same System that it is already
// a part, there is nothing to be done.
if (hasSystem() && (&getSystem() == &system)) {
return;
}
baseAddToSystem(system);
extendAddToSystem(system);
componentsAddToSystem(system);
extendAddToSystemAfterSubcomponents(system);
}
// Base class implementation of virtual method.
// Every Component owns an underlying SimTK::Measure
// which is a ComponentMeasure<T> and is added to the System's default
// subsystem. That measure is used only for the side effect of its realize()
// methods being called; its value is not used.
void Component::baseAddToSystem(SimTK::MultibodySystem& system) const
{
if (!isObjectUpToDateWithProperties()) {
std::string msg = "Component " + getConcreteClassName() + "::" + getName();
msg += " cannot extendAddToSystem until it is up-to-date with its properties.";
throw Exception(msg);
}
// Briefly get write access to the Component to record some
// information associated with the System; that info is const after this.
Component* mutableThis = const_cast<Component *>(this);
mutableThis->_system = system;
// Allocate the ComponentMeasure, point it to this Component for
// making realize() calls, and add it to the system's default subsystem.
ComponentMeasure<double> mcMeasure(system.updDefaultSubsystem(), *this);
mutableThis->_simTKcomponentIndex = mcMeasure.getSubsystemMeasureIndex();
}
void Component::componentsAddToSystem(SimTK::MultibodySystem& system) const
{
// If _orderedSubcomponents is specified, then use this Component's
// specification for the order in which subcomponents are added. At a
// minimum the order for all immediate subcomponents must be specified.
if (_orderedSubcomponents.size() >= getNumImmediateSubcomponents()) {
for (const auto& compRef : _orderedSubcomponents) {
compRef->addToSystem(system);
}
}
else if (_orderedSubcomponents.size() == 0) {
// Otherwise, invoke on all immediate subcomponents in tree order
auto mySubcomponents = getImmediateSubcomponents();
for (const auto& compRef : mySubcomponents) {
compRef->addToSystem(system);
}
}
else {
OPENSIM_THROW_FRMOBJ(Exception,
"_orderedSubcomponents specified, but its size does not reflect the "
"the number of immediate subcomponents. Verify that you have included "
"all immediate subcomponents in the ordered list."
)
}
}
void Component::initStateFromProperties(SimTK::State& state) const
{
extendInitStateFromProperties(state);
componentsInitStateFromProperties(state);
}
void Component::componentsInitStateFromProperties(SimTK::State& state) const
{
for (unsigned int i = 0; i<_memberSubcomponents.size(); ++i)
_memberSubcomponents[i]->initStateFromProperties(state);
for (unsigned int i = 0; i<_propertySubcomponents.size(); ++i)
_propertySubcomponents[i]->initStateFromProperties(state);
for (unsigned int i = 0; i<_adoptedSubcomponents.size(); ++i)
_adoptedSubcomponents[i]->initStateFromProperties(state);
}
void Component::setPropertiesFromState(const SimTK::State& state)
{
extendSetPropertiesFromState(state);
componentsSetPropertiesFromState(state);
}
void Component::componentsSetPropertiesFromState(const SimTK::State& state)
{
for (unsigned int i = 0; i<_memberSubcomponents.size(); ++i)
_memberSubcomponents[i]->setPropertiesFromState(state);
for (unsigned int i = 0; i<_propertySubcomponents.size(); ++i)
_propertySubcomponents[i]->setPropertiesFromState(state);
for (unsigned int i = 0; i<_adoptedSubcomponents.size(); ++i)
_adoptedSubcomponents[i]->setPropertiesFromState(state);
}
// Base class implementation of virtual method. Note that we're not handling
// subcomponents here; this method gets called from extendRealizeAcceleration()
// which will be invoked for each (sub) component by its own ComponentMeasure.
void Component::computeStateVariableDerivatives(const SimTK::State& s) const
{
int nsv = getNumStateVariablesAddedByComponent();
if(nsv > 0){
int nasv = 0;
std::map<std::string, StateVariableInfo>::const_iterator it;
for(it = _namedStateVariableInfo.begin();
it != _namedStateVariableInfo.end(); ++it){
const StateVariable& sv = *it->second.stateVariable;
const AddedStateVariable *asv =
dynamic_cast<const AddedStateVariable *>(&sv);
if(asv) nasv++;
}
if(nasv > 0){
std::stringstream msg;
msg << "Component " + getConcreteClassName()+"::"+getName();
msg << " added " << nasv << " state variables and ";
msg << " must specify their derivatives." << std::endl;
throw Exception(msg.str());
}
}
}
void Component::
addModelingOption(const std::string& optionName, int maxFlagValue) const
{
// don't add modeling option if there is another state with the same
// name for this component
std::map<std::string, ModelingOptionInfo>::const_iterator it;
it = _namedModelingOptionInfo.find(optionName);
if(it != _namedModelingOptionInfo.end())
throw Exception("Component::addModelingOption: Modeling option '"
+ optionName + "' already exists.");
// assign a "slot" for a modeling option by name
// modeling option index will be invalid by default
// upon allocation during realizeTopology the index will be set
_namedModelingOptionInfo[optionName] = ModelingOptionInfo(maxFlagValue);
}
void Component::addStateVariable(const std::string& stateVariableName,
const SimTK::Stage& invalidatesStage,
bool isHidden) const
{
if( (invalidatesStage < Stage::Position) ||
(invalidatesStage > Stage::Dynamics)) {
throw Exception("Component::addStateVariable: invalidatesStage "
"must be Position, Velocity or Dynamics.");
}
// Allocate space for a new state variable
AddedStateVariable* asv =
new AddedStateVariable(stateVariableName, *this, invalidatesStage, isHidden);
// Add it to the Component and let it take ownership
addStateVariable(asv);
}
void Component::addStateVariable(Component::StateVariable* stateVariable) const
{
const std::string& stateVariableName = stateVariable->getName();
// don't add state if there is another state variable with the same name
// for this component
std::map<std::string, StateVariableInfo>::const_iterator it;
it = _namedStateVariableInfo.find(stateVariableName);
if(it != _namedStateVariableInfo.end()){
throw Exception("Component::addStateVariable: State variable '" +
stateVariableName + "' already exists.");
}
int order = (int)_namedStateVariableInfo.size();
// assign a "slot" for a state variable by name
// state variable index will be invalid by default
// upon allocation during realizeTopology the index will be set
_namedStateVariableInfo[stateVariableName] =
StateVariableInfo(stateVariable, order);
const AddedStateVariable* asv =
dynamic_cast<const Component::AddedStateVariable *>(stateVariable);
// Now automatically add a cache variable to hold the derivative
// to enable a similar interface for setting and getting the derivatives
// based on the creator specified state name
if(asv){
addCacheVariable(stateVariableName+"_deriv", 0.0, Stage::Dynamics);
}
}
void Component::addDiscreteVariable(const std::string& discreteVariableName,
SimTK::Stage invalidatesStage) const
{
// don't add discrete var if there is another discrete variable with the
// same name for this component
std::map<std::string, DiscreteVariableInfo>::const_iterator it;
it = _namedDiscreteVariableInfo.find(discreteVariableName);
if(it != _namedDiscreteVariableInfo.end()){
throw Exception("Component::addDiscreteVariable: discrete variable '" +
discreteVariableName + "' already exists.");
}
// assign "slots" for the discrete variables by name
// discrete variable indices will be invalid by default
// upon allocation during realizeTopology the indices will be set
_namedDiscreteVariableInfo[discreteVariableName] =
DiscreteVariableInfo(invalidatesStage);
}
// Get the value of a ModelingOption flag for this Component.
int Component::
getModelingOption(const SimTK::State& s, const std::string& name) const
{
std::map<std::string, ModelingOptionInfo>::const_iterator it;
it = _namedModelingOptionInfo.find(name);
if(it != _namedModelingOptionInfo.end()) {
SimTK::DiscreteVariableIndex dvIndex = it->second.index;
return SimTK::Value<int>::downcast(
getDefaultSubsystem().getDiscreteVariable(s, dvIndex)).get();
} else {
std::stringstream msg;
msg << "Component::getModelingOption: ERR- name '" << name
<< "' not found.\n "
<< "for component '"<< getName() << "' of type "
<< getConcreteClassName();
throw Exception(msg.str(),__FILE__,__LINE__);
return -1;
}
}
// Set the value of a discrete variable allocated by this Component by name.
void Component::
setModelingOption(SimTK::State& s, const std::string& name, int flag) const
{
std::map<std::string, ModelingOptionInfo>::const_iterator it;
it = _namedModelingOptionInfo.find(name);
if(it != _namedModelingOptionInfo.end()) {
SimTK::DiscreteVariableIndex dvIndex = it->second.index;
if(flag > it->second.maxOptionValue){
std::stringstream msg;
msg << "Component::setModelingOption: "<< name
<< " flag cannot exceed "<< it->second.maxOptionValue <<".\n ";
throw Exception(msg.str(),__FILE__,__LINE__);
}
SimTK::Value<int>::downcast(
getDefaultSubsystem().updDiscreteVariable(s, dvIndex)).upd() = flag;
} else {
std::stringstream msg;
msg << "Component::setModelingOption: modeling option " << name
<< " not found.\n ";
throw Exception(msg.str(),__FILE__,__LINE__);
}
}
unsigned Component::printComponentsMatching(const std::string& substring) const
{
auto components = getComponentList();
components.setFilter(ComponentFilterAbsolutePathNameContainsString(substring));
unsigned count = 0;
for (const auto& comp : components) {
std::cout << comp.getAbsolutePathName() << std::endl;
++count;
}
return count;
}
int Component::getNumStateVariables() const
{
// Must have already called initSystem.
OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
//Get the number of state variables added (or exposed) by this Component
int ns = getNumStateVariablesAddedByComponent();
// And then include the states of its subcomponents
for (unsigned int i = 0; i<_memberSubcomponents.size(); i++)
ns += _memberSubcomponents[i]->getNumStateVariables();
for(unsigned int i=0; i<_propertySubcomponents.size(); i++)
ns += _propertySubcomponents[i]->getNumStateVariables();
for (unsigned int i = 0; i<_adoptedSubcomponents.size(); i++)
ns += _adoptedSubcomponents[i]->getNumStateVariables();
return ns;
}
const Component& Component::getParent() const
{
if (!hasParent()) {
std::string msg = "Component '" + getName() + "'::getParent(). " +
"Has no parent Component assigned.\n" +
"Make sure the component was added to the Model (or its parent).";
throw Exception(msg);
}
return _parent.getRef();
}
bool Component::hasParent() const
{
return !_parent.empty();
}
void Component::setParent(const Component& parent)
{
if (&parent == this) {
std::string msg = "Component '" + getName() + "'::setParent(). " +
"Attempted to set itself as its parent.";
throw Exception(msg);
}
else if (_parent.get() == &parent) {
return;
}
_parent.reset(&parent);
}
ComponentPath Component::getAbsolutePathName() const
{
std::vector<std::string> pathVec;
pathVec.push_back(getName());
const Component* up = this;
while (up && up->hasParent()) {
up = &up->getParent();
pathVec.insert(pathVec.begin(), up->getName());
}
// The root must have a leading '/'
ComponentPath path(pathVec, true);
return path;
}
ComponentPath Component::getRelativePathName(const Component& wrt) const
{
ComponentPath thisP(getAbsolutePathName());
ComponentPath wrtP(wrt.getAbsolutePathName());
return thisP.formRelativePath(wrtP);
}
const Component::StateVariable* Component::
findStateVariable(const std::string& name) const
{
// Must have already called initSystem.
OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
// Split the prefix from the varName (part of string past the last "/")
// In the case where no "/" is found, prefix = name.
std::string::size_type back = name.rfind("/");
std::string prefix = name.substr(0, back);
// In the case where no "/" is found, this assigns varName = name.
// When "/" is not found, back = UINT_MAX. Then, back + 1 = 0.
// Subtracting by UINT_MAX is effectively adding by 1, so the next line
// should work in all cases except if name.length() = UINT_MAX.
std::string varName = name.substr(back + 1, name.length() - back);
// first assume that the state variable named belongs to this
// top level component
std::map<std::string, StateVariableInfo>::const_iterator it;
it = _namedStateVariableInfo.find(varName);
if (it != _namedStateVariableInfo.end()) {
return it->second.stateVariable.get();
}
const StateVariable* found = nullptr;
const Component* comp = traversePathToComponent<Component>(prefix);
if (comp) {
found = comp->findStateVariable(varName);
}
// Path not given or could not find it along given path name
// Now try complete search.
if (!found) {
for (unsigned int i = 0; i < _propertySubcomponents.size(); ++i) {
comp = _propertySubcomponents[i]->findComponent(prefix, &found);
if (found) {
return found;
}
if (comp) {
return comp->findStateVariable(varName);
}
}
}
return found;
}
// Get the names of "continuous" state variables maintained by the Component and
// its subcomponents.
Array<std::string> Component::getStateVariableNames() const
{
// Must have already called initSystem.
OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
Array<std::string> names = getStateVariablesNamesAddedByComponent();
/** TODO: Use component iterator like below
for (int i = 0; i < stateNames.size(); ++i) {
stateNames[i] = (getAbsolutePathName() + "/" + stateNames[i]);
}
for (auto& comp : getComponentList<Component>()) {
const std::string& pathName = comp.getAbsolutePathName();// *this);
Array<std::string> subStateNames =
comp.getStateVariablesNamesAddedByComponent();
for (int i = 0; i < subStateNames.size(); ++i) {
stateNames.append(pathName + "/" + subStateNames[i]);
}
}
*/
// Include the states of its subcomponents
for (unsigned int i = 0; i<_memberSubcomponents.size(); i++) {
Array<std::string> subnames = _memberSubcomponents[i]->getStateVariableNames();
int nsubs = subnames.getSize();
const std::string& subCompName = _memberSubcomponents[i]->getName();
std::string::size_type front = subCompName.find_first_not_of(" \t\r\n");
std::string::size_type back = subCompName.find_last_not_of(" \t\r\n");
std::string prefix = "";
if (back > front) // have non-whitespace name
prefix = subCompName + "/";
for (int j = 0; j<nsubs; ++j) {
names.append(prefix + subnames[j]);
}
}
for(unsigned int i=0; i<_propertySubcomponents.size(); i++){
Array<std::string> subnames = _propertySubcomponents[i]->getStateVariableNames();
int nsubs = subnames.getSize();
const std::string& subCompName = _propertySubcomponents[i]->getName();
// TODO: We should implement checks that names do not have whitespace at the time
// they are assigned and not here where it is a waste of time - aseth
std::string::size_type front = subCompName.find_first_not_of(" \t\r\n");
std::string::size_type back = subCompName.find_last_not_of(" \t\r\n");
std::string prefix = "";
if(back > front) // have non-whitespace name
prefix = subCompName+"/";
for(int j =0; j<nsubs; ++j){
names.append(prefix+subnames[j]);
}
}
for (unsigned int i = 0; i<_adoptedSubcomponents.size(); i++) {
Array<std::string> subnames = _adoptedSubcomponents[i]->getStateVariableNames();
int nsubs = subnames.getSize();
const std::string& subCompName = _adoptedSubcomponents[i]->getName();
std::string::size_type front = subCompName.find_first_not_of(" \t\r\n");
std::string::size_type back = subCompName.find_last_not_of(" \t\r\n");
std::string prefix = "";
if (back > front) // have non-whitespace name
prefix = subCompName + "/";
for (int j = 0; j<nsubs; ++j) {
names.append(prefix + subnames[j]);
}
}
return names;
}
// Get the value of a state variable allocated by this Component.
double Component::
getStateVariableValue(const SimTK::State& s, const std::string& name) const
{
// Must have already called initSystem.
OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
// find the state variable with this component or its subcomponents
const StateVariable* rsv = findStateVariable(name);
if (rsv) {
return rsv->getValue(s);
}
std::stringstream msg;
msg << "Component::getStateVariable: ERR- state named '" << name
<< "' not found in " << getName() << " of type " << getConcreteClassName();
throw Exception(msg.str(),__FILE__,__LINE__);
return SimTK::NaN;
}
// Get the value of a state variable derivative computed by this Component.
double Component::
getStateVariableDerivativeValue(const SimTK::State& state,
const std::string& name) const
{
// Must have already called initSystem.
OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
computeStateVariableDerivatives(state);
std::map<std::string, StateVariableInfo>::const_iterator it;
it = _namedStateVariableInfo.find(name);
if(it != _namedStateVariableInfo.end()) {
return it->second.stateVariable->getDerivative(state);
}
else{
// otherwise find the component that variable belongs to
const StateVariable* rsv = findStateVariable(name);
if (rsv) {
return rsv->getDerivative(state);
}
}
std::stringstream msg;
msg << "Component::getStateVariableDerivative: ERR- variable name '" << name
<< "' not found.\n "
<< getName() << " of type " << getConcreteClassName()
<< " has " << getNumStateVariables() << " states.";
throw Exception(msg.str(),__FILE__,__LINE__);
return SimTK::NaN;
}
// Set the value of a state variable allocated by this Component given its name
// for this component.
void Component::
setStateVariableValue(State& s, const std::string& name, double value) const
{
// Must have already called initSystem.
OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
// find the state variable
const StateVariable* rsv = findStateVariable(name);
if(rsv){ // find required rummaging through the state variable names
return rsv->setValue(s, value);
}
std::stringstream msg;
msg << "Component::setStateVariable: ERR- state named '" << name
<< "' not found in " << getName() << " of type "
<< getConcreteClassName() << ".\n";
throw Exception(msg.str(),__FILE__,__LINE__);
}
bool Component::isAllStatesVariablesListValid() const
{
int nsv = getNumStateVariables();
// Consider the list of all StateVariables to be valid if all of
// the following conditions are true:
// 1. Component is up-to-date with its Properties
// 2. a System has been associated with the list of StateVariables
// 3. The list of all StateVariables is correctly sized (initialized)
// 4. The System associated with the StateVariables is the current System
// TODO: Enable the isObjectUpToDateWithProperties() check when computing
// the path of the GeomtryPath does not involve updating its PathPointSet.
// This change dirties the GeometryPath which is a property of a Muscle which
// is property of the Model. Therefore, during integration the Model is not
// up-to-date and this causes a rebuilding of the cached StateVariables list.
// See GeometryPath::computePath() for the corresponding TODO that must be
// addressed before we can re-enable the isObjectUpToDateWithProperties
// check.
// It has been verified that the adding Components will invalidate the state
// variables associated with the Model and force the list to be rebuilt.
bool valid = //isObjectUpToDateWithProperties() && // 1.
!_statesAssociatedSystem.empty() && // 2.
_allStateVariables.size() == nsv && // 3.
getSystem().isSameSystem(_statesAssociatedSystem.getRef()); // 4.
return valid;
}
// Get all values of the state variables allocated by this Component. Includes
// state variables allocated by its subcomponents.
SimTK::Vector Component::
getStateVariableValues(const SimTK::State& state) const
{
// Must have already called initSystem.
OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
int nsv = getNumStateVariables();
// if the StateVariables are invalid (see above) rebuild the list
if (!isAllStatesVariablesListValid()) {
_statesAssociatedSystem.reset(&getSystem());
_allStateVariables.clear();
_allStateVariables.resize(nsv);
Array<std::string> names = getStateVariableNames();
for (int i = 0; i < nsv; ++i)
_allStateVariables[i].reset(findStateVariable(names[i]));
}
Vector stateVariableValues(nsv, SimTK::NaN);
for(int i=0; i<nsv; ++i){
stateVariableValues[i]= _allStateVariables[i]->getValue(state);
}
return stateVariableValues;
}
// Set all values of the state variables allocated by this Component. Includes
// state variables allocated by its subcomponents.
void Component::
setStateVariableValues(SimTK::State& state, const SimTK::Vector& values)
{
// Must have already called initSystem.
OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
int nsv = getNumStateVariables();
SimTK_ASSERT(values.size() == nsv,
"Component::setStateVariableValues() number values does not match the "
"number of state variables.");
// if the StateVariables are invalid (see above) rebuild the list
if (!isAllStatesVariablesListValid()) {
_statesAssociatedSystem.reset(&getSystem());
_allStateVariables.clear();
_allStateVariables.resize(nsv);
Array<std::string> names = getStateVariableNames();
for (int i = 0; i < nsv; ++i)
_allStateVariables[i].reset(findStateVariable(names[i]));
}
for(int i=0; i<nsv; ++i){
_allStateVariables[i]->setValue(state, values[i]);
}
}
// Set the derivative of a state variable computed by this Component by name.
void Component::
setStateVariableDerivativeValue(const State& state,
const std::string& name, double value) const
{
std::map<std::string, StateVariableInfo>::const_iterator it;
it = _namedStateVariableInfo.find(name);
if(it != _namedStateVariableInfo.end()) {
const StateVariable& sv = *it->second.stateVariable;
sv.setDerivative(state, value);
}
else{
std::stringstream msg;
msg << "Component::setStateVariableDerivative: ERR- name '" << name
<< "' not found.\n "
<< getName() << " of type " << getConcreteClassName()
<< " has " << getNumStateVariables() << " states.";
throw Exception(msg.str(),__FILE__,__LINE__);
}
}
// Get the value of a discrete variable allocated by this Component by name.
double Component::
getDiscreteVariableValue(const SimTK::State& s, const std::string& name) const
{
// Must have already called initSystem.
OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
std::map<std::string, DiscreteVariableInfo>::const_iterator it;
it = _namedDiscreteVariableInfo.find(name);
if(it != _namedDiscreteVariableInfo.end()) {
SimTK::DiscreteVariableIndex dvIndex = it->second.index;
return SimTK::Value<double>::downcast(
getDefaultSubsystem().getDiscreteVariable(s, dvIndex)).get();
} else {
std::stringstream msg;
msg << "Component::getDiscreteVariable: ERR- name '" << name
<< "' not found.\n "
<< "for component '"<< getName() << "' of type "
<< getConcreteClassName();
throw Exception(msg.str(),__FILE__,__LINE__);
return SimTK::NaN;
}
}
// Set the value of a discrete variable allocated by this Component by name.
void Component::
setDiscreteVariableValue(SimTK::State& s, const std::string& name, double value) const
{
// Must have already called initSystem.
OPENSIM_THROW_IF_FRMOBJ(!hasSystem(), ComponentHasNoSystem);
std::map<std::string, DiscreteVariableInfo>::const_iterator it;
it = _namedDiscreteVariableInfo.find(name);
if(it != _namedDiscreteVariableInfo.end()) {
SimTK::DiscreteVariableIndex dvIndex = it->second.index;