GNU Octave  8.1.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-2023 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 
49 
50 // Compute the first three derivatives of the node polynomial.
51 //
52 // n0 (alpha,beta) n1
53 // p (x) = (x) * p (x) * (1 - x)
54 // nt n
55 //
56 // at the interpolation points. Each of the parameters n0 and n1
57 // may be given the value 0 or 1. The total number of points
58 // nt = n + n0 + n1
59 //
60 // The values of root must be known before a call to dif is possible.
61 // They may be computed using jcobi.
62 
63 static void dif (octave_idx_type nt, double *root, double *dif1,
64  double *dif2, double *dif3)
65 {
66  // Evaluate derivatives of node polynomial using recursion formulas.
67 
68  for (octave_idx_type i = 0; i < nt; i++)
69  {
70  double x = root[i];
71 
72  dif1[i] = 1.0;
73  dif2[i] = 0.0;
74  dif3[i] = 0.0;
75 
76  for (octave_idx_type j = 0; j < nt; j++)
77  {
78  if (j != i)
79  {
80  double y = x - root[j];
81 
82  dif3[i] = y * dif3[i] + 3.0 * dif2[i];
83  dif2[i] = y * dif2[i] + 2.0 * dif1[i];
84  dif1[i] = y * dif1[i];
85  }
86  }
87  }
88 }
89 
90 // Compute the zeros of the Jacobi polynomial.
91 //
92 // (alpha,beta)
93 // p (x)
94 // n
95 //
96 // Use dif to compute the derivatives of the node
97 // polynomial
98 //
99 // n0 (alpha,beta) n1
100 // p (x) = (x) * p (x) * (1 - x)
101 // nt n
102 //
103 // at the interpolation points.
104 //
105 // See Villadsen and Michelsen, pages 131-132 and 418.
106 //
107 // Input parameters:
108 //
109 // nd : the dimension of the vectors dif1, dif2, dif3, and root
110 //
111 // n : the degree of the jacobi polynomial, (i.e., the number
112 // of interior interpolation points)
113 //
114 // n0 : determines whether x = 0 is included as an
115 // interpolation point
116 //
117 // n0 = 0 ==> x = 0 is not included
118 // n0 = 1 ==> x = 0 is included
119 //
120 // n1 : determines whether x = 1 is included as an
121 // interpolation point
122 //
123 // n1 = 0 ==> x = 1 is not included
124 // n1 = 1 ==> x = 1 is included
125 //
126 // alpha : the value of alpha in the description of the jacobi
127 // polynomial
128 //
129 // beta : the value of beta in the description of the jacobi
130 // polynomial
131 //
132 // For a more complete explanation of alpha an beta, see Villadsen
133 // and Michelsen, pages 57 to 59.
134 //
135 // Output parameters:
136 //
137 // root : one dimensional vector containing on exit the
138 // n + n0 + n1 zeros of the node polynomial used in the
139 // interpolation routine
140 //
141 // dif1 : one dimensional vector containing the first derivative
142 // of the node polynomial at the zeros
143 //
144 // dif2 : one dimensional vector containing the second derivative
145 // of the node polynomial at the zeros
146 //
147 // dif3 : one dimensional vector containing the third derivative
148 // of the node polynomial at the zeros
149 
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 || 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 
312  octave_idx_type i, octave_idx_type id, double *dif1,
313  double *dif2, double *dif3, double *root, double *vect)
314 {
315  assert (n0 == 0 || n0 == 1);
316  assert (n1 == 0 || n1 == 1);
317 
318  octave_idx_type nt = n + n0 + n1;
319 
320  assert (nt >= 1);
321 
322  assert (id == 1 || id == 2 || id == 3);
323 
324  if (id != 3)
325  assert (i >= 0 && i < nt);
326 
327  // Evaluate discretization matrices and Gaussian quadrature weights.
328  // Quadrature weights are normalized to sum to one.
329 
330  if (id != 3)
331  {
332  for (octave_idx_type j = 0; j < nt; j++)
333  {
334  if (j == i)
335  {
336  if (id == 1)
337  vect[i] = dif2[i] / dif1[i] / 2.0;
338  else
339  vect[i] = dif3[i] / dif1[i] / 3.0;
340  }
341  else
342  {
343  double y = root[i] - root[j];
344 
345  vect[j] = dif1[i] / dif1[j] / y;
346 
347  if (id == 2)
348  vect[j] = vect[j] * (dif2[i] / dif1[i] - 2.0 / y);
349  }
350  }
351  }
352  else
353  {
354  double y = 0.0;
355 
356  for (octave_idx_type j = 0; j < nt; j++)
357  {
358  double x = root[j];
359 
360  double ax = x * (1.0 - x);
361 
362  if (n0 == 0)
363  ax = ax / x / x;
364 
365  if (n1 == 0)
366  ax = ax / (1.0 - x) / (1.0 - x);
367 
368  vect[j] = ax / (dif1[j] * dif1[j]);
369 
370  y += vect[j];
371  }
372 
373  for (octave_idx_type j = 0; j < nt; j++)
374  vect[j] = vect[j] / y;
375  }
376 }
377 
378 // Error handling.
379 
380 void CollocWt::error (const char *msg)
381 {
382  (*current_liboctave_error_handler) ("CollocWt: fatal error '%s'", msg);
383 }
384 
386 {
387  if (val >= m_rb)
388  error ("CollocWt: left bound greater than right bound");
389 
390  m_lb = val;
391  m_initialized = 0;
392  return *this;
393 }
394 
396 {
397  if (val <= m_lb)
398  error ("CollocWt: right bound less than left bound");
399 
400  m_rb = val;
401  m_initialized = 0;
402  return *this;
403 }
404 
405 void CollocWt::init (void)
406 {
407  // Check for possible errors.
408 
409  double wid = m_rb - m_lb;
410  if (wid <= 0.0)
411  {
412  error ("CollocWt: width less than or equal to zero");
413  }
414 
416 
417  if (nt < 0)
418  error ("CollocWt: total number of collocation points less than zero");
419  else if (nt == 0)
420  return;
421 
422  Array<double> dif1 (dim_vector (nt, 1));
423  double *pdif1 = dif1.fortran_vec ();
424 
425  Array<double> dif2 (dim_vector (nt, 1));
426  double *pdif2 = dif2.fortran_vec ();
427 
428  Array<double> dif3 (dim_vector (nt, 1));
429  double *pdif3 = dif3.fortran_vec ();
430 
431  Array<double> vect (dim_vector (nt, 1));
432  double *pvect = vect.fortran_vec ();
433 
434  m_r.resize (nt, 1);
435  m_q.resize (nt, 1);
436  m_A.resize (nt, nt);
437  m_B.resize (nt, nt);
438 
439  double *pr = m_r.fortran_vec ();
440 
441  // Compute roots.
442 
443  if (! jcobi (m_n, m_inc_left, m_inc_right, m_alpha, m_beta, pdif1,
444  pdif2, pdif3, pr))
445  error ("jcobi: newton iteration failed");
446 
447  octave_idx_type id;
448 
449  // First derivative weights.
450 
451  id = 1;
452  for (octave_idx_type i = 0; i < nt; i++)
453  {
454  dfopr (m_n, m_inc_left, m_inc_right, i, id, pdif1, pdif2, pdif3,
455  pr, pvect);
456 
457  for (octave_idx_type j = 0; j < nt; j++)
458  m_A(i, j) = vect(j);
459  }
460 
461  // Second derivative weights.
462 
463  id = 2;
464  for (octave_idx_type i = 0; i < nt; i++)
465  {
466  dfopr (m_n, m_inc_left, m_inc_right, i, id, pdif1, pdif2, pdif3,
467  pr, pvect);
468 
469  for (octave_idx_type j = 0; j < nt; j++)
470  m_B(i, j) = vect(j);
471  }
472 
473  // Gaussian quadrature weights.
474 
475  id = 3;
476  double *pq = m_q.fortran_vec ();
477  dfopr (m_n, m_inc_left, m_inc_right, id, id, pdif1, pdif2, pdif3, pr, pq);
478 
479  m_initialized = 1;
480 }
481 
482 std::ostream& operator << (std::ostream& os, const CollocWt& a)
483 {
484  if (a.left_included ())
485  os << "left boundary is included\n";
486  else
487  os << "left boundary is not included\n";
488 
489  if (a.right_included ())
490  os << "right boundary is included\n";
491  else
492  os << "right boundary is not included\n";
493 
494  os << "\n";
495 
496  os << a.m_alpha << ' ' << a.m_beta << "\n\n"
497  << a.m_r << "\n\n"
498  << a.m_q << "\n\n"
499  << a.m_A << "\n"
500  << a.m_B << "\n";
501 
502  return os;
503 }
504 
OCTAVE_END_NAMESPACE(octave)
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:482
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:311
static void dif(octave_idx_type nt, double *root, double *dif1, double *dif2, double *dif3)
Definition: CollocWt.cc:63
OCTARRAY_API T * fortran_vec(void)
Size of the specified dimension.
Definition: Array-base.cc:1766
ColumnVector m_q
Definition: CollocWt.h:198
Matrix m_A
Definition: CollocWt.h:200
ColumnVector m_r
Definition: CollocWt.h:197
Matrix m_B
Definition: CollocWt.h:201
double m_rb
Definition: CollocWt.h:192
octave_idx_type m_inc_left
Definition: CollocWt.h:188
octave_idx_type right_included(void) const
Definition: CollocWt.h:138
void error(const char *msg)
Definition: CollocWt.cc:380
double m_alpha
Definition: CollocWt.h:194
octave_idx_type m_inc_right
Definition: CollocWt.h:189
bool m_initialized
Definition: CollocWt.h:203
octave_idx_type left_included(void) const
Definition: CollocWt.h:137
double m_lb
Definition: CollocWt.h:191
double m_beta
Definition: CollocWt.h:195
void init(void)
Definition: CollocWt.cc:405
CollocWt & set_left(double val)
Definition: CollocWt.cc:385
octave_idx_type m_n
Definition: CollocWt.h:186
CollocWt & set_right(double val)
Definition: CollocWt.cc:395
void resize(octave_idx_type n, const double &rfv=0)
Definition: dColVector.h:112
void resize(octave_idx_type nr, octave_idx_type nc, double rfv=0)
Definition: dMatrix.h:158
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
bool isnan(bool)
Definition: lo-mappers.h:178
F77_RET_T const F77_DBLE * x
octave_idx_type n
Definition: mx-inlines.cc:753
static T abs(T x)
Definition: pr-output.cc:1678