File: TestSpline.cpp

package info (click to toggle)
simbody 3.7%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 72,876 kB
  • sloc: cpp: 248,828; ansic: 18,240; sh: 29; makefile: 24
file content (647 lines) | stat: -rw-r--r-- 25,140 bytes parent folder | download | duplicates (4)
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
/* -------------------------------------------------------------------------- *
 *                        Simbody(tm): SimTKmath                              *
 * -------------------------------------------------------------------------- *
 * This is part of the SimTK biosimulation toolkit originating from           *
 * Simbios, the NIH National Center for Physics-Based Simulation of           *
 * Biological Structures at Stanford, funded under the NIH Roadmap for        *
 * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody.  *
 *                                                                            *
 * Portions copyright (c) 2008-12 Stanford University and the Authors.        *
 * Authors: Peter Eastman                                                     *
 * Contributors: Matthew Millard (the testNaturalCubicSpline code)            *
 *                                                                            *
 * 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.                                             *
 * -------------------------------------------------------------------------- */

#include "SimTKmath.h"
#include <vector> // use some std::vectors to test Array_ interoperability
#include <iostream>
#include <fstream>
#include <cstdio>

using namespace SimTK;
using namespace std;

const Real TESTTOL = 1e-9;

void testSpline() {
    Vector_<Vec3> coeff(5);
    coeff[0] = Vec3(0, 1, 2);
    coeff[1] = Vec3(1, 4, 1);
    coeff[2] = Vec3(2, 2, 20);
    coeff[3] = Vec3(1, -1, 2);
    coeff[4] = Vec3(0, 0, 1);
    Vector x(Vec5(0, 1, 2, 5, 10));
    
    // Create a linear spline, and verify that it interpolates linearly between
    // the control points.
    
    Spline_<Vec3> spline(1, x, coeff);
    for (int i = 0; i < x.size(); ++i)
        SimTK_TEST_EQ(coeff[i], spline.calcValue(Vector(1, x[i])));
    std::vector<int> deriv;
    deriv.push_back(0);
    for (int i = 0; i < x.size()-1; ++i) {
        for (int j = 0; j < 10; ++j) {
            Real fract = (i+1.0)/12.0;
            Real t = x[i]+fract*(x[i+1]-x[i]);
            SimTK_TEST_EQ_TOL(spline.calcValue(Vector(1, t)), 
                              coeff[i]+fract*(coeff[i+1]-coeff[i]), TESTTOL);
            SimTK_TEST_EQ_TOL(spline.calcDerivative(deriv, Vector(1, t)), 
                              (coeff[i+1]-coeff[i])/(x[i+1]-x[i]),TESTTOL);
        }
    }
    
    // Create a cubic spline and verify the derivative calculations.
    
    spline = Spline_<Vec3>(3, x, coeff);
    Real delta = 1e-10;
    for (int i = 0; i < x.size()-1; ++i) {
        for (int j = 0; j < 10; ++j) {
            Real fract = (i+1.0)/12.0;
            Real t = x[i]+fract*(x[i+1]-x[i]);
            Vec3 value1 = spline.calcValue(Vector(1, t-delta));
            Vec3 value2 = spline.calcValue(Vector(1, t+delta));
            SimTK_TEST_EQ_TOL(spline.calcDerivative(deriv, Vector(1, t)), 
                              (value2-value1)/(2*delta), 1e-4);
        }
    }
}

