diff libinterp/corefcn/stream-euler.cc @ 27759:992e702ef0d7

Add stream* functions (patch #9859). * libinterp/corefcn/stream-euler.cc: New file. * libinterp/corefcn/module.mk: Add new file to list of source files. * stream2.m, stream3.m, streamline.m, streamtube.m: New functions. * plot.txi: Add documentation of new functions to manual. * __unimplemented__.m: Remove implemented functions from list. * NEWS: Announce new functions.
author Markus Meisinger <chloros2@gmx.de>
date Sat, 23 Nov 2019 11:47:16 +0100
parents
children afbaad39d25c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/stream-euler.cc	Sat Nov 23 11:47:16 2019 +0100
@@ -0,0 +1,530 @@
+/*
+
+Copyright (C) 2019 Markus Meisinger
+
+This file is part of Octave.
+
+Octave is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Octave is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<https://www.gnu.org/licenses/>.
+
+*/
+
+/*
+
+ References:
+
+ @article{
+    title = {Particle Tracing Algorithms for 3D Curvilinear Grids},
+    year = {2000},
+    author = {Nielson, Gregory and Uller, H. and Sadarjoen, I. and Walsum, Theo and Hin, Andrea and Post, Frits}
+ }
+
+ @article{
+    title = {Sources of error in the graphical analysis of CFD results},
+    publisher = {Journal of Scientific Computing},
+    year = {1988},
+    volume = {3},
+    number = {2},
+    pages = {149--164},
+    author = {Buning, Pieter G.},
+ }
+
+*/
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include "defun.h"
+#include "error.h"
+#include "ovl.h"
+
+// Coordinates of a point in C-Space (unit square mesh)
+
+typedef struct
+{
+  double x, y;
+} Vector2;
+
+// The integer- and the fractional value from a point in C-Space.
+// Equivalent to the cell index the point is located in and the local
+// coordinates of the point in the cell.
+
+typedef struct
+{
+  double fcx, fcy;
+  signed long idx, idy;
+} Cell2;
+
+typedef struct
+{
+  double x, y, z;
+} Vector3;
+
+typedef struct
+{
+  double fcx, fcy, fcz;
+  signed long idx, idy, idz;
+} Cell3;
+
+static inline void
+number_to_fractional (signed long *id, double *fc, const double u)
+{
+  *id = floor (u);
+  *fc = u - *id;
+}
+
+static inline octave_idx_type
+handle_border_index (const octave_idx_type id, const octave_idx_type N)
+{
+  return (id < N - 1 ? id : N - 2);
+}
+
+static inline void
+handle_border (octave_idx_type *id2, double *fc2, const octave_idx_type id1,
+               const double fc1, const octave_idx_type N)
+{
+  if (id1 < N - 1)
+    {
+      *id2 = id1;
+      *fc2 = fc1;
+    }
+  else
+    {
+      *id2 = N - 2;
+      *fc2 = 1.0;
+    }
+}
+
+static inline double
+bilinear (const double u11, const double u21, const double u12,
+          const double u22, const double x, const double y)
+{
+  return (u11 * (1-x) * (1-y) +
+          u21 * x * (1-y) +
+          u12 * (1-x) * y +
+          u22 * x * y);
+}
+
+static inline Cell2
+vector_to_cell2d (const Vector2 X)
+{
+  Cell2 Z;
+
+  number_to_fractional (&Z.idx, &Z.fcx, X.x);
+  number_to_fractional (&Z.idy, &Z.fcy, X.y);
+
+  return (Z);
+}
+
+static inline bool
+is_in_definition_set2d (const Cell2 X, const octave_idx_type cols,
+                        const octave_idx_type rows)
+{
+ return ( (((X.idx >= 0) && (X.idx < cols-1)) ||
+           ((X.idx == cols-1) && (X.fcx == 0.0))) &&
+          (((X.idy >= 0) && (X.idy < rows-1)) ||
+           ((X.idy == rows-1) && (X.fcy == 0.0))) );
+}
+
+static inline Vector2
+add2d (const Cell2 X, const Vector2 Y)
+{
+  Vector2 Z = {X.idx + X.fcx + Y.x,
+               X.idy + X.fcy + Y.y};
+
+  return (Z);
+}
+
+static inline Vector2
+vector_interpolation2d (const Cell2 X, const Matrix& u, const Matrix& v,
+                        const octave_idx_type cols, const octave_idx_type rows)
+{
+  Vector2 V;
+  double fcx, fcy;
+  octave_idx_type idx, idy;
+
+  handle_border (&idx, &fcx, X.idx, X.fcx, cols);
+  handle_border (&idy, &fcy, X.idy, X.fcy, rows);
+
+  V.x = bilinear (u(idy, idx), u(idy, idx+1), u(idy+1, idx),
+                  u(idy+1, idx+1), fcx, fcy);
+  V.y = bilinear (v(idy, idx), v(idy, idx+1), v(idy+1, idx),
+                  v(idy+1, idx+1), fcx, fcy);
+
+  return (V);
+}
+
+// Apply the Jacobian matrix on the vector V.
+// The step vector length is set to h.
+
+static inline 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)
+{
+  Vector2 S;
+
+  const octave_idx_type idx = handle_border_index (X.idx, cols);
+  const octave_idx_type idy = handle_border_index (X.idy, rows);
+
+  const double x = V.x * tx(idx);
+  const double y = V.y * ty(idy);
+  const double n = 1.0 / sqrt (x*x + y*y);
+  S.x = h * n * x;
+  S.y = h * n * y;
+
+  return (S);
+}
+
+static inline bool
+is_singular2d (const Vector2 V)
+{
+  return ((octave::math::isnan (V.x) || octave::math::isnan (V.y)) ||
+          ((V.x == 0) && (V.y == 0)));
+}
+
+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)
+{
+  Vector2 V0, V1, S0, X1, Xnxt, S1;
+  const Vector2 X0 = {zeta, xi};
+  Cell2 X0f, X1f;
+
+  octave_idx_type i = 0;
+
+  buffer(i, 0) = X0.x;
+  buffer(i, 1) = X0.y;
+
+  X0f = vector_to_cell2d (X0);
+  while (true)
+    {
+      if (! is_in_definition_set2d (X0f, cols, rows))
+        break;
+
+      V0 = vector_interpolation2d (X0f, u, v, cols, rows);
+      if (is_singular2d (V0))
+        break;
+
+      S0 = calculate_step_vector2d (X0f, V0, tx, ty, cols, rows, h);
+
+      X1 = add2d (X0f, S0);
+      X1f = vector_to_cell2d (X1);
+      if (! is_in_definition_set2d (X1f, cols, rows))
+        break;
+
+      V1 = vector_interpolation2d (X1f, u, v, cols, rows);
+      if (is_singular2d (V1))
+        break;
+
+      S1 = calculate_step_vector2d (X1f, V1, tx, ty, cols, rows, h);
+
+      // Runge Kutta - Heun's Scheme
+      const Vector2 S = {0.5 * (S0.x + S1.x),
+                         0.5 * (S0.y + S1.y)};
+      Xnxt = add2d (X0f, S);
+
+      X0f = vector_to_cell2d (Xnxt);
+      if (! is_in_definition_set2d (X0f, cols, rows))
+        break;
+
+      i++;
+      buffer(i, 0) = Xnxt.x;
+      buffer(i, 1) = Xnxt.y;
+
+      if (i + 1 >= maxnverts)
+        break;
+    }
+
+  *nverts = i + 1;
+}
+
+static inline 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)
+{
+  return (u111 * (1-x) * (1-y) * (1-z) +
+          u211 * x * (1-y) * (1-z) +
+          u121 * (1-x) * y * (1-z) +
+          u221 * x * y * (1-z) +
+          u112 * (1-x) * (1-y) * z +
+          u212 * x * (1-y) * z +
+          u122 * (1-x) * y * z +
+          u222 * x * y * z);
+}
+
+static inline Cell3
+vector_to_cell3d (const Vector3 X)
+{
+  Cell3 Z;
+
+  number_to_fractional (&Z.idx, &Z.fcx, X.x);
+  number_to_fractional (&Z.idy, &Z.fcy, X.y);
+  number_to_fractional (&Z.idz, &Z.fcz, X.z);
+
+  return (Z);
+}
+
+static inline bool
+is_in_definition_set3d (const Cell3 X, const octave_idx_type nx,
+                        const octave_idx_type ny, const octave_idx_type nz)
+{
+  return ( (((X.idx >= 0) && (X.idx < nx-1)) ||
+            ((X.idx == nx-1) && (X.fcx == 0.0))) &&
+           (((X.idy >= 0) && (X.idy < ny-1)) ||
+            ((X.idy == ny-1) && (X.fcy == 0.0))) &&
+           (((X.idz >= 0) && (X.idz < nz-1)) ||
+            ((X.idz == nz-1) && (X.fcz == 0.0))) );
+}
+
+static inline Vector3
+add3d (const Cell3 X, const Vector3 Y)
+{
+  Vector3 Z = {X.idx + X.fcx + Y.x,
+               X.idy + X.fcy + Y.y,
+               X.idz + X.fcz + Y.z};
+
+  return (Z);
+}
+
+static inline 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)
+{
+  Vector3 V;
+  double fcx, fcy, fcz;
+  octave_idx_type idx, idy, idz;
+
+  handle_border (&idx, &fcx, X.idx, X.fcx, nx);
+  handle_border (&idy, &fcy, X.idy, X.fcy, ny);
+  handle_border (&idz, &fcz, X.idz, X.fcz, nz);
+
+  V.x = trilinear (u(idy, idx, idz), u(idy, idx+1, idz),
+                   u(idy+1, idx, idz), u(idy+1, idx+1, idz),
+                   u(idy, idx, idz+1), u(idy, idx+1, idz+1),
+                   u(idy+1, idx, idz+1), u(idy+1, idx+1, idz+1),
+                   fcx, fcy, fcz);
+  V.y = trilinear (v(idy, idx, idz), v(idy, idx+1, idz),
+                   v(idy+1, idx, idz), v(idy+1, idx+1, idz),
+                   v(idy, idx, idz+1), v(idy, idx+1, idz+1),
+                   v(idy+1, idx, idz+1), v(idy+1, idx+1, idz+1),
+                   fcx, fcy, fcz);
+  V.z = trilinear (w(idy, idx, idz), w(idy, idx+1, idz),
+                   w(idy+1, idx, idz), w(idy+1, idx+1, idz),
+                   w(idy, idx, idz+1), w(idy, idx+1, idz+1),
+                   w(idy+1, idx, idz+1), w(idy+1, idx+1, idz+1),
+                   fcx, fcy, fcz);
+
+  return (V);
+}
+
+static inline 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)
+{
+  Vector3 S;
+
+  const octave_idx_type idx = handle_border_index (X.idx, nx);
+  const octave_idx_type idy = handle_border_index (X.idy, ny);
+  const octave_idx_type idz = handle_border_index (X.idz, nz);
+
+  const double x = V.x * tx(idx);
+  const double y = V.y * ty(idy);
+  const double z = V.z * tz(idz);
+  const double n = 1.0 / sqrt (x*x + y*y + z*z);
+  S.x = h * n * x;
+  S.y = h * n * y;
+  S.z = h * n * z;
+
+  return (S);
+}
+
+static inline bool
+is_singular3d (const Vector3 V)
+{
+  return ((octave::math::isnan (V.x) || octave::math::isnan (V.y) ||
+           octave::math::isnan (V.z)) ||
+          ((V.x == 0) && (V.y == 0) && (V.z == 0)));
+}
+
+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)
+{
+  Vector3 V0, V1, S0, X1, Xnxt, S1;
+  const Vector3 X0 = {zeta, xi, rho};
+  Cell3 X0f, X1f;
+
+  octave_idx_type i = 0;
+  buffer(i, 0) = X0.x;
+  buffer(i, 1) = X0.y;
+  buffer(i, 2) = X0.z;
+
+  X0f = vector_to_cell3d (X0);
+  while (true)
+    {
+      if (! is_in_definition_set3d (X0f, nx, ny, nz))
+        break;
+
+      V0 = vector_interpolation3d (X0f, u, v, w, nx, ny, nz);
+      if (is_singular3d (V0))
+        break;
+
+      S0 = calculate_step_vector3d (X0f, V0, tx, ty, tz, nx, ny, nz, h);
+
+      X1 = add3d (X0f, S0);
+
+      X1f = vector_to_cell3d (X1);
+      if (! is_in_definition_set3d (X1f, nx, ny, nz))
+        break;
+
+      V1 = vector_interpolation3d (X1f, u, v, w, nx, ny, nz);
+      if (is_singular3d (V1))
+        break;
+
+      S1 = calculate_step_vector3d (X1f, V1, tx, ty, tz, nx, ny, nz, h);
+
+      // Runge Kutta - Heun's Scheme
+      const Vector3 S = {0.5 * (S0.x + S1.x),
+                         0.5 * (S0.y + S1.y),
+                         0.5 * (S0.z + S1.z)};
+      Xnxt = add3d (X0f, S);
+
+      X0f = vector_to_cell3d (Xnxt);
+      if (! is_in_definition_set3d (X0f, nx, ny, nz))
+        break;
+
+      i++;
+      buffer(i, 0) = Xnxt.x;
+      buffer(i, 1) = Xnxt.y;
+      buffer(i, 2) = Xnxt.z;
+
+      if (i + 1 >= maxnverts)
+        break;
+
+    }
+
+  *nverts = i + 1;
+}
+
+static octave_value
+streameuler2d_internal (const octave_value_list& args)
+{
+
+  const int nargin = args.length ();
+  if (nargin != 8)
+    print_usage ();
+
+  const Matrix U = args(0).matrix_value ();
+  const Matrix V = args(1).matrix_value ();
+  const RowVector TX = args(2).row_vector_value ();
+  const RowVector TY = args(3).row_vector_value ();
+  const double zeta = args(4).double_value ();
+  const double xi = args(5).double_value ();
+  const double h = args(6).double_value ();
+  const octave_idx_type maxnverts = args(7).idx_type_value ();
+
+  const octave_idx_type rows = U.rows ();
+  const octave_idx_type cols = U.columns ();
+
+  octave_idx_type nverts;
+  Matrix buffer (maxnverts, 2);
+
+  euler2d (cols, rows, U, V, TX, TY, zeta, xi, h, maxnverts,
+           buffer, &nverts);
+
+  Matrix xy = buffer.extract (0, 0, nverts-1, 1);
+
+  return octave_value (xy);
+}
+
+static octave_value
+streameuler3d_internal (const octave_value_list& args, const char *fcn)
+{
+
+  const int nargin = args.length ();
+  if (nargin != 11)
+    print_usage ();
+
+  const NDArray U = args(0).array_value ();
+  const NDArray V = args(1).array_value ();
+  const NDArray W = args(2).array_value ();
+  const RowVector TX = args(3).row_vector_value ();
+  const RowVector TY = args(4).row_vector_value ();
+  const RowVector TZ = args(5).row_vector_value ();
+  const double zeta = args(6).double_value ();
+  const double xi = args(7).double_value ();
+  const double rho = args(8).double_value ();
+  const double h = args(9).double_value ();
+  const octave_idx_type maxnverts = args(10).idx_type_value ();
+
+  const dim_vector dims = args(0).dims ();
+  const int ndims = dims.ndims ();
+  if (ndims != 3)
+    error ("%s: dimension must be 3", fcn);
+
+  octave_idx_type nverts;
+  Matrix buffer (maxnverts, 3);
+
+  euler3d (dims(1), dims(0), dims(2), U, V, W, TX, TY, TZ, zeta, xi, rho,
+           h, maxnverts, buffer, &nverts);
+
+  Matrix xyz = buffer.extract (0, 0, nverts-1, 2);
+
+  return octave_value (xyz);
+}
+
+DEFUN (streameuler2d, args, ,
+       doc: /* -*- texinfo -*-
+@deftypefn  {} {} streameuler2d (@var{U}, @var{V}, @var{TX}, @var{TY}, @var{ZETA}, @var{XI}, @var{H}, @var{MAXNVERTS})
+Calculates the streamline in a vector field [@var{U}, @var{V}] starting from a
+seed point at position [@var{ZETA}, @var{XI}]. The integrator used is
+Heun's Scheme. The step size can be controlled by @var{H}. The Jacobian
+matrix can be defined for each grid cell by [@var{TX}, @var{TY}].
+
+@seealso{streamline, stream2, stream3, streameuler3d}
+@end deftypefn */)
+{
+  return streameuler2d_internal (args);
+}
+
+DEFUN (streameuler3d, args, ,
+       doc: /* -*- texinfo -*-
+@deftypefn  {} {} streameuler3d (@var{U}, @var{V}, @var{W}, @var{TX}, @var{TY}, @var{TZ}, @var{ZETA}, @var{XI}, @var{RHO}, @var{H}, @var{MAXNVERTS})
+Calculates the streamline in a vector field [@var{U}, @var{V}, @var{W}] starting
+from a seed point at position [@var{ZETA}, @var{XI}, @var{RHO}]. The integrator
+used is Heun's Scheme. The step size can be controlled by @var{H}. The Jacobian
+matrix can be defined for each grid cell by [@var{TX}, @var{TY}, @var{TZ}].
+
+@seealso{streamline, stream2, stream3, streameuler2d}
+@end deftypefn */)
+{
+  return streameuler3d_internal (args, "streameuler3d");
+}
+