GNU Octave 7.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-2022 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
48namespace octave
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
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}
OCTARRAY_API T * fortran_vec(void)
Size of the specified dimension.
Definition: Array.cc:1744
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
ColumnVector m_r
Definition: CollocWt.h:197
octave_idx_type m_inc_left
Definition: CollocWt.h:188
double m_alpha
Definition: CollocWt.h:194
CollocWt & set_right(double val)
Definition: CollocWt.cc:395
void error(const char *msg)
Definition: CollocWt.cc:380
octave_idx_type left_included(void) const
Definition: CollocWt.h:137
octave_idx_type m_n
Definition: CollocWt.h:186
bool m_initialized
Definition: CollocWt.h:203
octave_idx_type right_included(void) const
Definition: CollocWt.h:138
ColumnVector m_q
Definition: CollocWt.h:198
CollocWt & set_left(double val)
Definition: CollocWt.cc:385
void init(void)
Definition: CollocWt.cc:405
octave_idx_type m_inc_right
Definition: CollocWt.h:189
F77_RET_T const F77_DBLE * x
bool isnan(bool)
Definition: lo-mappers.h:178
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
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 T abs(T x)
Definition: pr-output.cc:1678