void testSplineFitter() {
    Real stddev = 0.5;
    int n = 100;
    Random::Gaussian random(0.0, stddev);
    Vector x(n);
    Vector_<Vec3> truey(n);
    Vector_<Vec3> y(n);
    for (int i = 0; i < x.size(); ++i) {
        x[i] = i*0.1;
        truey[i] = Vec3(sin(x[i]), 3.0*sin(2*x[i]), cos(x[i]));
        y[i] = truey[i] + Vec3(random.getValue(),random.getValue(),random.getValue());
    }
    SplineFitter<Vec3> fitter = SplineFitter<Vec3>::fitFromGCV(3, x, y);
    Spline_<Vec3> spline1 = fitter.getSpline();
    
    // The fitting should have reduced the error.
    
    Vec3 originalError = mean(abs(y-truey));
    Vec3 fittedError = mean(abs(spline1.getControlPointValues()-truey));
    SimTK_TEST(fittedError[0] < originalError[0]);
    SimTK_TEST(fittedError[1] < originalError[1]);
    SimTK_TEST(fittedError[2] < originalError[2]);
    
    // If we perform the fitting again, explicitly specifying the same value for
    // the smoothing parameter, it should produce identical results.   
    SimTK_TEST_EQ_TOL(SplineFitter<Vec3>::fitForSmoothingParameter
                                    (3, x, y, fitter.getSmoothingParameter())
                                        .getSpline().getControlPointValues(), 
                      spline1.getControlPointValues(), 
                      TESTTOL);
    
    // Likewise, specifying the same number of degrees of freedom should produce
    // identical results.
    SimTK_TEST_EQ_TOL(SplineFitter<Vec3>::fitFromDOF
                                    (3, x, y, fitter.getDegreesOfFreedom())
                                        .getSpline().getControlPointValues(), 
                      spline1.getControlPointValues(),
                      TESTTOL);
    
    // If we specify a smoothing parameter of 0, it should exactly reproduce 
    // the original data.
    Spline_<Vec3> nosmoothing = SplineFitter<Vec3>::fitForSmoothingParameter
                                                    (3, x, y, 0.0).getSpline();
    for (int i = 0; i < x.size(); ++i)
        SimTK_TEST_EQ_TOL(y[i], nosmoothing.calcValue(Vector(1, x[i])),TESTTOL);
}

void testRealSpline() {
    Vector coeff(5);
    coeff[0] = 0;
    coeff[1] = 1;
    coeff[2] = 2;
    coeff[3] = 1;
    coeff[4] = 0;
    Vector x(Vec5(0, 1, 2, 5, 10));

    // Create a linear spline, and verify that it interpolates linearly between
    // the control points.

    Spline spline(1, x, coeff);
    for (int i = 0; i < x.size(); ++i)
        SimTK_TEST_EQ_TOL(coeff[i], spline.calcValue(Vector(1, x[i])), TESTTOL);
    Array_<int> deriv;
    deriv.push_back(0);
    for (int i = 0; i < x.size()-1; ++i) {
        for (int j = 0; j < 10; ++j) {
            Real fract = (i+1.0)/12.0;
            Real t = x[i]+fract*(x[i+1]-x[i]);
            SimTK_TEST_EQ_TOL(spline.calcValue(Vector(1, t)), 
                              coeff[i]+fract*(coeff[i+1]-coeff[i]), TESTTOL);
            SimTK_TEST_EQ_TOL(spline.calcDerivative(deriv, Vector(1, t)), 
                              (coeff[i+1]-coeff[i])/(x[i+1]-x[i]), TESTTOL);
        }
    }
    SimTK_TEST_EQ_TOL(1, spline.getControlPointValues()[1], TESTTOL);

    // Try using a SplineFitter.

    SplineFitter<Real> fitter = SplineFitter<Real>::fitFromGCV(3, x, coeff);
    Spline spline2 = fitter.getSpline();
    SimTK_TEST_EQ_TOL(3, spline2.getSplineDegree(),TESTTOL);
}

//MM bits added to test the numerical accuracy of the natural cubic splines.
/**
* This function computes a standard central difference dy/dx. 
* If extrap_endpoints is set to 1, then the derivative at the 
* end points is estimated by linearly extrapolating the dy/dx 
* values beside the end points
*
* @param x domain vector
* @param y range vector
& @param extrap_endpoints: 
*   (false)   Endpoints of the returned vector will be zero, 
*             because a central difference is undefined at 
*             these endpoints
*    (true)   Endpoints are computed by linearly extrapolating 
*             using a first difference from the neighboring 2
*             points
*                                    
* @returns dy/dx computed using central differences
*/
Vector getCentralDifference(Vector x, Vector y, 
                                   bool extrap_endpoints){
    Vector dy(x.size());
    double dx1,dx2;
    double dy1,dy2;
    int size = x.size();
    for(int i=1; i<x.size()-1; i++){
        dx1 = x(i)-x(i-1);
        dx2 = x(i+1)-x(i);
        dy1 = y(i)-y(i-1);
        dy2 = y(i+1)-y(i);
        dy(i)= 0.5*dy1/dx1 + 0.5*dy2/dx2;
    }

    if(extrap_endpoints == true){
        dy1 = dy(2)-dy(1);
        dx1 = x(2)-x(1);
        dy(0) = dy(1) + (dy1/dx1)*(x(0)-x(1));

        dy2 = dy(size-2)-dy(size-3);
        dx2 = x(size-2)-x(size-3);
        dy(size-1) = dy(size-2) + (dy2/dx2)*(x(size-1)-x(size-2));
    }
    return dy;
}

/**
* This function will print cvs file of the column vector 
* col0 and the matrix data
*
* @params col0: A vector that must have the same number of rows 
*               as the data matrix. This column vector is 
*               printed as the first column
* @params data: A matrix of data
* @params filename: The name of the file to print
*/
void printMatrixToFile(Vector col0,Matrix data, string filename){
    ofstream datafile;
    datafile.open(filename.c_str());

    for(int i = 0; i < data.nrow(); i++){
        datafile << col0(i) << ",";
        for(int j = 0; j < data.ncol(); j++){
            if(j<data.ncol()-1)
                datafile << data(i,j) << ",";
            else
                datafile << data(i,j) << "\n";
        }    
    }
    datafile.close();
} 

