1 /* -------------------------------------------------------------------------- *
2  *               OpenSim:  FiberCompressiveForceLengthCurve.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): Matthew Millard                                                 *
12  *                                                                            *
13  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
14  * not use this file except in compliance with the License. You may obtain a  *
15  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
16  *                                                                            *
17  * Unless required by applicable law or agreed to in writing, software        *
18  * distributed under the License is distributed on an "AS IS" BASIS,          *
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
20  * See the License for the specific language governing permissions and        *
21  * limitations under the License.                                             *
22  * -------------------------------------------------------------------------- */
23 #include "FiberCompressiveForceLengthCurve.h"
24 #include <OpenSim/Common/SmoothSegmentedFunctionFactory.h>
25 
26 using namespace OpenSim;
27 using namespace SimTK;
28 using namespace std;
29 
30 //=============================================================================
31 // CONSTRUCTION
32 //=============================================================================
33 // Uses default (compiler-generated) destructor, copy constructor,
34 // copy assignment.
35 
36 FiberCompressiveForceLengthCurve::
FiberCompressiveForceLengthCurve()37         FiberCompressiveForceLengthCurve()
38 {
39     setNull();
40     constructProperties();
41     setName("default_FiberCompressiveForceLengthCurve");
42 
43     ensureCurveUpToDate();
44 }
45 
FiberCompressiveForceLengthCurve(double normLengthAtZeroForce,double stiffnessAtZeroLength,double curviness,const std::string & muscleName)46 FiberCompressiveForceLengthCurve::FiberCompressiveForceLengthCurve
47     (   double normLengthAtZeroForce,
48         double stiffnessAtZeroLength,
49         double curviness,
50         const std::string& muscleName)
51 {
52     setNull();
53     constructProperties();
54     setName(muscleName + "_FiberCompressiveForceLengthCurve");
55 
56     set_norm_length_at_zero_force(normLengthAtZeroForce);
57     set_stiffness_at_zero_length(stiffnessAtZeroLength);
58     set_curviness(curviness);
59 
60     ensureCurveUpToDate();
61 }
62 
63 
setNull()64 void FiberCompressiveForceLengthCurve::setNull()
65 {
66 
67     setAuthors("Matthew Millard");
68 }
69 
constructProperties()70 void FiberCompressiveForceLengthCurve::constructProperties()
71 {
72     constructProperty_norm_length_at_zero_force(0.5);
73     constructProperty_stiffness_at_zero_length();
74     constructProperty_curviness();
75 }
76 
77 
buildCurve(bool computeIntegral)78 void FiberCompressiveForceLengthCurve::buildCurve( bool computeIntegral )
79 {
80     SmoothSegmentedFunction* f = SmoothSegmentedFunctionFactory::
81         createFiberCompressiveForceLengthCurve(
82                 get_norm_length_at_zero_force(),
83                 m_stiffnessAtZeroLengthInUse,
84                 m_curvinessInUse,
85                 computeIntegral,
86                 getName());
87 
88     m_curve = *f;
89 
90     delete f;
91 
92     setObjectIsUpToDateWithProperties();
93 }
94 
95 
ensureCurveUpToDate()96 void FiberCompressiveForceLengthCurve::ensureCurveUpToDate()
97 {
98     if(isObjectUpToDateWithProperties() == false){
99 
100         //=====================================================================
101         //Compute the optional properties if they have not been set
102         //=====================================================================
103         if(getProperty_stiffness_at_zero_length().empty() == true &&
104             getProperty_curviness().empty() == true)
105         {
106             double lengthAtZeroForce = get_norm_length_at_zero_force();
107 
108             m_stiffnessAtZeroLengthInUse= -2.0/lengthAtZeroForce;
109             m_curvinessInUse = 0.5;
110             m_isFittedCurveBeingUsed = true;
111         }
112 
113         //=====================================================================
114         //Use the optional properties if they have been set
115         //=====================================================================
116         if(getProperty_stiffness_at_zero_length().empty() == false &&
117             getProperty_curviness().empty() == false)
118         {
119 
120             m_stiffnessAtZeroLengthInUse = get_stiffness_at_zero_length();
121             m_curvinessInUse = get_curviness();
122             m_isFittedCurveBeingUsed = false;
123         }
124 
125         //=====================================================================
126         //Error condition if only one optional parameter is set.
127         //=====================================================================
128         bool a = getProperty_stiffness_at_zero_length().empty();
129         bool b = getProperty_curviness().empty();
130 
131         //This is really a XOR operation ...
132         if( ( a && !b ) || ( !a && b ) ){
133 
134             //This error condition is checked to make sure that the reference
135             //fiber is being used to populate all optional properties or none
136             //of them. Anything different would result in an inconsistency in
137             //the computed curve - it wouldn't reflect the reference curve.
138             SimTK_ERRCHK1_ALWAYS(false,
139                 "FiberCompressiveForceLengthCurve::ensureCurveUpToDate()",
140                 "%s: Optional parameters stiffness and curviness must both"
141                 "be set, or both remain empty. You have set one parameter"
142                 "and left the other blank.",
143                 getName().c_str());
144         }
145 
146         buildCurve();
147     }
148 
149     //Since the name is not counted as a property, but it can change,
150     //and needs to be kept up to date.
151     std::string name = getName();
152     m_curve.setName(name);
153 }
154 
155 
156 //=============================================================================
157 //  OpenSim::Function Interface
158 //=============================================================================
159 
createSimTKFunction() const160 SimTK::Function* FiberCompressiveForceLengthCurve::createSimTKFunction() const
161 {
162     // back the OpenSim::Function with this SimTK::Function
163 
164     return SmoothSegmentedFunctionFactory::
165         createFiberCompressiveForceLengthCurve(
166                 get_norm_length_at_zero_force(),
167                 m_stiffnessAtZeroLengthInUse,
168                 m_curvinessInUse,
169                 false,
170                 getName());
171 }
172 
173 
174 
175 //=============================================================================
176 // GET & SET METHODS
177 //=============================================================================
getNormLengthAtZeroForce() const178 double FiberCompressiveForceLengthCurve::getNormLengthAtZeroForce() const
179 {
180     return get_norm_length_at_zero_force();
181 }
182 
getStiffnessAtZeroLengthInUse() const183 double FiberCompressiveForceLengthCurve::getStiffnessAtZeroLengthInUse() const
184 {
185     return m_stiffnessAtZeroLengthInUse;
186 }
187 
getCurvinessInUse() const188 double FiberCompressiveForceLengthCurve::getCurvinessInUse() const
189 {
190     return m_curvinessInUse;
191 }
192 
isFittedCurveBeingUsed() const193 bool FiberCompressiveForceLengthCurve::isFittedCurveBeingUsed() const
194 {
195     return m_isFittedCurveBeingUsed;
196 }
197 
198 
199 void FiberCompressiveForceLengthCurve::
setNormLengthAtZeroForce(double aNormLengthAtZeroForce)200     setNormLengthAtZeroForce(double aNormLengthAtZeroForce)
201 {
202     set_norm_length_at_zero_force(aNormLengthAtZeroForce);
203     ensureCurveUpToDate();
204 }
205 
206 void FiberCompressiveForceLengthCurve::
setOptionalProperties(double aStiffnessAtZeroLength,double aCurviness)207         setOptionalProperties(double aStiffnessAtZeroLength, double aCurviness)
208 {
209    set_stiffness_at_zero_length(aStiffnessAtZeroLength);
210    set_curviness(aCurviness);
211    ensureCurveUpToDate();
212 }
213 
214 
215 //=============================================================================
216 // SERVICES
217 //=============================================================================
218 
calcValue(double aNormLength) const219 double FiberCompressiveForceLengthCurve::calcValue(double aNormLength) const
220 {
221     SimTK_ASSERT(isObjectUpToDateWithProperties()==true,
222         "FiberCompressiveForceLengthCurve: Curve is not"
223         " to date with its properties");
224     return m_curve.calcValue(aNormLength);
225 }
226 
calcIntegral(double aNormLength) const227 double FiberCompressiveForceLengthCurve::calcIntegral(double aNormLength) const
228 {
229     SimTK_ASSERT(isObjectUpToDateWithProperties()==true,
230         "FiberCompressiveForceLengthCurve: Curve is not"
231         " to date with its properties");
232 
233     if (!m_curve.isIntegralAvailable()) {
234         FiberCompressiveForceLengthCurve* mutableThis =
235             const_cast<FiberCompressiveForceLengthCurve*>(this);
236         mutableThis->buildCurve(true);
237     }
238 
239     return m_curve.calcIntegral(aNormLength);
240 }
241 
242 double FiberCompressiveForceLengthCurve::
calcDerivative(double aNormLength,int order) const243     calcDerivative(double aNormLength, int order) const
244 {
245     SimTK_ASSERT(isObjectUpToDateWithProperties()==true,
246         "FiberCompressiveForceLengthCurve: Curve is not"
247         " to date with its properties");
248 
249     SimTK_ERRCHK1_ALWAYS(order >= 0 && order <= 2,
250         "FiberCompressiveForceLengthCurve::calcDerivative",
251         "order must be 0, 1, or 2, but %i was entered", order);
252 
253     return m_curve.calcDerivative(aNormLength,order);
254 }
255 
256 double FiberCompressiveForceLengthCurve::
calcDerivative(const std::vector<int> & derivComponents,const SimTK::Vector & x) const257     calcDerivative(const std::vector<int>& derivComponents,
258                    const SimTK::Vector& x) const
259 {
260     return m_curve.calcDerivative(derivComponents, x);
261 }
262 
getCurveDomain() const263 SimTK::Vec2 FiberCompressiveForceLengthCurve::getCurveDomain() const
264 {
265     SimTK_ASSERT(isObjectUpToDateWithProperties()==true,
266         "FiberCompressiveForceLengthCurve: Curve is not"
267         " to date with its properties");
268     return m_curve.getCurveDomain();
269 }
270 
271 void FiberCompressiveForceLengthCurve::
printMuscleCurveToCSVFile(const std::string & path)272     printMuscleCurveToCSVFile(const std::string& path)
273 {
274     ensureCurveUpToDate();
275 
276     double xmin = 0;
277     double xmax = max(1.0,get_norm_length_at_zero_force());
278 
279     m_curve.printMuscleCurveToCSVFile(path,xmin,xmax);
280 }
281