| Signature | Description | Parameters |
|---|---|---|
#include <DataFrame/DataFrameStatsVisitors.h> template<typename T, typename I = unsigned long, std::size_t A = 0> struct DivergenceVisitor; // ------------------------------------- template<typename T, typename I = unsigned long, std::size_t A = 0> using div_v = DivergenceVisitor<T, I, A>; |
Divergence of a Vector Field (∇.F) Computes the divergence of an N-dimensional vector field sampled at discrete, possibly non-uniformly-spaced points. The divergence measures the net "outward flux" of the field per unit volume at each sample point:
(∇.F)(x) = ∂F1 / ∂x1 + ∂F2 / ∂x2 + … + ∂F / ∂xₙ
Each partial derivative ∂F/∂x is approximated by central finite differences at interior rows and by one-sided differences at the two boundary rows:
Interior (1 <= i <= n − 2): ∂F / ∂xₖ|i = (F[i + 1] − F[i − 1]) / (x[i + 1] − x[i − 1])
Left boundary (i = 0): ∂F / ∂xₖ|0 = (F[1] − F[0]) / (x[1] − x[0])
Right boundary (i = n−1): ∂F / ∂xₖ| = (F[n − 1] − F[n − 2]) / (x[n − 1] − x[n − 2])
The result has one double per sample row = (∇.F at that point, parallel to the input columns.INTERFACE The visitor takes two columns via single_act_visit column 1 (T) — field values column 2 (T) — spatial coordinates For a scalar 1-D field T is a plain arithmetic type (e.g. double): DivergenceVisitor df.single_act_visit result: dF/dx per row For an N-D vector field T is a fixed-size container (std::array using Vec3 = std::array DivergenceVisitor df.single_act_visit result: (dF1/dx1 + dF2/dx2 + dF3/dx3) per row This 2-column design works with all existing single_act_visit overloads and is consistent with the random_acc_cont / is_md_ convention used throughout the library (KMeansVisitor, DBSCANVisitor, etc.). DEGENERATE CASES n = 1: single row -> derivative undefined -> result is 0. Coincident coordinates (same xk on adjacent rows): partial = 0, not NaN Scalar T: reduces to ordinary derivative dF/dx
References:
Strikwerda, J.C. (2004). "Finite Difference Schemes and Partial
Differential Equations", SIAM, 2nd ed., sec. 1.1.
get_results() Per-row divergence. Length equals the input column length.
DivergenceVisitor() = default;
|
T: Column data type I: Index type A: Memory alignment boundary for vectors. Default is system default alignment |
static void test_DivergenceVisitor() { std::cout << "\nTesting DivergenceVisitor{ } ..." << std::endl; using MyDataFrame = StdDataFrame<unsigned long>; MyDataFrame df; std::vector<unsigned long> idx = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }; df.load_index(std::move(idx)); // 1-D quadratic // { std::vector<double> F = { 0.0, 1.0, 4.0, 9.0, 16.0 }; std::vector<double> c = { 0.0, 1.0, 2.0, 3.0, 4.0 }; df.load_column("F1", std::move(F), nan_policy::dont_pad_with_nans); df.load_column("c1", std::move(c), nan_policy::dont_pad_with_nans); DivergenceVisitor<double> div; df.single_act_visit<double, double>("F1", "c1", div); const auto &r = div.get_result(); assert(r.size() == 5); assert(std::abs(r[0] - 1.0) < 1e-9); // forward FD assert(std::abs(r[1] - 2.0) < 1e-9); // central assert(std::abs(r[2] - 4.0) < 1e-9); assert(std::abs(r[3] - 6.0) < 1e-9); assert(std::abs(r[4] - 7.0) < 1e-9); // backward FD } // 1-D constant // { std::vector<double> F(6, 5.0); std::vector<double> c(6); std::iota(c.begin(), c.end(), 0.0); df.load_column("F2", std::move(F), nan_policy::dont_pad_with_nans); df.load_column("c2", std::move(c), nan_policy::dont_pad_with_nans); DivergenceVisitor<double> div; df.single_act_visit<double, double>("F2", "c2", div); assert(div.get_result().size() == 6); for (const double v : div.get_result()) assert(std::abs(v) < 1e-12); } // 2-D linear // { using A2 = std::array<double, 2>; std::vector<A2> F = { {0, 0}, {1, 1}, {2, 2}, {3, 3} }; std::vector<A2> c = { {0, 0}, {1, 1}, {2, 2}, {3, 3} }; df.load_column("F3", std::move(F), nan_policy::dont_pad_with_nans); df.load_column("c3", std::move(c), nan_policy::dont_pad_with_nans); DivergenceVisitor<A2> div; df.single_act_visit<A2, A2>("F3", "c3", div); const auto &r = div.get_result(); assert(r.size() == 4); for (const double v : r) assert(std::abs(v - 2.0) < 1e-9); } // 2-D quadratic // { using A2 = std::array<double, 2>; std::vector<A2> F = { {0, 0}, {1, 1}, {4, 4}, {9, 9} }; std::vector<A2> c = { {0, 0}, {1, 1}, {2, 2}, {3, 3} }; df.load_column("F4", std::move(F), nan_policy::dont_pad_with_nans); df.load_column("c4", std::move(c), nan_policy::dont_pad_with_nans); DivergenceVisitor<A2> div; df.single_act_visit<A2, A2>("F4", "c4", div); const auto &r = div.get_result(); assert(r.size() == 4); assert(std::abs(r[0] - 2.0) < 1e-9); assert(std::abs(r[1] - 4.0) < 1e-9); assert(std::abs(r[2] - 8.0) < 1e-9); assert(std::abs(r[3] - 10.0) < 1e-9); } // 3-D identity // { using A3 = std::array<double, 3>; std::vector<A3> F, c; for (double v { 0.0 }; v <= 4.0; v += 1.0) { F.push_back({ v, v, v }); c.push_back({ v, v, v }); } df.load_column("F5", std::move(F), nan_policy::dont_pad_with_nans); df.load_column("c5", std::move(c), nan_policy::dont_pad_with_nans); DivergenceVisitor<A3> div; df.single_act_visit<A3, A3>("F5", "c5", div); const auto &r = div.get_result(); assert(r.size() == 5); for (const double v : r) assert(std::abs(v - 3.0) < 1e-9); } // 1-D non-uniform // { std::vector<double> F = { 0.0, 1.0, 27.0, 216.0 }; std::vector<double> c = { 0.0, 1.0, 3.0, 6.0 }; df.load_column("F6", std::move(F), nan_policy::dont_pad_with_nans); df.load_column("c6", std::move(c), nan_policy::dont_pad_with_nans); DivergenceVisitor<double> div; df.single_act_visit<double, double>("F6", "c6", div); const auto &r = div.get_result(); assert(r.size() == 4); assert(std::abs(r[0] - 1.0) < 1e-9); assert(std::abs(r[1] - 9.0) < 1e-9); assert(std::abs(r[2] - 43.0) < 1e-9); assert(std::abs(r[3] - 63.0) < 1e-9); } // 2-D solenoidal // { using A2 = std::array<double, 2>; std::vector<A2> F, c; for (double v { 0.0 }; v <= 4.0; v += 1.0) { F.push_back({ -v, v }); // F = (−y, x) c.push_back({ v, v }); // coord = (x, y) = (v, v) on diagonal } df.load_column("F7", std::move(F), nan_policy::dont_pad_with_nans); df.load_column("c7", std::move(c), nan_policy::dont_pad_with_nans); DivergenceVisitor<A2> div; df.single_act_visit<A2, A2>("F7", "c7", div); assert(div.get_result().size() == 5); for (const double v : div.get_result()) assert(std::abs(v) < 1e-12); } // Integer type // { std::vector<int> F = { 0, 1, 4, 9, -16 }; // x² std::vector<int> c = { 0, 1, 2, 3, 4 }; df.load_column("F7", std::move(F), nan_policy::dont_pad_with_nans); df.load_column("c7", std::move(c), nan_policy::dont_pad_with_nans); DivergenceVisitor<int> div; df.single_act_visit<int, int>("F7", "c7", div); const auto &r = div.get_result(); assert(r.size() == 5); assert(r[0] == 1); assert(r[1] == 2); assert(r[2] == 4); assert(r[3] == -10); assert(r[4] == -25); } }