/**
* This function will compute the value and first two 
* derivatives of an analytic function at the point x. 
*
* @params x       the input value
* @params fcnType the function to compute. There are 
*                 currently 5 choices (see below)
* @returns Vector: a 3x1 vector of the value, 
*                 first derivative and second derivative
*/
Vector getAnalyticFunction(double x,int fcnType){
    Vector fdF(3);
    fdF = -1;

    switch(fcnType){
        case 0:     //f(x) = 0;
            fdF = 0;
            break;
        case 1:     //f(x) = 2*x
            fdF(0) = 2*x;   //f
            fdF(1) = 2;     //fx
            fdF(2) = 0;
            break;
        case 2:     //f(x) = x^2
            fdF(0) = x*x;   //f
            fdF(1) = 2*x;   //fx
            fdF(2) = 2;
            break;
        case 3:     //f(x) = 2*x + x*x;
            //f
            fdF(0) = 2*x + x*x;
            fdF(1) = 2 + 2*x;
            fdF(2) = 2;
            break;
        case 4:     //f(x)  =2*x + x*x + 5*x*x*x
            fdF(0) = 2*x + x*x + 5*x*x*x;
            fdF(1) = 2 + 2*x + 15*x*x;
            fdF(2) = 2 + 30*x;
            break;
        case 5:     //fx(x) = sin(x)
            fdF(0) = sin(x);
            fdF(1) = cos(x);
            fdF(2) = -sin(x);
            break;
        default:
            cout << "Invalid fcnType in testBicubicSurface.cpp: getAnayticFunction";
    }


    return fdF;
}
/**
* This function tests the accuracy of the natural cubic spline sp. 
* The accuracy of the spline is tested in the following manner:
*
*    a.    Spline must pass through the knots given
*             -Error between spline and input data at the knots 
*             (should be zero)
*    b.   The first derivatives are continuous at the knot points
*             -Error between the value of the first derivative at 
*             the knot point, and what a linear extrapolation would 
*             predict just to the left and right ofthe knot point. 
*             (should be zero, within a tolerace affected by the
*             step size in xD)
*    c.   The second derivatives are continuous at the knots points
*             -Error between the value of the numerically calculated 
*             derivative at the knot point, and what a linear
*             extrapolation would predict just to the left and 
*             right of the knot point. (should be zero, within a 
*             tolerace affected by the step size in xD)
*    d.  The second derivative is zero at the end points.
*             -Numerically calculated extrapolation of the 2nd 
*             derivative should be zero at the end points within
*             some tolerance
*
*/
Vector benchmarkNaturalCubicSpline
   (Function* sp, Vector xK, Vector yK, Vector xM,Vector xD,
    string name, bool print)
{
    int size = xK.size();
    int sizeD= xD.size();
    int sizeDK = xD.size()/(xK.size()-1);
    double deltaD = (xK(xK.size()-1)-xK(0))/xD.size();

    Matrix ysp_K(size,2),ysp_M(size-1,2),ysp_D(sizeD,4);
    Vector errVec(4);
    errVec = 1;
    ysp_K = 0;
    ysp_M = 0;
    ysp_D = 0;

    vector<int> derOrder(1);
    derOrder[0] = 0;
    
    

    ///////////////////////////////////////////
    //1. Evaluate the spline at the knots, the mid points and then a dense sample
    ///////////////////////////////////////////
        Vector tmpV1(1);
        double xVal=0;
        for(int i=0;i<size;i++){
            xVal = xK(i);
            tmpV1(0)=xK(i);
            ysp_K(i,0) = sp->calcValue(tmpV1);
            ysp_K(i,1) = sp->calcDerivative(derOrder,tmpV1);
        }            
        for(int i=0;i<size-1;i++){
            xVal = xM(i);
            tmpV1(0) = xM(i);
            ysp_M(i,0) = sp->calcValue(tmpV1);
            ysp_M(i,1) = sp->calcDerivative(derOrder,tmpV1);
        }
        for(int i=0;i<sizeD;i++){
            xVal = xD(i);
            tmpV1(0) = xD(i);
            ysp_D(i,0) = sp->calcValue(tmpV1);
            ysp_D(i,1) = sp->calcDerivative(derOrder,tmpV1);
        }

    //////////////////////////////////////
    //2.    Compute the second derivative of the spline (using central 
    //differences), and linearly  interpolate to get the end points. 
    //The end points should go to exactly zero because the second
    // derivative is linear in a cubic spline, as is the linear 
    // extrapolation
    //
    // Also compute the 3rd derivative using the same method. The 3rd 
    // derivative is required in the test to determine if the second 
    // derivative is continuous at the knots or not.
    //////////////////////////////////////

        ysp_D(2)    = getCentralDifference(xD, ysp_D(1),    true);
        ysp_D(3)    = getCentralDifference(xD, ysp_D(2),    true);

    //////////////////////////////////////
    //3. Now check to see if the splines meet the conditions of a 
    //natural cubic spline:
    //////////////////////////////////////

        Vector tmpK(size,size),tmpM(size-1,size-1);

        //* a.    Spline passes through all knot points given
                    tmpK = yK-ysp_K(0);
                errVec(0) = tmpK.norm();
            
        //    b. The first derivative is continuous at the knot points. 
        //    Apply a continuity test to the data points that define 
        //    the second derivative
        //
        //        Continuity test:    a linear extrapolation of first 
        //                            derivative of the curve in interest
        //                            on either side of the point in 
        //                            interest should equal the point in 
        //                            interest;

            double ykL,ykR,y0L,dydxL,y0R,dydxR = 0;
            for(int i=1; i<size-1; i++){
                y0L = ysp_D(i*sizeDK-1,1);
                y0R = ysp_D(i*sizeDK+1,1);
                dydxL = ysp_D(i*sizeDK-1,2); //Found using central differences
                dydxR = ysp_D(i*sizeDK+1,2); //Found using central differences
                ykL = y0L + dydxL*deltaD;
                ykR = y0R - dydxR*deltaD;
                errVec(1) = (ysp_D(i*sizeDK,1)-ykL)+(ysp_D(i*sizeDK,1)-ykR);
            }
            
            
        //    c. The second derivative is continuous at the knot points. 
        //    Apply a continuity test to the data points that define 
        //    the second derivative. This also tests if the first 
        //    derivative is smooth.
        //
        //        Continuity test:    a linear extrapolation of first
        //                            derivative of the curve in interest
        //                            on either side of the point in 
        //                            interest should equal the point in 
        //                            interest;
            for(int i=1; i<size-1; i++){
                y0L = ysp_D(i*sizeDK-1,2);
                y0R = ysp_D(i*sizeDK+1,2);
                dydxL = ysp_D(i*sizeDK-1,3); //Found using central differences
                dydxR = ysp_D(i*sizeDK+1,3); //Found using central differences
                ykL = y0L + dydxL*deltaD;
                ykR = y0R - dydxR*deltaD;
                errVec(2) = (ysp_D(i*sizeDK,2)-ykL)+(ysp_D(i*sizeDK,2)-ykR);
            }    

        //////////////////////////////////////
        //* d.    The second derivative is zero at the end points
        //////////////////////////////////////

        errVec(3) = abs(ysp_D(0,2)) + abs(ysp_D(sizeD-1,2));

        

        //////////////////////////////////////
        //print the data for analysis
        //////////////////////////////////////

        if(print==true){
            string fname = name;
            fname.append("_K.csv");
            printMatrixToFile(xK,    ysp_K,    fname);

            fname = name;
            fname.append("_M.csv");
            printMatrixToFile(xM,    ysp_M,    fname);

            fname = name;
            fname.append("_D.csv");
            printMatrixToFile(xD,    ysp_D,    fname);
        }

        return errVec;

}

