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