GNU Octave  9.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-2024 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 
150 static bool
152  double alpha, double beta, double *dif1, double *dif2,
153  double *dif3, double *root)
154 {
155  assert (n0 == 0 || n0 == 1);
156  assert (n1 == 0 || n1 == 1);
157 
158  octave_idx_type nt = n + n0 + n1;
159 
160  assert (nt >= 1);
161 
162  // -- first evaluation of coefficients in recursion formulas.
163  // -- recursion coefficients are stored in dif1 and dif2.
164 
165  double ab = alpha + beta;
166  double ad = beta - alpha;
167  double ap = beta * alpha;
168 
169  dif1[0] = (ad / (ab + 2.0) + 1.0) / 2.0;
170  dif2[0] = 0.0;
171 
172  if (n >= 2)
173  {
174  for (octave_idx_type i = 1; i < n; i++)
175  {
176  double z1 = i;
177  double z = ab + 2 * z1;
178 
179  dif1[i] = (ab * ad / z / (z + 2.0) + 1.0) / 2.0;
180 
181  if (i == 1)
182  dif2[i] = (ab + ap + z1) / z / z / (z + 1.0);
183  else
184  {
185  z *= z;
186  double y = z1 * (ab + z1);
187  y *= (ap + y);
188  dif2[i] = y / z / (z - 1.0);
189  }
190  }
191  }
192 
193  // Root determination by Newton method with suppression of previously
194  // determined roots.
195 
196  double x = 0.0;
197 
198  for (octave_idx_type i = 0; i < n; i++)
199  {
200  bool done = false;
201 
202  int k = 0;
203 
204  while (! done)
205  {
206  double xd = 0.0;
207  double xn = 1.0;
208  double xd1 = 0.0;
209  double xn1 = 0.0;
210 
211  for (octave_idx_type j = 0; j < n; j++)
212  {
213  double xp = (dif1[j] - x) * xn - dif2[j] * xd;
214  double xp1 = (dif1[j] - x) * xn1 - dif2[j] * xd1 - xn;
215 
216  xd = xn;
217  xd1 = xn1;
218  xn = xp;
219  xn1 = xp1;
220  }
221 
222  double zc = 1.0;
223  double z = xn / xn1;
224 
225  if (i != 0)
226  {
227  for (octave_idx_type j = 1; j <= i; j++)
228  zc -= z / (x - root[j-1]);
229  }
230 
231  z /= zc;
232  x -= z;
233 
234  // Famous last words: 100 iterations should be more than
235  // enough in all cases.
236 
237  if (++k > 100 || math::isnan (z))
238  return false;
239 
240  if (std::abs (z) <= 100 * std::numeric_limits<double>::epsilon ())
241  done = true;
242  }
243 
244  root[i] = x;
245  x += std::sqrt (std::numeric_limits<double>::epsilon ());
246  }
247 
248  // Add interpolation points at x = 0 and/or x = 1.
249 
250  if (n0 != 0)
251  {
252  for (octave_idx_type i = n; i > 0; i--)
253  root[i] = root[i-1];
254 
255  root[0] = 0.0;
256  }
257 
258  if (n1 != 0)
259  root[nt-1] = 1.0;
260 
261  dif (nt, root, dif1, dif2, dif3);
262 
263  return true;
264 }
265 
266 // Compute derivative weights for orthogonal collocation.
267 //
268 // See Villadsen and Michelsen, pages 133-134, 419.
269 //
270 // Input parameters:
271 //
272 // nd : the dimension of the vectors dif1, dif2, dif3, and root
273 //
274 // n : the degree of the jacobi polynomial, (i.e., the number
275 // of interior interpolation points)
276 //
277 // n0 : determines whether x = 0 is included as an
278 // interpolation point
279 //
280 // n0 = 0 ==> x = 0 is not included
281 // n0 = 1 ==> x = 0 is included
282 //
283 // n1 : determines whether x = 1 is included as an
284 // interpolation point
285 //
286 // n1 = 0 ==> x = 1 is not included
287 // n1 = 1 ==> x = 1 is included
288 //
289 // i : the index of the node for which the weights are to be
290 // calculated
291 //
292 // id : indicator
293 //
294 // id = 1 ==> first derivative weights are computed
295 // id = 2 ==> second derivative weights are computed
296 // id = 3 ==> gaussian weights are computed (in this
297 // case, the value of i is irrelevant)
298 //
299 // Output parameters:
300 //
301 // dif1 : one dimensional vector containing the first derivative
302 // of the node polynomial at the zeros
303 //
304 // dif2 : one dimensional vector containing the second derivative
305 // of the node polynomial at the zeros
306 //
307 // dif3 : one dimensional vector containing the third derivative
308 // of the node polynomial at the zeros
309 //
310 // vect : one dimensional vector of computed weights
311 
312 static void
314  octave_idx_type i, octave_idx_type id, double *dif1,
315  double *dif2, double *dif3, double *root, double *vect)
316 {
317  assert (n0 == 0 || n0 == 1);
318  assert (n1 == 0 || n1 == 1);
319 
320  octave_idx_type nt = n + n0 + n1;
321 
322  assert (nt >= 1);
323 
324  assert (id == 1 || id == 2 || id == 3);
325 
326  if (id != 3)
327  assert (i >= 0 && i < nt);
328 
329  // Evaluate discretization matrices and Gaussian quadrature weights.
330  // Quadrature weights are normalized to sum to one.
331 
332  if (id != 3)
333  {
334  for (octave_idx_type j = 0; j < nt; j++)
335  {
336  if (j == i)
337  {
338  if (id == 1)
339  vect[i] = dif2[i] / dif1[i] / 2.0;
340  else
341  vect[i] = dif3[i] / dif1[i] / 3.0;
342  }
343  else
344  {
345  double y = root[i] - root[j];
346 
347  vect[j] = dif1[i] / dif1[j] / y;
348 
349  if (id == 2)
350  vect[j] = vect[j] * (dif2[i] / dif1[i] - 2.0 / y);
351  }
352  }
353  }
354  else
355  {
356  double y = 0.0;
357 
358  for (octave_idx_type j = 0; j < nt; j++)
359  {
360  double x = root[j];
361 
362  double ax = x * (1.0 - x);
363 
364  if (n0 == 0)
365  ax = ax / x / x;
366 
367  if (n1 == 0)
368  ax = ax / (1.0 - x) / (1.0 - x);
369 
370  vect[j] = ax / (dif1[j] * dif1[j]);
371 
372  y += vect[j];
373  }
374 
375  for (octave_idx_type j = 0; j < nt; j++)
376  vect[j] = vect[j] / y;
377  }
378 }
379 
380 // Error handling.
381 
382 void
383 CollocWt::error (const char *msg)
384 {
385  (*current_liboctave_error_handler) ("CollocWt: fatal error '%s'", msg);
386 }
387 
388 CollocWt&
389 CollocWt::set_left (double val)
390 {
391  if (val >= m_rb)
392  error ("CollocWt: left bound greater than right bound");
393 
394  m_lb = val;
395  m_initialized = 0;
396  return *this;
397 }
398 
399 CollocWt&
401 {
402  if (val <= m_lb)
403  error ("CollocWt: right bound less than left bound");
404 
405  m_rb = val;
406  m_initialized = 0;
407  return *this;
408 }
409 
410 void
412 {
413  // Check for possible errors.
414 
415  double wid = m_rb - m_lb;
416  if (wid <= 0.0)
417  {
418  error ("CollocWt: width less than or equal to zero");
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  m_r.resize (nt, 1);
441  m_q.resize (nt, 1);
442  m_A.resize (nt, nt);
443  m_B.resize (nt, nt);
444 
445  double *pr = m_r.fortran_vec ();
446 
447  // Compute roots.
448 
449  if (! jcobi (m_n, m_inc_left, m_inc_right, m_alpha, m_beta, pdif1,
450  pdif2, pdif3, pr))
451  error ("jcobi: newton iteration failed");
452 
453  octave_idx_type id;
454 
455  // First derivative weights.
456 
457  id = 1;
458  for (octave_idx_type i = 0; i < nt; i++)
459  {
460  dfopr (m_n, m_inc_left, m_inc_right, i, id, pdif1, pdif2, pdif3,
461  pr, pvect);
462 
463  for (octave_idx_type j = 0; j < nt; j++)
464  m_A(i, j) = vect(j);
465  }
466 
467  // Second derivative weights.
468 
469  id = 2;
470  for (octave_idx_type i = 0; i < nt; i++)
471  {
472  dfopr (m_n, m_inc_left, m_inc_right, i, id, pdif1, pdif2, pdif3,
473  pr, pvect);
474 
475  for (octave_idx_type j = 0; j < nt; j++)
476  m_B(i, j) = vect(j);
477  }
478 
479  // Gaussian quadrature weights.
480 
481  id = 3;
482  double *pq = m_q.fortran_vec ();
483  dfopr (m_n, m_inc_left, m_inc_right, id, id, pdif1, pdif2, pdif3, pr, pq);
484 
485  m_initialized = 1;
486 }
487 
488 std::ostream&
489 operator << (std::ostream& os, const CollocWt& a)
490 {
491  if (a.left_included ())
492  os << "left boundary is included\n";
493  else
494  os << "left boundary is not included\n";
495 
496  if (a.right_included ())
497  os << "right boundary is included\n";
498  else
499  os << "right boundary is not included\n";
500 
501  os << "\n";
502 
503  os << a.m_alpha << ' ' << a.m_beta << "\n\n"
504  << a.m_r << "\n\n"
505  << a.m_q << "\n\n"
506  << a.m_A << "\n"
507  << a.m_B << "\n";
508 
509  return os;
510 }
511 
512 OCTAVE_END_NAMESPACE(octave)
std::ostream & operator<<(std::ostream &os, const CollocWt &a)
Definition: CollocWt.cc:489
T * fortran_vec()
Size of the specified dimension.
Definition: Array-base.cc:1764
ColumnVector m_q
Definition: CollocWt.h:198
Matrix m_A
Definition: CollocWt.h:200
octave_idx_type left_included() const
Definition: CollocWt.h:137
ColumnVector m_r
Definition: CollocWt.h:197
Matrix m_B
Definition: CollocWt.h:201
void init()
Definition: CollocWt.cc:411
double m_rb
Definition: CollocWt.h:192
octave_idx_type m_inc_left
Definition: CollocWt.h:188
void error(const char *msg)
Definition: CollocWt.cc:383
double m_alpha
Definition: CollocWt.h:194
octave_idx_type m_inc_right
Definition: CollocWt.h:189
bool m_initialized
Definition: CollocWt.h:203
double m_lb
Definition: CollocWt.h:191
double m_beta
Definition: CollocWt.h:195
octave_idx_type right_included() const
Definition: CollocWt.h:138
CollocWt & set_left(double val)
Definition: CollocWt.cc:389
octave_idx_type m_n
Definition: CollocWt.h:186
CollocWt & set_right(double val)
Definition: CollocWt.cc:400
void resize(octave_idx_type n, const double &rfv=0)
Definition: dColVector.h:114
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:761