CollocWt.cc

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 1993-2012 John W. Eaton
00004 
00005 This file is part of Octave.
00006 
00007 Octave is free software; you can redistribute it and/or modify it
00008 under the terms of the GNU General Public License as published by the
00009 Free Software Foundation; either version 3 of the License, or (at your
00010 option) any later version.
00011 
00012 Octave is distributed in the hope that it will be useful, but WITHOUT
00013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00015 for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Octave; see the file COPYING.  If not, see
00019 <http://www.gnu.org/licenses/>.
00020 
00021 */
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #include <config.h>
00025 #endif
00026 
00027 #include <iostream>
00028 
00029 #include <cfloat>
00030 
00031 #include "CollocWt.h"
00032 #include "f77-fcn.h"
00033 #include "lo-error.h"
00034 
00035 // The following routines jcobi, dif, and dfopr are based on the code
00036 // found in Villadsen, J. and M. L. Michelsen, Solution of Differential
00037 // Equation Models by Polynomial Approximation, Prentice-Hall (1978)
00038 // pages 418-420.
00039 //
00040 // Translated to C++ by jwe.
00041 
00042 // Compute the first three derivatives of the node polynomial.
00043 //
00044 //                 n0     (alpha,beta)           n1
00045 //   p  (x)  =  (x)   *  p (x)         *  (1 - x)
00046 //    nt                   n
00047 //
00048 // at the interpolation points.  Each of the parameters n0 and n1
00049 // may be given the value 0 or 1.  The total number of points
00050 // nt = n + n0 + n1
00051 //
00052 // The values of root must be known before a call to dif is possible.
00053 // They may be computed using jcobi.
00054 
00055 static void
00056 dif (octave_idx_type nt, double *root, double *dif1, double *dif2,
00057      double *dif3)
00058 {
00059   // Evaluate derivatives of node polynomial using recursion formulas.
00060 
00061   for (octave_idx_type i = 0; i < nt; i++)
00062     {
00063       double x = root[i];
00064 
00065       dif1[i] = 1.0;
00066       dif2[i] = 0.0;
00067       dif3[i] = 0.0;
00068 
00069       for (octave_idx_type j = 0; j < nt; j++)
00070         {
00071           if (j != i)
00072             {
00073               double y = x - root[j];
00074 
00075               dif3[i] = y * dif3[i] + 3.0 * dif2[i];
00076               dif2[i] = y * dif2[i] + 2.0 * dif1[i];
00077               dif1[i] = y * dif1[i];
00078             }
00079         }
00080     }
00081 }
00082 
00083 // Compute the zeros of the Jacobi polynomial.
00084 //
00085 //    (alpha,beta)
00086 //   p  (x)
00087 //    n
00088 //
00089 // Use dif to compute the derivatives of the node
00090 // polynomial
00091 //
00092 //                 n0     (alpha,beta)           n1
00093 //   p  (x)  =  (x)   *  p (x)         *  (1 - x)
00094 //    nt                   n
00095 //
00096 // at the interpolation points.
00097 //
00098 // See Villadsen and Michelsen, pages 131-132 and 418.
00099 //
00100 // Input parameters:
00101 //
00102 //   nd     : the dimension of the vectors dif1, dif2, dif3, and root
00103 //
00104 //   n      : the degree of the jacobi polynomial, (i.e. the number
00105 //            of interior interpolation points)
00106 //
00107 //   n0     : determines whether x = 0 is included as an
00108 //            interpolation point
00109 //
00110 //              n0 = 0  ==>  x = 0 is not included
00111 //              n0 = 1  ==>  x = 0 is included
00112 //
00113 //   n1     : determines whether x = 1 is included as an
00114 //            interpolation point
00115 //
00116 //              n1 = 0  ==>  x = 1 is not included
00117 //              n1 = 1  ==>  x = 1 is included
00118 //
00119 //   alpha  : the value of alpha in the description of the jacobi
00120 //            polynomial
00121 //
00122 //   beta   : the value of beta in the description of the jacobi
00123 //            polynomial
00124 //
00125 //   For a more complete explanation of alpha an beta, see Villadsen
00126 //   and Michelsen, pages 57 to 59.
00127 //
00128 // Output parameters:
00129 //
00130 //   root   : one dimensional vector containing on exit the
00131 //            n + n0 + n1 zeros of the node polynomial used in the
00132 //            interpolation routine
00133 //
00134 //   dif1   : one dimensional vector containing the first derivative
00135 //            of the node polynomial at the zeros
00136 //
00137 //   dif2   : one dimensional vector containing the second derivative
00138 //            of the node polynomial at the zeros
00139 //
00140 //   dif3   : one dimensional vector containing the third derivative
00141 //            of the node polynomial at the zeros
00142 
00143 static bool
00144 jcobi (octave_idx_type n, octave_idx_type n0, octave_idx_type n1,
00145        double alpha, double beta, double *dif1, double *dif2,
00146        double *dif3, double *root)
00147 {
00148   assert (n0 == 0 || n0 == 1);
00149   assert (n1 == 0 || n1 == 1);
00150 
00151   octave_idx_type nt = n + n0 + n1;
00152 
00153   assert (nt > 1);
00154 
00155 // -- first evaluation of coefficients in recursion formulas.
00156 // -- recursion coefficients are stored in dif1 and dif2.
00157 
00158   double ab = alpha + beta;
00159   double ad = beta - alpha;
00160   double ap = beta * alpha;
00161 
00162   dif1[0] = (ad / (ab + 2.0) + 1.0) / 2.0;
00163   dif2[0] = 0.0;
00164 
00165   if (n >= 2)
00166     {
00167       for (octave_idx_type i = 1; i < n; i++)
00168         {
00169           double z1 = i;
00170           double z = ab + 2 * z1;
00171 
00172           dif1[i] = (ab * ad / z / (z + 2.0) + 1.0) / 2.0;
00173 
00174           if (i == 1)
00175             dif2[i] = (ab + ap + z1) / z / z / (z + 1.0);
00176           else
00177             {
00178               z = z * z;
00179               double y = z1 * (ab + z1);
00180               y = y * (ap + y);
00181               dif2[i] = y / z / (z - 1.0);
00182             }
00183         }
00184     }
00185 
00186   // Root determination by Newton method with suppression of previously
00187   // determined roots.
00188 
00189   double x = 0.0;
00190 
00191   for (octave_idx_type i = 0; i < n; i++)
00192     {
00193       bool done = false;
00194 
00195       int k = 0;
00196 
00197       while (! done)
00198         {
00199           double xd = 0.0;
00200           double xn = 1.0;
00201           double xd1 = 0.0;
00202           double xn1 = 0.0;
00203 
00204           for (octave_idx_type j = 0; j < n; j++)
00205             {
00206               double xp  = (dif1[j] - x) * xn  - dif2[j] * xd;
00207               double xp1 = (dif1[j] - x) * xn1 - dif2[j] * xd1 - xn;
00208 
00209               xd  = xn;
00210               xd1 = xn1;
00211               xn  = xp;
00212               xn1 = xp1;
00213             }
00214 
00215           double zc  = 1.0;
00216           double z = xn / xn1;
00217 
00218           if (i != 0)
00219             {
00220               for (octave_idx_type j = 1; j <= i; j++)
00221                 zc = zc - z / (x - root[j-1]);
00222             }
00223 
00224           z = z / zc;
00225           x = x - z;
00226 
00227           // Famous last words:  100 iterations should be more than
00228           // enough in all cases.
00229 
00230           if (++k > 100 || xisnan (z))
00231             return false;
00232 
00233           if (std::abs (z) <= 100 * DBL_EPSILON)
00234             done = true;
00235         }
00236 
00237       root[i] = x;
00238       x = x + sqrt (DBL_EPSILON);
00239     }
00240 
00241   // Add interpolation points at x = 0 and/or x = 1.
00242 
00243   if (n0 != 0)
00244     {
00245       for (octave_idx_type i = n; i > 0; i--)
00246         root[i] = root[i-1];
00247 
00248       root[0] = 0.0;
00249     }
00250 
00251   if (n1 != 0)
00252     root[nt-1] = 1.0;
00253 
00254   dif (nt, root, dif1, dif2, dif3);
00255 
00256   return true;
00257 }
00258 
00259 // Compute derivative weights for orthogonal collocation.
00260 //
00261 // See Villadsen and Michelsen, pages 133-134, 419.
00262 //
00263 // Input parameters:
00264 //
00265 //   nd     : the dimension of the vectors dif1, dif2, dif3, and root
00266 //
00267 //   n      : the degree of the jacobi polynomial, (i.e. the number
00268 //            of interior interpolation points)
00269 //
00270 //   n0     : determines whether x = 0 is included as an
00271 //            interpolation point
00272 //
00273 //              n0 = 0  ==>  x = 0 is not included
00274 //              n0 = 1  ==>  x = 0 is included
00275 //
00276 //   n1     : determines whether x = 1 is included as an
00277 //            interpolation point
00278 //
00279 //              n1 = 0  ==>  x = 1 is not included
00280 //              n1 = 1  ==>  x = 1 is included
00281 //
00282 //   i      : the index of the node for which the weights are to be
00283 //            calculated
00284 //
00285 //   id     : indicator
00286 //
00287 //              id = 1  ==>  first derivative weights are computed
00288 //              id = 2  ==>  second derivative weights are computed
00289 //              id = 3  ==>  gaussian weights are computed (in this
00290 //                           case, the value of i is irrelevant)
00291 //
00292 // Output parameters:
00293 //
00294 //   dif1   : one dimensional vector containing the first derivative
00295 //            of the node polynomial at the zeros
00296 //
00297 //   dif2   : one dimensional vector containing the second derivative
00298 //            of the node polynomial at the zeros
00299 //
00300 //   dif3   : one dimensional vector containing the third derivative
00301 //            of the node polynomial at the zeros
00302 //
00303 //   vect   : one dimensional vector of computed weights
00304 
00305 static void
00306 dfopr (octave_idx_type n, octave_idx_type n0, octave_idx_type n1,
00307        octave_idx_type i, octave_idx_type id, double *dif1,
00308        double *dif2, double *dif3, double *root, double *vect)
00309 {
00310   assert (n0 == 0 || n0 == 1);
00311   assert (n1 == 0 || n1 == 1);
00312 
00313   octave_idx_type nt = n + n0 + n1;
00314 
00315   assert (nt > 1);
00316 
00317   assert (id == 1 || id == 2 || id == 3);
00318 
00319   if (id != 3)
00320     assert (i >= 0 && i < nt);
00321 
00322   // Evaluate discretization matrices and Gaussian quadrature weights.
00323   // Quadrature weights are normalized to sum to one.
00324 
00325   if (id != 3)
00326     {
00327       for (octave_idx_type j = 0; j < nt; j++)
00328         {
00329           if (j == i)
00330             {
00331               if (id == 1)
00332                 vect[i] = dif2[i] / dif1[i] / 2.0;
00333               else
00334                 vect[i] = dif3[i] / dif1[i] / 3.0;
00335             }
00336           else
00337             {
00338               double y = root[i] - root[j];
00339 
00340               vect[j] = dif1[i] / dif1[j] / y;
00341 
00342               if (id == 2)
00343                 vect[j] = vect[j] * (dif2[i] / dif1[i] - 2.0 / y);
00344             }
00345         }
00346     }
00347   else
00348     {
00349       double y = 0.0;
00350 
00351       for (octave_idx_type j = 0; j < nt; j++)
00352         {
00353           double x  = root[j];
00354 
00355           double ax = x * (1.0 - x);
00356 
00357           if (n0 == 0)
00358             ax = ax / x / x;
00359 
00360           if (n1 == 0)
00361             ax = ax / (1.0 - x) / (1.0 - x);
00362 
00363           vect[j] = ax / (dif1[j] * dif1[j]);
00364 
00365           y = y + vect[j];
00366         }
00367 
00368       for (octave_idx_type j = 0; j < nt; j++)
00369         vect[j] = vect[j] / y;
00370     }
00371 }
00372 
00373 // Error handling.
00374 
00375 void
00376 CollocWt::error (const char* msg)
00377 {
00378   (*current_liboctave_error_handler) ("fatal CollocWt error: %s", msg);
00379 }
00380 
00381 CollocWt&
00382 CollocWt::set_left (double val)
00383 {
00384   if (val >= rb)
00385     {
00386       error ("left bound greater than right bound");
00387       return *this;
00388     }
00389 
00390   lb = val;
00391   initialized = 0;
00392   return *this;
00393 }
00394 
00395 CollocWt&
00396 CollocWt::set_right (double val)
00397 {
00398   if (val <= lb)
00399     {
00400       error ("right bound less than left bound");
00401       return *this;
00402     }
00403 
00404   rb = val;
00405   initialized = 0;
00406   return *this;
00407 }
00408 
00409 void
00410 CollocWt::init (void)
00411 {
00412   // Check for possible errors.
00413 
00414   double wid = rb - lb;
00415   if (wid <= 0.0)
00416     {
00417       error ("width less than or equal to zero");
00418       return;
00419     }
00420 
00421   octave_idx_type nt = n + inc_left + inc_right;
00422 
00423   if (nt < 0)
00424     {
00425       error ("total number of collocation points less than zero");
00426       return;
00427     }
00428   else if (nt == 0)
00429     return;
00430 
00431   Array<double> dif1 (dim_vector (nt, 1));
00432   double *pdif1 = dif1.fortran_vec ();
00433 
00434   Array<double> dif2 (dim_vector (nt, 1));
00435   double *pdif2 = dif2.fortran_vec ();
00436 
00437   Array<double> dif3 (dim_vector (nt, 1));
00438   double *pdif3 = dif3.fortran_vec ();
00439 
00440   Array<double> vect (dim_vector (nt, 1));
00441   double *pvect = vect.fortran_vec ();
00442 
00443   r.resize (nt, 1);
00444   q.resize (nt, 1);
00445   A.resize (nt, nt);
00446   B.resize (nt, nt);
00447 
00448   double *pr = r.fortran_vec ();
00449 
00450   // Compute roots.
00451 
00452   if (! jcobi (n, inc_left, inc_right, Alpha, Beta, pdif1, pdif2, pdif3, pr))
00453     {
00454       error ("jcobi: newton iteration failed");
00455       return;
00456     }
00457 
00458   octave_idx_type id;
00459 
00460   // First derivative weights.
00461 
00462   id = 1;
00463   for (octave_idx_type i = 0; i < nt; i++)
00464     {
00465       dfopr (n, inc_left, inc_right, i, id, pdif1, pdif2, pdif3, pr, pvect);
00466 
00467       for (octave_idx_type j = 0; j < nt; j++)
00468         A(i,j) = vect(j);
00469     }
00470 
00471   // Second derivative weights.
00472 
00473   id = 2;
00474   for (octave_idx_type i = 0; i < nt; i++)
00475     {
00476       dfopr (n, inc_left, inc_right, i, id, pdif1, pdif2, pdif3, pr, pvect);
00477 
00478       for (octave_idx_type j = 0; j < nt; j++)
00479         B(i,j) = vect(j);
00480     }
00481 
00482   // Gaussian quadrature weights.
00483 
00484   id = 3;
00485   double *pq = q.fortran_vec ();
00486   dfopr (n, inc_left, inc_right, id, id, pdif1, pdif2, pdif3, pr, pq);
00487 
00488   initialized = 1;
00489 }
00490 
00491 std::ostream&
00492 operator << (std::ostream& os, const CollocWt& a)
00493 {
00494   if (a.left_included ())
00495     os << "left  boundary is included\n";
00496   else
00497     os << "left  boundary is not included\n";
00498 
00499   if (a.right_included ())
00500     os << "right boundary is included\n";
00501   else
00502     os << "right boundary is not included\n";
00503 
00504   os << "\n";
00505 
00506   os << a.Alpha << " " << a.Beta << "\n\n"
00507      << a.r << "\n\n"
00508      << a.q << "\n\n"
00509      << a.A << "\n"
00510      << a.B << "\n";
00511 
00512   return os;
00513 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines