GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
stream-euler.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2019-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 /*
27 
28  References:
29 
30  @article{
31  title = {Particle Tracing Algorithms for 3D Curvilinear Grids},
32  year = {2000},
33  author = {Nielson, Gregory and Uller, H. and Sadarjoen, I. and Walsum, Theo and Hin, Andrea and Post, Frits}
34  }
35 
36  @article{
37  title = {Sources of error in the graphical analysis of CFD results},
38  publisher = {Journal of Scientific Computing},
39  year = {1988},
40  volume = {3},
41  number = {2},
42  pages = {149--164},
43  author = {Buning, Pieter G.},
44  }
45 
46 */
47 
48 #if defined (HAVE_CONFIG_H)
49 # include "config.h"
50 #endif
51 
52 #include "defun.h"
53 #include "error.h"
54 #include "ovl.h"
55 
56 // Coordinates of a point in C-Space (unit square mesh)
57 
58 typedef struct
59 {
60  double x, y;
61 } Vector2;
62 
63 // The integer value and the fractional value from a point in C-Space.
64 // Equivalent to the cell index the point is located in and the local
65 // coordinates of the point in the cell.
66 
67 typedef struct
68 {
69  double fcx, fcy;
70  signed long idx, idy;
71 } Cell2;
72 
73 typedef struct
74 {
75  double x, y, z;
76 } Vector3;
77 
78 typedef struct
79 {
80  double fcx, fcy, fcz;
81  signed long idx, idy, idz;
82 } Cell3;
83 
84 static inline void
85 number_to_fractional (signed long *id, double *fc, const double u)
86 {
87  *id = floor (u);
88  *fc = u - *id;
89 }
90 
91 static inline octave_idx_type
93 {
94  return (id < N - 1 ? id : N - 2);
95 }
96 
97 static inline void
98 handle_border (octave_idx_type *id2, double *fc2, const octave_idx_type id1,
99  const double fc1, const octave_idx_type N)
100 {
101  if (id1 < N - 1)
102  {
103  *id2 = id1;
104  *fc2 = fc1;
105  }
106  else
107  {
108  *id2 = N - 2;
109  *fc2 = 1.0;
110  }
111 }
112 
113 static inline double
114 bilinear (const double u11, const double u21, const double u12,
115  const double u22, const double x, const double y)
116 {
117  return (u11 * (1-x) * (1-y) +
118  u21 * x * (1-y) +
119  u12 * (1-x) * y +
120  u22 * x * y);
121 }
122 
123 static inline Cell2
125 {
126  Cell2 Z;
127 
128  number_to_fractional (&Z.idx, &Z.fcx, X.x);
129  number_to_fractional (&Z.idy, &Z.fcy, X.y);
130 
131  return (Z);
132 }
133 
134 static inline bool
136  const octave_idx_type rows)
137 {
138  return ( (((X.idx >= 0) && (X.idx < cols-1)) ||
139  ((X.idx == cols-1) && (X.fcx == 0.0))) &&
140  (((X.idy >= 0) && (X.idy < rows-1)) ||
141  ((X.idy == rows-1) && (X.fcy == 0.0))) );
142 }
143 
144 static inline Vector2
145 add2d (const Cell2 X, const Vector2 Y)
146 {
147  Vector2 Z = {X.idx + X.fcx + Y.x,
148  X.idy + X.fcy + Y.y};
149 
150  return (Z);
151 }
152 
153 static inline Vector2
154 vector_interpolation2d (const Cell2 X, const Matrix& u, const Matrix& v,
155  const octave_idx_type cols, const octave_idx_type rows)
156 {
157  Vector2 V;
158  double fcx, fcy;
159  octave_idx_type idx, idy;
160 
161  handle_border (&idx, &fcx, X.idx, X.fcx, cols);
162  handle_border (&idy, &fcy, X.idy, X.fcy, rows);
163 
164  V.x = bilinear (u(idy, idx), u(idy, idx+1), u(idy+1, idx),
165  u(idy+1, idx+1), fcx, fcy);
166  V.y = bilinear (v(idy, idx), v(idy, idx+1), v(idy+1, idx),
167  v(idy+1, idx+1), fcx, fcy);
168 
169  return (V);
170 }
171 
172 // Apply the Jacobian matrix on the vector V.
173 // The step vector length is set to h.
174 
175 static inline Vector2
177  const RowVector& tx, const RowVector& ty,
178  const octave_idx_type cols, const octave_idx_type rows,
179  const double h)
180 {
181  Vector2 S;
182 
183  const octave_idx_type idx = handle_border_index (X.idx, cols);
184  const octave_idx_type idy = handle_border_index (X.idy, rows);
185 
186  const double x = V.x * tx(idx);
187  const double y = V.y * ty(idy);
188  const double n = 1.0 / sqrt (x*x + y*y);
189  S.x = h * n * x;
190  S.y = h * n * y;
191 
192  return (S);
193 }
194 
195 static inline bool
197 {
198  return ((octave::math::isnan (V.x) || octave::math::isnan (V.y)) ||
199  ((V.x == 0) && (V.y == 0)));
200 }
201 
202 static void
203 euler2d (const octave_idx_type cols, const octave_idx_type rows,
204  const Matrix& u, const Matrix& v,
205  const RowVector& tx, const RowVector& ty,
206  const double zeta, const double xi,
207  const double h, const octave_idx_type maxnverts,
208  Matrix& buffer, octave_idx_type *nverts)
209 {
210  Vector2 V0, V1, S0, X1, Xnxt, S1;
211  const Vector2 X0 = {zeta, xi};
212  Cell2 X0f, X1f;
213 
214  octave_idx_type i = 0;
215 
216  buffer(i, 0) = X0.x;
217  buffer(i, 1) = X0.y;
218 
219  X0f = vector_to_cell2d (X0);
220  while (true)
221  {
222  if (! is_in_definition_set2d (X0f, cols, rows))
223  break;
224 
225  V0 = vector_interpolation2d (X0f, u, v, cols, rows);
226  if (is_singular2d (V0))
227  break;
228 
229  S0 = calculate_step_vector2d (X0f, V0, tx, ty, cols, rows, h);
230 
231  X1 = add2d (X0f, S0);
232  X1f = vector_to_cell2d (X1);
233  if (! is_in_definition_set2d (X1f, cols, rows))
234  break;
235 
236  V1 = vector_interpolation2d (X1f, u, v, cols, rows);
237  if (is_singular2d (V1))
238  break;
239 
240  S1 = calculate_step_vector2d (X1f, V1, tx, ty, cols, rows, h);
241 
242  // Runge Kutta - Heun's Scheme
243  const Vector2 S = {0.5 * (S0.x + S1.x),
244  0.5 * (S0.y + S1.y)};
245  Xnxt = add2d (X0f, S);
246 
247  X0f = vector_to_cell2d (Xnxt);
248  if (! is_in_definition_set2d (X0f, cols, rows))
249  break;
250 
251  i++;
252  buffer(i, 0) = Xnxt.x;
253  buffer(i, 1) = Xnxt.y;
254 
255  if (i + 1 >= maxnverts)
256  break;
257  }
258 
259  *nverts = i + 1;
260 }
261 
262 static inline double
263 trilinear (const double u111, const double u211, const double u121,
264  const double u221, const double u112, const double u212,
265  const double u122, const double u222,
266  const double x, const double y, const double z)
267 {
268  return (u111 * (1-x) * (1-y) * (1-z) +
269  u211 * x * (1-y) * (1-z) +
270  u121 * (1-x) * y * (1-z) +
271  u221 * x * y * (1-z) +
272  u112 * (1-x) * (1-y) * z +
273  u212 * x * (1-y) * z +
274  u122 * (1-x) * y * z +
275  u222 * x * y * z);
276 }
277 
278 static inline Cell3
280 {
281  Cell3 Z;
282 
283  number_to_fractional (&Z.idx, &Z.fcx, X.x);
284  number_to_fractional (&Z.idy, &Z.fcy, X.y);
285  number_to_fractional (&Z.idz, &Z.fcz, X.z);
286 
287  return (Z);
288 }
289 
290 static inline bool
292  const octave_idx_type ny, const octave_idx_type nz)
293 {
294  return ( (((X.idx >= 0) && (X.idx < nx-1)) ||
295  ((X.idx == nx-1) && (X.fcx == 0.0))) &&
296  (((X.idy >= 0) && (X.idy < ny-1)) ||
297  ((X.idy == ny-1) && (X.fcy == 0.0))) &&
298  (((X.idz >= 0) && (X.idz < nz-1)) ||
299  ((X.idz == nz-1) && (X.fcz == 0.0))) );
300 }
301 
302 static inline Vector3
303 add3d (const Cell3 X, const Vector3 Y)
304 {
305  Vector3 Z = {X.idx + X.fcx + Y.x,
306  X.idy + X.fcy + Y.y,
307  X.idz + X.fcz + Y.z};
308 
309  return (Z);
310 }
311 
312 static inline Vector3
313 vector_interpolation3d (const Cell3 X, const NDArray& u, const NDArray& v,
314  const NDArray& w, const octave_idx_type nx,
315  const octave_idx_type ny, const octave_idx_type nz)
316 {
317  Vector3 V;
318  double fcx, fcy, fcz;
319  octave_idx_type idx, idy, idz;
320 
321  handle_border (&idx, &fcx, X.idx, X.fcx, nx);
322  handle_border (&idy, &fcy, X.idy, X.fcy, ny);
323  handle_border (&idz, &fcz, X.idz, X.fcz, nz);
324 
325  V.x = trilinear (u(idy, idx, idz), u(idy, idx+1, idz),
326  u(idy+1, idx, idz), u(idy+1, idx+1, idz),
327  u(idy, idx, idz+1), u(idy, idx+1, idz+1),
328  u(idy+1, idx, idz+1), u(idy+1, idx+1, idz+1),
329  fcx, fcy, fcz);
330  V.y = trilinear (v(idy, idx, idz), v(idy, idx+1, idz),
331  v(idy+1, idx, idz), v(idy+1, idx+1, idz),
332  v(idy, idx, idz+1), v(idy, idx+1, idz+1),
333  v(idy+1, idx, idz+1), v(idy+1, idx+1, idz+1),
334  fcx, fcy, fcz);
335  V.z = trilinear (w(idy, idx, idz), w(idy, idx+1, idz),
336  w(idy+1, idx, idz), w(idy+1, idx+1, idz),
337  w(idy, idx, idz+1), w(idy, idx+1, idz+1),
338  w(idy+1, idx, idz+1), w(idy+1, idx+1, idz+1),
339  fcx, fcy, fcz);
340 
341  return (V);
342 }
343 
344 static inline Vector3
346  const RowVector& tx, const RowVector& ty, const RowVector& tz,
347  const octave_idx_type nx, const octave_idx_type ny,
348  const octave_idx_type nz, const double h)
349 {
350  Vector3 S;
351 
352  const octave_idx_type idx = handle_border_index (X.idx, nx);
353  const octave_idx_type idy = handle_border_index (X.idy, ny);
354  const octave_idx_type idz = handle_border_index (X.idz, nz);
355 
356  const double x = V.x * tx(idx);
357  const double y = V.y * ty(idy);
358  const double z = V.z * tz(idz);
359  const double n = 1.0 / sqrt (x*x + y*y + z*z);
360  S.x = h * n * x;
361  S.y = h * n * y;
362  S.z = h * n * z;
363 
364  return (S);
365 }
366 
367 static inline bool
369 {
370  return ((octave::math::isnan (V.x) || octave::math::isnan (V.y) ||
371  octave::math::isnan (V.z)) ||
372  ((V.x == 0) && (V.y == 0) && (V.z == 0)));
373 }
374 
375 static void
377  const NDArray& u, const NDArray& v, const NDArray& w,
378  const RowVector& tx, const RowVector& ty, const RowVector& tz,
379  const double zeta, const double xi, const double rho,
380  const double h, const octave_idx_type maxnverts,
381  Matrix& buffer, octave_idx_type *nverts)
382 {
383  Vector3 V0, V1, S0, X1, Xnxt, S1;
384  const Vector3 X0 = {zeta, xi, rho};
385  Cell3 X0f, X1f;
386 
387  octave_idx_type i = 0;
388  buffer(i, 0) = X0.x;
389  buffer(i, 1) = X0.y;
390  buffer(i, 2) = X0.z;
391 
392  X0f = vector_to_cell3d (X0);
393  while (true)
394  {
395  if (! is_in_definition_set3d (X0f, nx, ny, nz))
396  break;
397 
398  V0 = vector_interpolation3d (X0f, u, v, w, nx, ny, nz);
399  if (is_singular3d (V0))
400  break;
401 
402  S0 = calculate_step_vector3d (X0f, V0, tx, ty, tz, nx, ny, nz, h);
403 
404  X1 = add3d (X0f, S0);
405 
406  X1f = vector_to_cell3d (X1);
407  if (! is_in_definition_set3d (X1f, nx, ny, nz))
408  break;
409 
410  V1 = vector_interpolation3d (X1f, u, v, w, nx, ny, nz);
411  if (is_singular3d (V1))
412  break;
413 
414  S1 = calculate_step_vector3d (X1f, V1, tx, ty, tz, nx, ny, nz, h);
415 
416  // Runge Kutta - Heun's Scheme
417  const Vector3 S = {0.5 * (S0.x + S1.x),
418  0.5 * (S0.y + S1.y),
419  0.5 * (S0.z + S1.z)};
420  Xnxt = add3d (X0f, S);
421 
422  X0f = vector_to_cell3d (Xnxt);
423  if (! is_in_definition_set3d (X0f, nx, ny, nz))
424  break;
425 
426  i++;
427  buffer(i, 0) = Xnxt.x;
428  buffer(i, 1) = Xnxt.y;
429  buffer(i, 2) = Xnxt.z;
430 
431  if (i + 1 >= maxnverts)
432  break;
433 
434  }
435 
436  *nverts = i + 1;
437 }
438 
439 static octave_value
441 {
442 
443  const int nargin = args.length ();
444  if (nargin != 8)
445  print_usage ();
446 
447  const Matrix U = args(0).matrix_value ();
448  const Matrix V = args(1).matrix_value ();
449  const RowVector TX = args(2).row_vector_value ();
450  const RowVector TY = args(3).row_vector_value ();
451  const double zeta = args(4).double_value ();
452  const double xi = args(5).double_value ();
453  const double h = args(6).double_value ();
454  const octave_idx_type maxnverts = args(7).idx_type_value ();
455 
456  const octave_idx_type rows = U.rows ();
457  const octave_idx_type cols = U.columns ();
458 
459  octave_idx_type nverts;
460  Matrix buffer (maxnverts, 2);
461 
462  euler2d (cols, rows, U, V, TX, TY, zeta, xi, h, maxnverts,
463  buffer, &nverts);
464 
465  Matrix xy = buffer.extract (0, 0, nverts-1, 1);
466 
467  return octave_value (xy);
468 }
469 
470 static octave_value
471 streameuler3d_internal (const octave_value_list& args, const char *fcn)
472 {
473 
474  const int nargin = args.length ();
475  if (nargin != 11)
476  print_usage ();
477 
478  const NDArray U = args(0).array_value ();
479  const NDArray V = args(1).array_value ();
480  const NDArray W = args(2).array_value ();
481  const RowVector TX = args(3).row_vector_value ();
482  const RowVector TY = args(4).row_vector_value ();
483  const RowVector TZ = args(5).row_vector_value ();
484  const double zeta = args(6).double_value ();
485  const double xi = args(7).double_value ();
486  const double rho = args(8).double_value ();
487  const double h = args(9).double_value ();
488  const octave_idx_type maxnverts = args(10).idx_type_value ();
489 
490  const dim_vector dims = args(0).dims ();
491  const int ndims = dims.ndims ();
492  if (ndims != 3)
493  error ("%s: dimension must be 3", fcn);
494 
495  octave_idx_type nverts;
496  Matrix buffer (maxnverts, 3);
497 
498  euler3d (dims(1), dims(0), dims(2), U, V, W, TX, TY, TZ, zeta, xi, rho,
499  h, maxnverts, buffer, &nverts);
500 
501  Matrix xyz = buffer.extract (0, 0, nverts-1, 2);
502 
503  return octave_value (xyz);
504 }
505 
506 DEFUN (__streameuler2d__, args, ,
507  doc: /* -*- texinfo -*-
508 @deftypefn {} {} __streameuler2d__ (@var{U}, @var{V}, @var{TX}, @var{TY}, @var{ZETA}, @var{XI}, @var{H}, @var{MAXNVERTS})
509 Calculates the streamline in a vector field @code{[@var{U}, @var{V}]} starting
510 from a seed point at position @code{[@var{ZETA}, @var{XI}]}. The integrator
511 used is Heun's Scheme. The step size can be controlled by @var{H}. The
512 Jacobian matrix can be defined for each grid cell by
513 @code{[@var{TX}, @var{TY}]}.
514 
515 @seealso{streamline, stream2, stream3, __streameuler3d__}
516 @end deftypefn */)
517 {
518  return streameuler2d_internal (args);
519 }
520 
521 DEFUN (__streameuler3d__, args, ,
522  doc: /* -*- texinfo -*-
523 @deftypefn {} {} __streameuler3d__ (@var{U}, @var{V}, @var{W}, @var{TX}, @var{TY}, @var{TZ}, @var{ZETA}, @var{XI}, @var{RHO}, @var{H}, @var{MAXNVERTS})
524 Calculates the streamline in a vector field @code{[@var{U}, @var{V}, @var{W}]}
525 starting from a seed point at position
526 @code{[@var{ZETA}, @var{XI}, @var{RHO}]}. The integrator used is Heun's
527 Scheme. The step size can be controlled by @var{H}. The Jacobian matrix can
528 be defined for each grid cell by @code{[@var{TX}, @var{TY}, @var{TZ}]}.
529 
530 @seealso{streamline, stream2, stream3, __streameuler2d__}
531 @end deftypefn */)
532 {
533  return streameuler3d_internal (args, "__streameuler3d__");
534 }
535 
octave_idx_type columns(void) const
Definition: Array.h:424
octave_idx_type rows(void) const
Definition: Array.h:415
Definition: dMatrix.h:42
Matrix extract(octave_idx_type r1, octave_idx_type c1, octave_idx_type r2, octave_idx_type c2) const
Definition: dMatrix.cc:397
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:95
octave_idx_type ndims(void) const
Number of dimensions.
Definition: dim-vector.h:334
octave_idx_type length(void) const
Definition: ovl.h:113
Array< octave_value > array_value(void) const
Definition: ovl.h:90
OCTINTERP_API void print_usage(void)
Definition: defun.cc:53
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
void error(const char *fmt,...)
Definition: error.cc:968
F77_RET_T const F77_INT const F77_INT const F77_INT const F77_DBLE const F77_DBLE F77_INT F77_DBLE * V
F77_RET_T const F77_INT const F77_INT const F77_INT F77_DBLE const F77_INT F77_DBLE const F77_INT F77_DBLE const F77_INT F77_DBLE * Z
F77_RET_T const F77_INT & N
F77_RET_T const F77_DBLE * x
octave_idx_type n
Definition: mx-inlines.cc:753
std::complex< double > w(std::complex< double > z, double relerr=0)
bool isnan(bool)
Definition: lo-mappers.h:178
std::complex< T > floor(const std::complex< T > &x)
Definition: lo-mappers.h:130
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))
static const double xi[33]
Definition: quadcc.cc:66
static void handle_border(octave_idx_type *id2, double *fc2, const octave_idx_type id1, const double fc1, const octave_idx_type N)
Definition: stream-euler.cc:98
static bool is_in_definition_set2d(const Cell2 X, const octave_idx_type cols, const octave_idx_type rows)
static Cell2 vector_to_cell2d(const Vector2 X)
static octave_value streameuler3d_internal(const octave_value_list &args, const char *fcn)
static void euler3d(const octave_idx_type nx, const octave_idx_type ny, const octave_idx_type nz, const NDArray &u, const NDArray &v, const NDArray &w, const RowVector &tx, const RowVector &ty, const RowVector &tz, const double zeta, const double xi, const double rho, const double h, const octave_idx_type maxnverts, Matrix &buffer, octave_idx_type *nverts)
static Cell3 vector_to_cell3d(const Vector3 X)
static void number_to_fractional(signed long *id, double *fc, const double u)
Definition: stream-euler.cc:85
static bool is_in_definition_set3d(const Cell3 X, const octave_idx_type nx, const octave_idx_type ny, const octave_idx_type nz)
static octave_value streameuler2d_internal(const octave_value_list &args)
static octave_idx_type handle_border_index(const octave_idx_type id, const octave_idx_type N)
Definition: stream-euler.cc:92
static bool is_singular2d(const Vector2 V)
static Vector3 add3d(const Cell3 X, const Vector3 Y)
static Vector2 add2d(const Cell2 X, const Vector2 Y)
static Vector2 vector_interpolation2d(const Cell2 X, const Matrix &u, const Matrix &v, const octave_idx_type cols, const octave_idx_type rows)
static Vector3 vector_interpolation3d(const Cell3 X, const NDArray &u, const NDArray &v, const NDArray &w, const octave_idx_type nx, const octave_idx_type ny, const octave_idx_type nz)
static Vector3 calculate_step_vector3d(const Cell3 X, const Vector3 V, const RowVector &tx, const RowVector &ty, const RowVector &tz, const octave_idx_type nx, const octave_idx_type ny, const octave_idx_type nz, const double h)
static double trilinear(const double u111, const double u211, const double u121, const double u221, const double u112, const double u212, const double u122, const double u222, const double x, const double y, const double z)
static Vector2 calculate_step_vector2d(const Cell2 X, const Vector2 V, const RowVector &tx, const RowVector &ty, const octave_idx_type cols, const octave_idx_type rows, const double h)
static bool is_singular3d(const Vector3 V)
static double bilinear(const double u11, const double u21, const double u12, const double u22, const double x, const double y)
static void euler2d(const octave_idx_type cols, const octave_idx_type rows, const Matrix &u, const Matrix &v, const RowVector &tx, const RowVector &ty, const double zeta, const double xi, const double h, const octave_idx_type maxnverts, Matrix &buffer, octave_idx_type *nverts)
signed long idy
Definition: stream-euler.cc:70
double fcx
Definition: stream-euler.cc:69
signed long idx
Definition: stream-euler.cc:70
double fcy
Definition: stream-euler.cc:69
double fcx
Definition: stream-euler.cc:80
signed long idz
Definition: stream-euler.cc:81
signed long idx
Definition: stream-euler.cc:81
double fcz
Definition: stream-euler.cc:80
signed long idy
Definition: stream-euler.cc:81
double fcy
Definition: stream-euler.cc:80
double y
Definition: stream-euler.cc:60
double x
Definition: stream-euler.cc:60
double x
Definition: stream-euler.cc:75
double z
Definition: stream-euler.cc:75
double y
Definition: stream-euler.cc:75