/**
* The test works by seeing if the tested splines have the properties of 
* a natural cubic spline. To do so, this test file has several steps
*
* User Steps: Configure the script
*        a. Choose the function to be interpolated
*        b. Choose the location and number of knot points
*        c. Choose the density of a high resolution interpolation
*
* Test Script Steps:
* 0. Initialize the input vectors xK, xM, and xD for the knot locations, 
*    mid knot location and high resolution step locations respectively
* 1. Initialize the analytically computed output yK, yM and yD
* 2. Create each of the spline objects. 
* 3. Evaluate the numerical accuracy of the splines by calling 
*    testNaturalCubicSpline
*/
void testNaturalCubicSpline() {
    /////////////////////////////
    //Configuration Variables
    ////////////////////////////
        bool printToTerminal = false; //Setting this to true will print some 
                                      //useful data to the terminal
        bool printData = false;       //Set to true to print the knot, 
                                      //mid knot, and 
                                      //dense vector values, first derivatives, 
                                      //and second derivatives (for the splines) 
                                      //for analysis outside of this script.
        int fcnType = 5;    //Chooses what kind of analytical test function to 
                            //use to initialize and test the various 
                            //spline classes
        const int size =6;             //Number of knot points
        const int sizeDK = 100;        //Number of points per knot in 
                                       //the densely sampled vector
        int sizeD=sizeDK*(size-1);     //Number of points in a densely sampled 
                                       //interpolation

        //Domain vector variables
        double xmin,xmax,deltaX,deltaD;
        xmin = Pi/4;            //Value of first knot
        xmax = Pi/2;            //Value of the final knot

        deltaX = (xmax-xmin)/(size-1);    
        deltaD = (xmax-xmin)/(sizeD-1);
        double etime = 0;
    /////////////////////////////
    //Test Code body
    ////////////////////////////

        Matrix testResults(4,1); //This matrix stores the results of the 
                                        //5 tests in each row entry, for each 
                                        //of the 2 spline classes tested.
                                        // SimTK SplineFitter results are stored
                                        // in column 0
                                        //  OpenSim::NaturalCubicSpline results
                                        //  are stored in column 1
        //testResults.elementwiseAssign(0.0);
        testResults = -1;
        Vector tmpV1(1);

        //Generate initialization knot points (denoted by a 'K') 
        //        and the mid points (denoted by a 'M')        
        //        and for the densely sampled interpolation vector 
        //        (denoted by a 'D')
        Vector xK(size), xM(size-1), xD(sizeD);
        Matrix yK(size,3), yM(size-1,3), yD(sizeD,3);



    ///////////////////////////////////////////
    //0. Initialize the input vectors xK, xM and xD
    ///////////////////////////////////////////
        for (int i = 0; i < size; i++) {
            xK(i) = xmin + ((double)i)*deltaX;
            if(i<size-1){
                xM(i) = xmin + deltaX/(double)2 + ((double)i)*deltaX;
            }
        }
        for(int i = 0; i < sizeD; i++)
            xD(i) = xmin + deltaD*(double)i;

    ///////////////////////////////////////////        
    //1.    Initialize the analytic function vector data to interpolate
    //        Let the user know which function is being used
    ///////////////////////////////////////////
        if(printToTerminal==true){
            switch(fcnType){
                case 0:
                    cout << "f(x) = 0" <<endl;
                    break;
                case 1:
                    cout << "f(x) = 2*x" <<endl;
                    break;
                case 2:
                    cout << "f(x) = x^2" <<endl;
                    break;
                case 3:
                    cout << "f(x) = 2*x + x^2 " <<endl;
                    break;
                case 4:
                    cout << "f(x) = 2*x + x^2 + 5x^3 " <<endl;
                    break;
            }
        }


        //Get the function values at the knot points
        Vector tmp(3);
        tmp = 0;
        for(int i=0; i<size;i++){
            tmp = getAnalyticFunction(xK(i),fcnType);
            for(int k=0;k<3;k++)
                yK(i,k) = tmp(k);

        }
         Vector yKVal = yK(0);

        //Get the function y, dy, ddy at the mid points
        for(int i=0; i<size-1;i++){
            tmp = getAnalyticFunction(xM(i),fcnType);
            for(int k=0;k<3;k++)
                yM(i,k) = tmp(k);
        }
        //Get the function y, dy, ddy at the dense points
        for(int i=0; i<sizeD;i++){
            tmp = getAnalyticFunction(xD(i),fcnType);
            for(int k=0;k<3;k++)
                yD(i,k) = tmp(k);
        }

    ///////////////////////////////////////////
    //2. Create each of the splines
    ///////////////////////////////////////////

        //SplineFitter
        Vector sfDerivs1(xK.size());
        

        
        Spline_<Real> sTK = SplineFitter<Real>::fitForSmoothingParameter(3,xK,yKVal,0.0).getSpline();
            
    ///////////////////////////////////////////
    //3. Test the splines
    ///////////////////////////////////////////

        testResults(0) = benchmarkNaturalCubicSpline(&sTK, xK, yK(0), xM, xD, "simtk_splinefitter",true);
        if(printToTerminal==true){
            cout << "Test Result Matrix: 0 or small numbers pass" <<endl;
            cout << "  column 0: SplineFitter, column 1: OpenSim::NaturalCubicSpline" <<endl;
            cout << "  row 0: Passes through knots (tol 1e-14)" << endl;
            cout << "  row 1: First derivative is continuous and smooth (tol " << deltaD << ")" << endl;
            cout << "  row 2: Second derivative is continuous (tol " << 10*deltaD << ")" << endl;
            cout << "  row 3: Second derivative is zero at endpoints (tol "<< deltaD/10 <<")" << endl;
            cout << testResults << endl;
        }

    //////////////////////////////////////
    //4. Run numerical assertions on each test
    //////////////////////////////////////

        double tol = 0;
            
        for(int k=0;k<testResults.ncol();k++){
            for(int i=0;i<testResults.nrow();i++){                    
                switch(i){
                    case 0: //Equal at knots
                        tol = 1e-14;
                        break;
                    case 1: //Continuous 1st derivative
                        tol = deltaD;
                        break;
                    case 2: //Continuous 2nd derivative
                        tol = 10*deltaD;
                        break;    
                    case 3: //2nd derivative zero at end points
                        tol = deltaD/10;
                        break;
                    default:
                        cout << "testNCSpline: Invalid error type selected" << endl;
                }
                //cout << "Testing (i,k) " << i << " " << k << " tol " << tol << " \tval " << testResults(i,k) << endl;
                SimTK_TEST_EQ_TOL(testResults(i,k),0,tol);
            }
        }
        // getchar();

}

int main () {
    SimTK_START_TEST("TestSpline");
        SimTK_SUBTEST(testSpline);
        SimTK_SUBTEST(testSplineFitter);
        SimTK_SUBTEST(testRealSpline);
        SimTK_SUBTEST(testNaturalCubicSpline);
    SimTK_END_TEST();
}