Repository: gonum/gonum
Pull request #1817 (2022-07-26 04:28:39)

spatial/r3: add Divergence
Please take a look.

Resolves part of https://github.com/gonum/gonum/issues/1809#issuecomment-1140334118

codecov-commenter: # [Codecov](https://codecov.io/gh/gonum/gonum/pull/1817?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum) Report
> Merging [#1817](https://codecov.io/gh/gonum/gonum/pull/1817?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum) (4ad3467) into [master](https://codecov.io/gh/gonum/gonum/commit/c4e2c8cf3ea5b5daac3aa18419abe4538c7f2b73?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum) (c4e2c8c) will **decrease** coverage by `0.06%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##           master    #1817      +/-   ##
==========================================
- Coverage   73.47%   73.41%   -0.07%     
==========================================
  Files         528      520       -8     
  Lines       61965    61546     -419     
==========================================
- Hits        45531    45185     -346     
+ Misses      13870    13827      -43     
+ Partials     2564     2534      -30     
```


| [Impacted Files](https://codecov.io/gh/gonum/gonum/pull/1817?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum) | Coverage Δ | |
|---|---|---|
| [spatial/r3/vector.go](https://codecov.io/gh/gonum/gonum/pull/1817/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum#diff-c3BhdGlhbC9yMy92ZWN0b3IuZ28=) | `100.00% <100.00%> (ø)` | |
| [lapack/gonum/dlaqr1.go](https://codecov.io/gh/gonum/gonum/pull/1817/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum#diff-bGFwYWNrL2dvbnVtL2RsYXFyMS5nbw==) | `40.62% <0.00%> (-15.63%)` | :arrow_down: |
| [graph/coloring/coloring.go](https://codecov.io/gh/gonum/gonum/pull/1817/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum#diff-Z3JhcGgvY29sb3JpbmcvY29sb3JpbmcuZ28=) | `89.72% <0.00%> (-0.61%)` | :arrow_down: |
| [spatial/r3/mat\_unsafe.go](https://codecov.io/gh/gonum/gonum/pull/1817/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum#diff-c3BhdGlhbC9yMy9tYXRfdW5zYWZlLmdv) | | |
| [graph/iterator/lines\_map.go](https://codecov.io/gh/gonum/gonum/pull/1817/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum#diff-Z3JhcGgvaXRlcmF0b3IvbGluZXNfbWFwLmdv) | | |
| [...sp/fourier/internal/fftpack/array\_bounds\_checks.go](https://codecov.io/gh/gonum/gonum/pull/1817/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum#diff-ZHNwL2ZvdXJpZXIvaW50ZXJuYWwvZmZ0cGFjay9hcnJheV9ib3VuZHNfY2hlY2tzLmdv) | | |
| [graph/internal/set/same.go](https://codecov.io/gh/gonum/gonum/pull/1817/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum#diff-Z3JhcGgvaW50ZXJuYWwvc2V0L3NhbWUuZ28=) | | |
| [graph/iterator/map.go](https://codecov.io/gh/gonum/gonum/pull/1817/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum#diff-Z3JhcGgvaXRlcmF0b3IvbWFwLmdv) | | |
| [mat/offset.go](https://codecov.io/gh/gonum/gonum/pull/1817/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum#diff-bWF0L29mZnNldC5nbw==) | | |
| [mat/index\_bound\_checks.go](https://codecov.io/gh/gonum/gonum/pull/1817/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum#diff-bWF0L2luZGV4X2JvdW5kX2NoZWNrcy5nbw==) | | |
| ... and [1 more](https://codecov.io/gh/gonum/gonum/pull/1817/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/gonum/gonum/pull/1817?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/gonum/gonum/pull/1817?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum). Last update [c4e2c8c...4ad3467](https://codecov.io/gh/gonum/gonum/pull/1817?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum).


Patch:
diff --git a/spatial/r3/vector.go b/spatial/r3/vector.go
index bcd0f881..9d4ab0dd 100644
--- a/spatial/r3/vector.go
+++ b/spatial/r3/vector.go
@@ -83,6 +83,18 @@ func Cos(p, q Vec) float64 {
 	return Dot(p, q) / (Norm(p) * Norm(q))
 }
 
+// Divergence returns the divergence of the vector field at the point p,
+// approximated using finite differences with the given step sizes.
+func Divergence(p, step Vec, field func(Vec) Vec) float64 {
+	sx := Vec{X: step.X}
+	divx := (field(Add(p, sx)).X - field(Sub(p, sx)).X) / step.X
+	sy := Vec{Y: step.Y}
+	divy := (field(Add(p, sy)).Y - field(Sub(p, sy)).Y) / step.Y
+	sz := Vec{Z: step.Z}
+	divz := (field(Add(p, sz)).Z - field(Sub(p, sz)).Z) / step.Z
+	return 0.5 * (divx + divy + divz)
+}
+
 // minElem return a vector with the minimum components of two vectors.
 func minElem(a, b Vec) Vec {
 	return Vec{


Test patch:
diff --git a/spatial/r3/vector_test.go b/spatial/r3/vector_test.go
index ad5376b3..8c9df4fc 100644
--- a/spatial/r3/vector_test.go
+++ b/spatial/r3/vector_test.go
@@ -8,6 +8,8 @@ import (
 	"math"
 	"testing"
 
+	"golang.org/x/exp/rand"
+
 	"gonum.org/v1/gonum/floats/scalar"
 	"gonum.org/v1/gonum/mat"
 )
@@ -254,6 +256,54 @@ func TestRotate(t *testing.T) {
 	}
 }
 
+var vectorFields = []struct {
+	field      func(Vec) Vec
+	divergence func(Vec) float64
+}{
+	{
+		field: func(v Vec) Vec {
+			return Vec{X: v.X * v.Y * v.Z, Y: v.Y * v.Z, Z: v.Z * v.X}
+		},
+		divergence: func(v Vec) float64 {
+			return v.X + v.Y*v.Z + v.Z
+		},
+	},
+	{
+		field: func(v Vec) Vec {
+			sx := math.Sin(v.X)
+			sy, cy := math.Sincos(v.Y)
+			return Vec{
+				X: v.X * v.Y * v.Z * cy,
+				Y: v.Y*v.Z + sx,
+				Z: v.Z * v.X / sy,
+			}
+		},
+		divergence: func(v Vec) float64 {
+			sy, cy := math.Sincos(v.Y)
+			return v.X/sy + v.Y*v.Z*cy + v.Z
+		},
+	},
+}
+
+func TestDivergence(t *testing.T) {
+	const (
+		tol = 1e-10
+		h   = 1e-2
+	)
+	step := Vec{X: h, Y: h, Z: h}
+	rnd := rand.New(rand.NewSource(1))
+	for _, test := range vectorFields {
+		for i := 0; i < 30; i++ {
+			p := randomVec(rnd)
+			got := Divergence(p, step, test.field)
+			want := test.divergence(p)
+			if math.Abs(got-want) > tol {
+				t.Errorf("result out of tolerance. got %v, want %v", got, want)
+			}
+		}
+	}
+}
+
 func vecDense(v Vec) *mat.VecDense {
 	return mat.NewVecDense(3, []float64{v.X, v.Y, v.Z})
 }