GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
CollocWt.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1993-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <cassert>
31 #include <cmath>
32 
33 #include <limits>
34 #include <ostream>
35 
36 #include "Array.h"
37 #include "CollocWt.h"
38 #include "lo-error.h"
39 #include "lo-mappers.h"
40 
41 // The following routines jcobi, dif, and dfopr are based on the code
42 // found in Villadsen, J. and M. L. Michelsen, Solution of Differential
43 // Equation Models by Polynomial Approximation, Prentice-Hall (1978)
44 // pages 418-420.
45 //
46 // Translated to C++ by jwe.
47 
48 // Compute the first three derivatives of the node polynomial.
49 //
50 // n0 (alpha,beta) n1
51 // p (x) = (x) * p (x) * (1 - x)
52 // nt n
53 //
54 // at the interpolation points. Each of the parameters n0 and n1
55 // may be given the value 0 or 1. The total number of points
56 // nt = n + n0 + n1
57 //
58 // The values of root must be known before a call to dif is possible.
59 // They may be computed using jcobi.
60 
61 static void
62 dif (octave_idx_type nt, double *root, double *dif1, double *dif2,
63  double *dif3)
64 {
65  // Evaluate derivatives of node polynomial using recursion formulas.
66 
67  for (octave_idx_type i = 0; i < nt; i++)
68  {
69  double x = root[i];
70 
71  dif1[i] = 1.0;
72  dif2[i] = 0.0;
73  dif3[i] = 0.0;
74 
75  for (octave_idx_type j = 0; j < nt; j++)
76  {
77  if (j != i)
78  {
79  double y = x - root[j];
80 
81  dif3[i] = y * dif3[i] + 3.0 * dif2[i];
82  dif2[i] = y * dif2[i] + 2.0 * dif1[i];
83  dif1[i] = y * dif1[i];
84  }
85  }
86  }
87 }
88 
89 // Compute the zeros of the Jacobi polynomial.
90 //
91 // (alpha,beta)
92 // p (x)
93 // n
94 //
95 // Use dif to compute the derivatives of the node
96 // polynomial
97 //
98 // n0 (alpha,beta) n1
99 // p (x) = (x) * p (x) * (1 - x)
100 // nt n
101 //
102 // at the interpolation points.
103 //
104 // See Villadsen and Michelsen, pages 131-132 and 418.
105 //
106 // Input parameters:
107 //
108 // nd : the dimension of the vectors dif1, dif2, dif3, and root
109 //
110 // n : the degree of the jacobi polynomial, (i.e., the number
111 // of interior interpolation points)
112 //
113 // n0 : determines whether x = 0 is included as an
114 // interpolation point
115 //
116 // n0 = 0 ==> x = 0 is not included
117 // n0 = 1 ==> x = 0 is included
118 //
119 // n1 : determines whether x = 1 is included as an
120 // interpolation point
121 //
122 // n1 = 0 ==> x = 1 is not included
123 // n1 = 1 ==> x = 1 is included
124 //
125 // alpha : the value of alpha in the description of the jacobi
126 // polynomial
127 //
128 // beta : the value of beta in the description of the jacobi
129 // polynomial
130 //
131 // For a more complete explanation of alpha an beta, see Villadsen
132 // and Michelsen, pages 57 to 59.
133 //
134 // Output parameters:
135 //
136 // root : one dimensional vector containing on exit the
137 // n + n0 + n1 zeros of the node polynomial used in the
138 // interpolation routine
139 //
140 // dif1 : one dimensional vector containing the first derivative
141 // of the node polynomial at the zeros
142 //
143 // dif2 : one dimensional vector containing the second derivative
144 // of the node polynomial at the zeros
145 //
146 // dif3 : one dimensional vector containing the third derivative
147 // of the node polynomial at the zeros
148 
149 static bool
151  double alpha, double beta, double *dif1, double *dif2,
152  double *dif3, double *root)
153 {
154  assert (n0 == 0 || n0 == 1);
155  assert (n1 == 0 || n1 == 1);
156 
157  octave_idx_type nt = n + n0 + n1;
158 
159  assert (nt >= 1);
160 
161 // -- first evaluation of coefficients in recursion formulas.
162 // -- recursion coefficients are stored in dif1 and dif2.
163 
164  double ab = alpha + beta;
165  double ad = beta - alpha;
166  double ap = beta * alpha;
167 
168  dif1[0] = (ad / (ab + 2.0) + 1.0) / 2.0;
169  dif2[0] = 0.0;
170 
171  if (n >= 2)
172  {
173  for (octave_idx_type i = 1; i < n; i++)
174  {
175  double z1 = i;
176  double z = ab + 2 * z1;
177 
178  dif1[i] = (ab * ad / z / (z + 2.0) + 1.0) / 2.0;
179 
180  if (i == 1)
181  dif2[i] = (ab + ap + z1) / z / z / (z + 1.0);
182  else
183  {
184  z *= z;
185  double y = z1 * (ab + z1);
186  y *= (ap + y);
187  dif2[i] = y / z / (z - 1.0);
188  }
189  }
190  }
191 
192  // Root determination by Newton method with suppression of previously
193  // determined roots.
194 
195  double x = 0.0;
196 
197  for (octave_idx_type i = 0; i < n; i++)
198  {
199  bool done = false;
200 
201  int k = 0;
202 
203  while (! done)
204  {
205  double xd = 0.0;
206  double xn = 1.0;
207  double xd1 = 0.0;
208  double xn1 = 0.0;
209 
210  for (octave_idx_type j = 0; j < n; j++)
211  {
212  double xp = (dif1[j] - x) * xn - dif2[j] * xd;
213  double xp1 = (dif1[j] - x) * xn1 - dif2[j] * xd1 - xn;
214 
215  xd = xn;
216  xd1 = xn1;
217  xn = xp;
218  xn1 = xp1;
219  }
220 
221  double zc = 1.0;
222  double z = xn / xn1;
223 
224  if (i != 0)
225  {
226  for (octave_idx_type j = 1; j <= i; j++)
227  zc -= z / (x - root[j-1]);
228  }
229 
230  z /= zc;
231  x -= z;
232 
233  // Famous last words: 100 iterations should be more than
234  // enough in all cases.
235 
236  if (++k > 100 || octave::math::isnan (z))
237  return false;
238 
239  if (std::abs (z) <= 100 * std::numeric_limits<double>::epsilon ())
240  done = true;
241  }
242 
243  root[i] = x;
244  x += std::sqrt (std::numeric_limits<double>::epsilon ());
245  }
246 
247  // Add interpolation points at x = 0 and/or x = 1.
248 
249  if (n0 != 0)
250  {
251  for (octave_idx_type i = n; i > 0; i--)
252  root[i] = root[i-1];
253 
254  root[0] = 0.0;
255  }
256 
257  if (n1 != 0)
258  root[nt-1] = 1.0;
259 
260  dif (nt, root, dif1, dif2, dif3);
261 
262  return true;
263 }
264 
265 // Compute derivative weights for orthogonal collocation.
266 //
267 // See Villadsen and Michelsen, pages 133-134, 419.
268 //
269 // Input parameters:
270 //
271 // nd : the dimension of the vectors dif1, dif2, dif3, and root
272 //
273 // n : the degree of the jacobi polynomial, (i.e., the number
274 // of interior interpolation points)
275 //
276 // n0 : determines whether x = 0 is included as an
277 // interpolation point
278 //
279 // n0 = 0 ==> x = 0 is not included
280 // n0 = 1 ==> x = 0 is included
281 //
282 // n1 : determines whether x = 1 is included as an
283 // interpolation point
284 //
285 // n1 = 0 ==> x = 1 is not included
286 // n1 = 1 ==> x = 1 is included
287 //
288 // i : the index of the node for which the weights are to be
289 // calculated
290 //
291 // id : indicator
292 //
293 // id = 1 ==> first derivative weights are computed
294 // id = 2 ==> second derivative weights are computed
295 // id = 3 ==> gaussian weights are computed (in this
296 // case, the value of i is irrelevant)
297 //
298 // Output parameters:
299 //
300 // dif1 : one dimensional vector containing the first derivative
301 // of the node polynomial at the zeros
302 //
303 // dif2 : one dimensional vector containing the second derivative
304 // of the node polynomial at the zeros
305 //
306 // dif3 : one dimensional vector containing the third derivative
307 // of the node polynomial at the zeros
308 //
309 // vect : one dimensional vector of computed weights
310 
311 static void
313  octave_idx_type i, octave_idx_type id, double *dif1,
314  double *dif2, double *dif3, double *root, double *vect)
315 {
316  assert (n0 == 0 || n0 == 1);
317  assert (n1 == 0 || n1 == 1);
318 
319  octave_idx_type nt = n + n0 + n1;
320 
321  assert (nt >= 1);
322 
323  assert (id == 1 || id == 2 || id == 3);
324 
325  if (id != 3)
326  assert (i >= 0 && i < nt);
327 
328  // Evaluate discretization matrices and Gaussian quadrature weights.
329  // Quadrature weights are normalized to sum to one.
330 
331  if (id != 3)
332  {
333  for (octave_idx_type j = 0; j < nt; j++)
334  {
335  if (j == i)
336  {
337  if (id == 1)
338  vect[i] = dif2[i] / dif1[i] / 2.0;
339  else
340  vect[i] = dif3[i] / dif1[i] / 3.0;
341  }
342  else
343  {
344  double y = root[i] - root[j];
345 
346  vect[j] = dif1[i] / dif1[j] / y;
347 
348  if (id == 2)
349  vect[j] = vect[j] * (dif2[i] / dif1[i] - 2.0 / y);
350  }
351  }
352  }
353  else
354  {
355  double y = 0.0;
356 
357  for (octave_idx_type j = 0; j < nt; j++)
358  {
359  double x = root[j];
360 
361  double ax = x * (1.0 - x);
362 
363  if (n0 == 0)
364  ax = ax / x / x;
365 
366  if (n1 == 0)
367  ax = ax / (1.0 - x) / (1.0 - x);
368 
369  vect[j] = ax / (dif1[j] * dif1[j]);
370 
371  y += vect[j];
372  }
373 
374  for (octave_idx_type j = 0; j < nt; j++)
375  vect[j] = vect[j] / y;
376  }
377 }
378 
379 // Error handling.
380 
381 void
382 CollocWt::error (const char *msg)
383 {
384  (*current_liboctave_error_handler) ("CollocWt: fatal error '%s'", msg);
385 }
386 
387 CollocWt&
388 CollocWt::set_left (double val)
389 {
390  if (val >= rb)
391  error ("CollocWt: left bound greater than right bound");
392 
393  lb = val;
394  initialized = 0;
395  return *this;
396 }
397 
398 CollocWt&
400 {
401  if (val <= lb)
402  error ("CollocWt: right bound less than left bound");
403 
404  rb = val;
405  initialized = 0;
406  return *this;
407 }
408 
409 void
411 {
412  // Check for possible errors.
413 
414  double wid = rb - lb;
415  if (wid <= 0.0)
416  {
417  error ("CollocWt: width less than or equal to zero");
418  return;
419  }
420 
422 
423  if (nt < 0)
424  error ("CollocWt: total number of collocation points less than zero");
425  else if (nt == 0)
426  return;
427 
428  Array<double> dif1 (dim_vector (nt, 1));
429  double *pdif1 = dif1.fortran_vec ();
430 
431  Array<double> dif2 (dim_vector (nt, 1));
432  double *pdif2 = dif2.fortran_vec ();
433 
434  Array<double> dif3 (dim_vector (nt, 1));
435  double *pdif3 = dif3.fortran_vec ();
436 
437  Array<double> vect (dim_vector (nt, 1));
438  double *pvect = vect.fortran_vec ();
439 
440  r.resize (nt, 1);
441  q.resize (nt, 1);
442  A.resize (nt, nt);
443  B.resize (nt, nt);
444 
445  double *pr = r.fortran_vec ();
446 
447  // Compute roots.
448 
449  if (! jcobi (n, inc_left, inc_right, Alpha, Beta, pdif1, pdif2, pdif3, pr))
450  error ("jcobi: newton iteration failed");
451 
452  octave_idx_type id;
453 
454  // First derivative weights.
455 
456  id = 1;
457  for (octave_idx_type i = 0; i < nt; i++)
458  {
459  dfopr (n, inc_left, inc_right, i, id, pdif1, pdif2, pdif3, pr, pvect);
460 
461  for (octave_idx_type j = 0; j < nt; j++)
462  A(i,j) = vect(j);
463  }
464 
465  // Second derivative weights.
466 
467  id = 2;
468  for (octave_idx_type i = 0; i < nt; i++)
469  {
470  dfopr (n, inc_left, inc_right, i, id, pdif1, pdif2, pdif3, pr, pvect);
471 
472  for (octave_idx_type j = 0; j < nt; j++)
473  B(i,j) = vect(j);
474  }
475 
476  // Gaussian quadrature weights.
477 
478  id = 3;
479  double *pq = q.fortran_vec ();
480  dfopr (n, inc_left, inc_right, id, id, pdif1, pdif2, pdif3, pr, pq);
481 
482  initialized = 1;
483 }
484 
485 std::ostream&
486 operator << (std::ostream& os, const CollocWt& a)
487 {
488  if (a.left_included ())
489  os << "left boundary is included\n";
490  else
491  os << "left boundary is not included\n";
492 
493  if (a.right_included ())
494  os << "right boundary is included\n";
495  else
496  os << "right boundary is not included\n";
497 
498  os << "\n";
499 
500  os << a.Alpha << ' ' << a.Beta << "\n\n"
501  << a.r << "\n\n"
502  << a.q << "\n\n"
503  << a.A << "\n"
504  << a.B << "\n";
505 
506  return os;
507 }
static bool jcobi(octave_idx_type n, octave_idx_type n0, octave_idx_type n1, double alpha, double beta, double *dif1, double *dif2, double *dif3, double *root)
Definition: CollocWt.cc:150
std::ostream & operator<<(std::ostream &os, const CollocWt &a)
Definition: CollocWt.cc:486
static void dfopr(octave_idx_type n, octave_idx_type n0, octave_idx_type n1, octave_idx_type i, octave_idx_type id, double *dif1, double *dif2, double *dif3, double *root, double *vect)
Definition: CollocWt.cc:312
static void dif(octave_idx_type nt, double *root, double *dif1, double *dif2, double *dif3)
Definition: CollocWt.cc:62
const T * fortran_vec(void) const
Size of the specified dimension.
Definition: Array.h:583
double Alpha
Definition: CollocWt.h:179
Matrix A
Definition: CollocWt.h:185
octave_idx_type inc_left
Definition: CollocWt.h:173
Matrix B
Definition: CollocWt.h:186
octave_idx_type right_included(void) const
Definition: CollocWt.h:148
double lb
Definition: CollocWt.h:176
void error(const char *msg)
Definition: CollocWt.cc:382
double rb
Definition: CollocWt.h:177
octave_idx_type n
Definition: CollocWt.h:171
bool initialized
Definition: CollocWt.h:188
octave_idx_type left_included(void) const
Definition: CollocWt.h:147
void init(void)
Definition: CollocWt.cc:410
CollocWt & set_left(double val)
Definition: CollocWt.cc:388
ColumnVector r
Definition: CollocWt.h:182
double Beta
Definition: CollocWt.h:180
ColumnVector q
Definition: CollocWt.h:183
octave_idx_type inc_right
Definition: CollocWt.h:174
CollocWt & set_right(double val)
Definition: CollocWt.cc:399
void resize(octave_idx_type n, const double &rfv=0)
Definition: dColVector.h:109
void resize(octave_idx_type nr, octave_idx_type nc, double rfv=0)
Definition: dMatrix.h:151
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:95
F77_RET_T const F77_DBLE * x
octave_idx_type n
Definition: mx-inlines.cc:753
bool isnan(bool)
Definition: lo-mappers.h:178
static T abs(T x)
Definition: pr-output.cc:1678