Back to home page

EIC code displayed by LXR

 
 

    


Warning, /include/Geant4/tools/spline is written in an unsupported language. File is not indexed.

0001 // Copyright (C) 2010, Guy Barrand. All rights reserved.
0002 // See the file tools.license for terms.
0003 
0004 #ifndef tools_spline
0005 #define tools_spline
0006 
0007 // From Federico Carminati code found in root-6.08.06/TSpline.h, TSpline.cxx.
0008 
0009 #include "mnmx"
0010 #include <cstddef>
0011 #include <vector>
0012 #include <ostream>
0013 #include <cmath>
0014 
0015 namespace tools {
0016 namespace spline {
0017 
0018 class base_poly {
0019 public:
0020   base_poly():fX(0),fY(0) {}
0021   base_poly(double x,double y):fX(x),fY(y) {}
0022   virtual ~base_poly(){}
0023 public:
0024   base_poly(base_poly const &a_from):fX(a_from.fX),fY(a_from.fY) {}
0025   base_poly& operator=(base_poly const &a_from) {
0026     if(this==&a_from) return *this;
0027     fX = a_from.fX;
0028     fY = a_from.fY;
0029     return *this;
0030   }
0031 public:
0032   const double& X() const {return fX;}
0033   const double& Y() const {return fY;}
0034   double &X() {return fX;}
0035   double &Y() {return fY;}
0036 protected:
0037   double fX;     // abscissa
0038   double fY;     // constant term
0039 };
0040 
0041 class cubic_poly : public base_poly {
0042 public:
0043   cubic_poly():fB(0), fC(0), fD(0) {}
0044   cubic_poly(double x, double y, double b, double c, double d):base_poly(x,y), fB(b), fC(c), fD(d) {}
0045 public:
0046   cubic_poly(cubic_poly const &a_from)
0047   :base_poly(a_from), fB(a_from.fB), fC(a_from.fC), fD(a_from.fD) {}
0048   cubic_poly& operator=(cubic_poly const &a_from) {
0049     if(this==&a_from) return *this;
0050     base_poly::operator=(a_from);
0051     fB = a_from.fB;
0052     fC = a_from.fC;
0053     fD = a_from.fD;
0054     return *this;
0055   }
0056 public:
0057   double &B() {return fB;}
0058   double &C() {return fC;}
0059   double &D() {return fD;}
0060   double eval(double x) const {double dx=x-fX;return (fY+dx*(fB+dx*(fC+dx*fD)));}
0061 protected:
0062   double fB; // first order expansion coefficient :  fB*1! is the first derivative at x
0063   double fC; // second order expansion coefficient : fC*2! is the second derivative at x
0064   double fD; // third order expansion coefficient :  fD*3! is the third derivative at x
0065 };
0066 
0067 ////////////////////////////////////////////////////////////////////////////////////
0068 ////////////////////////////////////////////////////////////////////////////////////
0069 ////////////////////////////////////////////////////////////////////////////////////
0070 class base_spline {
0071 protected:
0072   base_spline(std::ostream& a_out):m_out(a_out), fDelta(-1), fXmin(0), fXmax(0), fNp(0), fKstep(false) {}
0073 public:
0074   base_spline(std::ostream& a_out,double delta, double xmin, double xmax, size_t np, bool step)
0075   :m_out(a_out),fDelta(delta), fXmin(xmin),fXmax(xmax), fNp(np), fKstep(step)
0076   {}
0077   virtual ~base_spline() {}
0078 protected:
0079   base_spline(const base_spline& a_from)
0080   :m_out(a_from.m_out)
0081   ,fDelta(a_from.fDelta),fXmin(a_from.fXmin),fXmax(a_from.fXmax),fNp(a_from.fNp),fKstep(a_from.fKstep) {}
0082   base_spline& operator=(const base_spline& a_from) {
0083     if(this==&a_from) return *this;
0084     fDelta=a_from.fDelta;
0085     fXmin=a_from.fXmin;
0086     fXmax=a_from.fXmax;
0087     fNp=a_from.fNp;
0088     fKstep=a_from.fKstep;
0089     return *this;
0090   }
0091 protected:
0092   std::ostream& m_out;
0093   double  fDelta;     // Distance between equidistant knots
0094   double  fXmin;      // Minimum value of abscissa
0095   double  fXmax;      // Maximum value of abscissa
0096   size_t  fNp;        // Number of knots
0097   bool    fKstep;     // True of equidistant knots
0098 };
0099 
0100 
0101 //////////////////////////////////////////////////////////////////////////
0102 //                                                                      //
0103 // cubic                                                                //
0104 //                                                                      //
0105 // Class to create third splines to interpolate knots                   //
0106 // Arbitrary conditions can be introduced for first and second          //
0107 // derivatives at beginning and ending points                           //
0108 //                                                                      //
0109 //////////////////////////////////////////////////////////////////////////
0110 
0111 class cubic : public base_spline {
0112 protected:
0113   cubic(std::ostream& a_out) : base_spline(a_out) , fPoly(0), fValBeg(0), fValEnd(0), fBegCond(-1), fEndCond(-1) {}
0114 public:
0115   cubic(std::ostream& a_out,size_t a_n,double a_x[], double a_y[], double a_valbeg = 0, double a_valend = 0)
0116   :base_spline(a_out,-1,0,0,a_n,false)
0117   ,fValBeg(a_valbeg), fValEnd(a_valend), fBegCond(0), fEndCond(0)
0118   {
0119     if(!a_n) {
0120       m_out << "tools::spline::cubic : a_np is null." << std::endl;
0121       return;
0122     }
0123     fXmin = a_x[0];
0124     fXmax = a_x[a_n-1];
0125     fPoly.resize(a_n);
0126     for (size_t i=0; i<a_n; ++i) {
0127        fPoly[i].X() = a_x[i];
0128        fPoly[i].Y() = a_y[i];
0129     }
0130     build_coeff(); // Build the spline coefficients
0131   }
0132 public:
0133   cubic(const cubic& a_from)
0134   :base_spline(a_from)
0135   ,fPoly(a_from.fPoly),fValBeg(a_from.fValBeg),fValEnd(a_from.fValEnd),fBegCond(a_from.fBegCond),fEndCond(a_from.fEndCond)
0136   {}
0137   cubic& operator=(const cubic& a_from) {
0138     if(this==&a_from) return *this;
0139     base_spline::operator=(a_from);
0140     fPoly = a_from.fPoly;
0141     fValBeg=a_from.fValBeg;
0142     fValEnd=a_from.fValEnd;
0143     fBegCond=a_from.fBegCond;
0144     fEndCond=a_from.fEndCond;
0145     return *this;
0146   }
0147 public:
0148   double eval(double x) const {
0149     if(!fNp) return 0;
0150     // Eval this spline at x
0151     size_t klow = find_x(x);
0152     if ( (fNp > 1) && (klow >= (fNp-1))) klow = fNp-2; //see: https://savannah.cern.ch/bugs/?71651
0153     return fPoly[klow].eval(x);
0154   }
0155 protected:
0156   template<typename T>
0157   static int TMath_Nint(T x) {
0158     // Round to nearest integer. Rounds half integers to the nearest even integer.
0159     int i;
0160     if (x >= 0) {
0161       i = int(x + 0.5);
0162       if ( i & 1 && x + 0.5 == T(i) ) i--;
0163     } else {
0164       i = int(x - 0.5);
0165       if ( i & 1 && x - 0.5 == T(i) ) i++;
0166     }
0167     return i;
0168   }
0169   static int TMath_FloorNint(double x) { return TMath_Nint(::floor(x)); }
0170 
0171   size_t find_x(double x) const {
0172     int klow=0, khig=int(fNp-1);
0173     //
0174     // If out of boundaries, extrapolate
0175     // It may be badly wrong
0176     if(x<=fXmin) klow=0;
0177     else if(x>=fXmax) klow=khig;
0178     else {
0179       if(fKstep) { // Equidistant knots, use histogramming :
0180          klow = TMath_FloorNint((x-fXmin)/fDelta);
0181          // Correction for rounding errors
0182          if (x < fPoly[klow].X())
0183            klow = max_of<int>(klow-1,0);
0184          else if (klow < khig) {
0185            if (x > fPoly[klow+1].X()) ++klow;
0186          }
0187       } else {
0188          int khalf;
0189          //
0190          // Non equidistant knots, binary search
0191          while((khig-klow)>1) {
0192            khalf = (klow+khig)/2;
0193            if(x>fPoly[khalf].X()) klow=khalf;
0194            else                   khig=khalf;
0195          }
0196          //
0197          // This could be removed, sanity check
0198          if( (x<fPoly[klow].X()) || (fPoly[klow+1].X()<x) ) {
0199             m_out << "tools::spline::cubic::find_x : Binary search failed"
0200                   << " x(" << klow << ") = " << fPoly[klow].X() << " < x= " << x
0201                   << " < x(" << klow+1 << ") = " << fPoly[klow+1].X() << "."
0202                   << "." << std::endl;
0203          }     
0204       }
0205     }
0206     return klow;
0207   }
0208 
0209   void build_coeff() {
0210     ///      subroutine cubspl ( tau, c, n, ibcbeg, ibcend )
0211     ///  from  * a practical guide to splines *  by c. de boor
0212     ///     ************************  input  ***************************
0213     ///     n = number of data points. assumed to be .ge. 2.
0214     ///     (tau(i), c(1,i), i=1,...,n) = abscissae and ordinates of the
0215     ///        data points. tau is assumed to be strictly increasing.
0216     ///     ibcbeg, ibcend = boundary condition indicators, and
0217     ///     c(2,1), c(2,n) = boundary condition information. specifically,
0218     ///        ibcbeg = 0  means no boundary condition at tau(1) is given.
0219     ///           in this case, the not-a-knot condition is used, i.e. the
0220     ///           jump in the third derivative across tau(2) is forced to
0221     ///           zero, thus the first and the second cubic polynomial pieces
0222     ///           are made to coincide.)
0223     ///        ibcbeg = 1  means that the slope at tau(1) is made to equal
0224     ///           c(2,1), supplied by input.
0225     ///        ibcbeg = 2  means that the second derivative at tau(1) is
0226     ///           made to equal c(2,1), supplied by input.
0227     ///        ibcend = 0, 1, or 2 has analogous meaning concerning the
0228     ///           boundary condition at tau(n), with the additional infor-
0229     ///           mation taken from c(2,n).
0230     ///     ***********************  output  **************************
0231     ///     c(j,i), j=1,...,4; i=1,...,l (= n-1) = the polynomial coefficients
0232     ///        of the cubic interpolating spline with interior knots (or
0233     ///        joints) tau(2), ..., tau(n-1). precisely, in the interval
0234     ///        (tau(i), tau(i+1)), the spline f is given by
0235     ///           f(x) = c(1,i)+h*(c(2,i)+h*(c(3,i)+h*c(4,i)/3.)/2.)
0236     ///        where h = x - tau(i). the function program *ppvalu* may be
0237     ///        used to evaluate f or its derivatives from tau,c, l = n-1,
0238     ///        and k=4.
0239 
0240     int j, l;
0241     double   divdf1,divdf3,dtau,g=0;
0242     // ***** a tridiagonal linear system for the unknown slopes s(i) of
0243     //  f  at tau(i), i=1,...,n, is generated and then solved by gauss elim-
0244     //  ination, with s(i) ending up in c(2,i), all i.
0245     //     c(3,.) and c(4,.) are used initially for temporary storage.
0246     l = int(fNp-1);
0247     // compute first differences of x sequence and store in C also,
0248     // compute first divided difference of data and store in D.
0249    {for (size_t m=1; m<fNp ; ++m) {
0250        fPoly[m].C() = fPoly[m].X() - fPoly[m-1].X();
0251        fPoly[m].D() = (fPoly[m].Y() - fPoly[m-1].Y())/fPoly[m].C();
0252     }}
0253     // construct first equation from the boundary condition, of the form
0254     //             D[0]*s[0] + C[0]*s[1] = B[0]
0255     if(fBegCond==0) {
0256        if(fNp == 2) {
0257          //     no condition at left end and n = 2.
0258          fPoly[0].D() = 1.;
0259          fPoly[0].C() = 1.;
0260          fPoly[0].B() = 2.*fPoly[1].D();
0261       } else {
0262          //     not-a-knot condition at left end and n .gt. 2.
0263          fPoly[0].D() = fPoly[2].C();
0264          fPoly[0].C() = fPoly[1].C() + fPoly[2].C();
0265          fPoly[0].B() = ((fPoly[1].C()+2.*fPoly[0].C())*fPoly[1].D()*fPoly[2].C()+
0266                         fPoly[1].C()*fPoly[1].C()*fPoly[2].D())/fPoly[0].C();
0267       }
0268     } else if (fBegCond==1) {
0269       //     slope prescribed at left end.
0270       fPoly[0].B() = fValBeg;
0271       fPoly[0].D() = 1.;
0272       fPoly[0].C() = 0.;
0273     } else if (fBegCond==2) {
0274       //     second derivative prescribed at left end.
0275       fPoly[0].D() = 2.;
0276       fPoly[0].C() = 1.;
0277       fPoly[0].B() = 3.*fPoly[1].D() - fPoly[1].C()/2.*fValBeg;
0278     }
0279     bool forward_gauss_elimination = true;
0280     if(fNp > 2) {
0281       //  if there are interior knots, generate the corresp. equations and car-
0282       //  ry out the forward pass of gauss elimination, after which the m-th
0283       //  equation reads    D[m]*s[m] + C[m]*s[m+1] = B[m].
0284      {for (int m=1; m<l; ++m) {
0285          g = -fPoly[m+1].C()/fPoly[m-1].D();
0286          fPoly[m].B() = g*fPoly[m-1].B() + 3.*(fPoly[m].C()*fPoly[m+1].D()+fPoly[m+1].C()*fPoly[m].D());
0287          fPoly[m].D() = g*fPoly[m-1].C() + 2.*(fPoly[m].C() + fPoly[m+1].C());
0288       }}
0289       // construct last equation from the second boundary condition, of the form
0290       //           (-g*D[n-2])*s[n-2] + D[n-1]*s[n-1] = B[n-1]
0291       //     if slope is prescribed at right end, one can go directly to back-
0292       //     substitution, since c array happens to be set up just right for it
0293       //     at this point.
0294       if(fEndCond == 0) {
0295          if (fNp > 3 || fBegCond != 0) {
0296             //     not-a-knot and n .ge. 3, and either n.gt.3 or  also not-a-knot at
0297             //     left end point.
0298             g = fPoly[fNp-2].C() + fPoly[fNp-1].C();
0299             fPoly[fNp-1].B() = ((fPoly[fNp-1].C()+2.*g)*fPoly[fNp-1].D()*fPoly[fNp-2].C()
0300                          + fPoly[fNp-1].C()*fPoly[fNp-1].C()*(fPoly[fNp-2].Y()-fPoly[fNp-3].Y())/fPoly[fNp-2].C())/g;
0301             g = -g/fPoly[fNp-2].D();
0302             fPoly[fNp-1].D() = fPoly[fNp-2].C();
0303          } else {
0304             //     either (n=3 and not-a-knot also at left) or (n=2 and not not-a-
0305             //     knot at left end point).
0306             fPoly[fNp-1].B() = 2.*fPoly[fNp-1].D();
0307             fPoly[fNp-1].D() = 1.;
0308             g = -1./fPoly[fNp-2].D();
0309          }
0310       } else if (fEndCond == 1) {
0311          fPoly[fNp-1].B() = fValEnd;
0312          forward_gauss_elimination = false;
0313       } else if (fEndCond == 2) {
0314          //     second derivative prescribed at right endpoint.
0315          fPoly[fNp-1].B() = 3.*fPoly[fNp-1].D() + fPoly[fNp-1].C()/2.*fValEnd;
0316          fPoly[fNp-1].D() = 2.;
0317          g = -1./fPoly[fNp-2].D();
0318       }
0319     } else {
0320       if(fEndCond == 0) {
0321          if (fBegCond > 0) {
0322             //     either (n=3 and not-a-knot also at left) or (n=2 and not not-a-
0323             //     knot at left end point).
0324             fPoly[fNp-1].B() = 2.*fPoly[fNp-1].D();
0325             fPoly[fNp-1].D() = 1.;
0326             g = -1./fPoly[fNp-2].D();
0327          } else {
0328             //     not-a-knot at right endpoint and at left endpoint and n = 2.
0329             fPoly[fNp-1].B() = fPoly[fNp-1].D();
0330             forward_gauss_elimination = false;
0331          }
0332       } else if(fEndCond == 1) {
0333          fPoly[fNp-1].B() = fValEnd;
0334          forward_gauss_elimination = false;
0335       } else if(fEndCond == 2) {
0336          //     second derivative prescribed at right endpoint.
0337          fPoly[fNp-1].B() = 3.*fPoly[fNp-1].D() + fPoly[fNp-1].C()/2.*fValEnd;
0338          fPoly[fNp-1].D() = 2.;
0339          g = -1./fPoly[fNp-2].D();
0340       }
0341     }
0342     // complete forward pass of gauss elimination.
0343     if(forward_gauss_elimination) {
0344       fPoly[fNp-1].D() = g*fPoly[fNp-2].C() + fPoly[fNp-1].D();
0345       fPoly[fNp-1].B() = (g*fPoly[fNp-2].B() + fPoly[fNp-1].B())/fPoly[fNp-1].D();
0346     }
0347     // carry out back substitution
0348     j = l-1;
0349     do {
0350        fPoly[j].B() = (fPoly[j].B() - fPoly[j].C()*fPoly[j+1].B())/fPoly[j].D();
0351        --j;
0352     }  while (j>=0);
0353     // ****** generate cubic coefficients in each interval, i.e., the deriv.s
0354     //  at its left endpoint, from value and slope at its endpoints.
0355     for (size_t i=1; i<fNp; ++i) {
0356        dtau = fPoly[i].C();
0357        divdf1 = (fPoly[i].Y() - fPoly[i-1].Y())/dtau;
0358        divdf3 = fPoly[i-1].B() + fPoly[i].B() - 2.*divdf1;
0359        fPoly[i-1].C() = (divdf1 - fPoly[i-1].B() - divdf3)/dtau;
0360        fPoly[i-1].D() = (divdf3/dtau)/dtau;
0361     }
0362   }
0363 
0364 protected:
0365   std::vector<cubic_poly> fPoly; //[fNp] Array of polynomial terms
0366   double        fValBeg;     // Initial value of first or second derivative
0367   double        fValEnd;     // End value of first or second derivative
0368   int           fBegCond;    // 0=no beg cond, 1=first derivative, 2=second derivative
0369   int           fEndCond;    // 0=no end cond, 1=first derivative, 2=second derivative
0370 };
0371 
0372 }}
0373 
0374 #endif