mirror of
https://github.com/gonum/gonum.git
synced 2025-10-04 23:02:42 +08:00
544 lines
16 KiB
Go
544 lines
16 KiB
Go
// Copyright ©2019 The Gonum Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package barneshut
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"golang.org/x/exp/rand"
|
|
|
|
"gonum.org/v1/gonum/floats"
|
|
"gonum.org/v1/gonum/spatial/r3"
|
|
)
|
|
|
|
type particle3 struct {
|
|
x, y, z, m float64
|
|
name string
|
|
}
|
|
|
|
func (p particle3) Coord3() r3.Vec { return r3.Vec{X: p.x, Y: p.y, Z: p.z} }
|
|
func (p particle3) Mass() float64 { return p.m }
|
|
|
|
var volumeTests = []struct {
|
|
name string
|
|
particles []particle3
|
|
want *Volume
|
|
}{
|
|
{
|
|
name: "nil",
|
|
particles: nil,
|
|
want: &Volume{},
|
|
},
|
|
{
|
|
name: "empty",
|
|
particles: []particle3{},
|
|
want: &Volume{Particles: []Particle3{}},
|
|
},
|
|
{
|
|
name: "one",
|
|
particles: []particle3{{m: 1}}, // Must have a mass to avoid vacuum decay.
|
|
want: &Volume{
|
|
root: bucket{
|
|
particle: particle3{x: 0, y: 0, z: 0, m: 1},
|
|
bounds: r3.Box{Min: r3.Vec{X: 0, Y: 0, Z: 0}, Max: r3.Vec{X: 0, Y: 0, Z: 0}},
|
|
center: r3.Vec{X: 0, Y: 0, Z: 0},
|
|
mass: 1,
|
|
},
|
|
|
|
Particles: []Particle3{
|
|
particle3{x: 0, y: 0, z: 0, m: 1},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "3 corners",
|
|
particles: []particle3{
|
|
{x: 1, y: 1, z: 1, m: 1},
|
|
{x: -1, y: 1, z: 0, m: 1},
|
|
{x: -1, y: -1, z: -1, m: 1},
|
|
},
|
|
want: &Volume{
|
|
root: bucket{
|
|
bounds: r3.Box{Min: r3.Vec{X: -1, Y: -1, Z: -1}, Max: r3.Vec{X: 1, Y: 1, Z: 1}},
|
|
nodes: [8]*bucket{
|
|
lnw: {
|
|
particle: particle3{x: -1, y: -1, z: -1, m: 1},
|
|
bounds: r3.Box{Min: r3.Vec{X: -1, Y: -1, Z: -1}, Max: r3.Vec{X: 0, Y: 0, Z: 0}},
|
|
center: r3.Vec{X: -1, Y: -1, Z: -1},
|
|
mass: 1,
|
|
},
|
|
use: {
|
|
particle: particle3{x: 1, y: 1, z: 1, m: 1},
|
|
bounds: r3.Box{Min: r3.Vec{X: 0, Y: 0, Z: 0}, Max: r3.Vec{X: 1, Y: 1, Z: 1}},
|
|
center: r3.Vec{X: 1, Y: 1, Z: 1},
|
|
mass: 1,
|
|
},
|
|
usw: {
|
|
particle: particle3{x: -1, y: 1, z: 0, m: 1},
|
|
bounds: r3.Box{Min: r3.Vec{X: -1, Y: 0, Z: 0}, Max: r3.Vec{X: 0, Y: 1, Z: 1}},
|
|
center: r3.Vec{X: -1, Y: 1, Z: 0},
|
|
mass: 1,
|
|
},
|
|
},
|
|
center: r3.Vec{X: -0.3333333333333333, Y: 0.3333333333333333, Z: 0},
|
|
mass: 3,
|
|
},
|
|
|
|
Particles: []Particle3{
|
|
particle3{x: 1, y: 1, z: 1, m: 1},
|
|
particle3{x: -1, y: 1, z: 0, m: 1},
|
|
particle3{x: -1, y: -1, z: -1, m: 1},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "4 corners",
|
|
particles: []particle3{
|
|
{x: 1, y: 1, z: -1, m: 1},
|
|
{x: -1, y: 1, z: 1, m: 1},
|
|
{x: 1, y: -1, z: 1, m: 1},
|
|
{x: -1, y: -1, z: -1, m: 1},
|
|
},
|
|
want: &Volume{
|
|
root: bucket{
|
|
bounds: r3.Box{Min: r3.Vec{X: -1, Y: -1, Z: -1}, Max: r3.Vec{X: 1, Y: 1, Z: 1}},
|
|
nodes: [8]*bucket{
|
|
lse: {
|
|
particle: particle3{x: 1, y: 1, z: -1, m: 1},
|
|
bounds: r3.Box{Min: r3.Vec{X: 0, Y: 0, Z: -1}, Max: r3.Vec{X: 1, Y: 1, Z: 0}},
|
|
center: r3.Vec{X: 1, Y: 1, Z: -1},
|
|
mass: 1,
|
|
},
|
|
lnw: {
|
|
particle: particle3{x: -1, y: -1, z: -1, m: 1},
|
|
bounds: r3.Box{Min: r3.Vec{X: -1, Y: -1, Z: -1}, Max: r3.Vec{X: 0, Y: 0, Z: 0}},
|
|
center: r3.Vec{X: -1, Y: -1, Z: -1},
|
|
mass: 1,
|
|
},
|
|
une: {
|
|
particle: particle3{x: 1, y: -1, z: 1, m: 1},
|
|
bounds: r3.Box{Min: r3.Vec{X: 0, Y: -1, Z: 0}, Max: r3.Vec{X: 1, Y: 0, Z: 1}},
|
|
center: r3.Vec{X: 1, Y: -1, Z: 1},
|
|
mass: 1,
|
|
},
|
|
usw: {
|
|
particle: particle3{x: -1, y: 1, z: 1, m: 1},
|
|
bounds: r3.Box{Min: r3.Vec{X: -1, Y: 0, Z: 0}, Max: r3.Vec{X: 0, Y: 1, Z: 1}},
|
|
center: r3.Vec{X: -1, Y: 1, Z: 1},
|
|
mass: 1,
|
|
},
|
|
},
|
|
center: r3.Vec{X: 0, Y: 0, Z: 0},
|
|
mass: 4,
|
|
},
|
|
|
|
Particles: []Particle3{
|
|
particle3{x: 1, y: 1, z: -1, m: 1},
|
|
particle3{x: -1, y: 1, z: 1, m: 1},
|
|
particle3{x: 1, y: -1, z: 1, m: 1},
|
|
particle3{x: -1, y: -1, z: -1, m: 1},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "5 corners",
|
|
particles: []particle3{
|
|
{x: 1, y: 1, z: -1, m: 1},
|
|
{x: -1, y: 1, z: 1, m: 1},
|
|
{x: 1, y: -1, z: 1, m: 1},
|
|
{x: -1, y: -1, z: -1, m: 1},
|
|
{x: -1.1, y: -1, z: -1.1, m: 1},
|
|
},
|
|
want: &Volume{
|
|
root: bucket{
|
|
bounds: r3.Box{Min: r3.Vec{X: -1.1, Y: -1, Z: -1.1}, Max: r3.Vec{X: 1, Y: 1, Z: 1}},
|
|
nodes: [8]*bucket{
|
|
lse: {
|
|
particle: particle3{x: 1, y: 1, z: -1, m: 1},
|
|
bounds: r3.Box{Min: r3.Vec{X: -0.050000000000000044, Y: 0, Z: -1.1}, Max: r3.Vec{X: 1, Y: 1, Z: -0.050000000000000044}},
|
|
center: r3.Vec{X: 1, Y: 1, Z: -1},
|
|
mass: 1,
|
|
},
|
|
lnw: {
|
|
bounds: r3.Box{Min: r3.Vec{X: -1.1, Y: -1, Z: -1.1}, Max: r3.Vec{X: -0.050000000000000044, Y: 0, Z: -0.050000000000000044}},
|
|
nodes: [8]*bucket{
|
|
lnw: {
|
|
bounds: r3.Box{Min: r3.Vec{X: -1.1, Y: -1, Z: -1.1}, Max: r3.Vec{X: -0.5750000000000001, Y: -0.5, Z: -0.5750000000000001}},
|
|
nodes: [8]*bucket{
|
|
lnw: {
|
|
bounds: r3.Box{Min: r3.Vec{X: -1.1, Y: -1, Z: -1.1}, Max: r3.Vec{X: -0.8375000000000001, Y: -0.75, Z: -0.8375000000000001}},
|
|
nodes: [8]*bucket{
|
|
lnw: {
|
|
bounds: r3.Box{Min: r3.Vec{X: -1.1, Y: -1, Z: -1.1}, Max: r3.Vec{X: -0.9687500000000001, Y: -0.875, Z: -0.9687500000000001}},
|
|
nodes: [8]*bucket{
|
|
lnw: {
|
|
particle: particle3{x: -1.1, y: -1, z: -1.1, m: 1},
|
|
bounds: r3.Box{Min: r3.Vec{X: -1.1, Y: -1, Z: -1.1}, Max: r3.Vec{X: -1.034375, Y: -0.9375, Z: -1.034375}},
|
|
center: r3.Vec{X: -1.1, Y: -1, Z: -1.1},
|
|
mass: 1,
|
|
},
|
|
une: {
|
|
particle: particle3{x: -1, y: -1, z: -1, m: 1},
|
|
bounds: r3.Box{Min: r3.Vec{X: -1.034375, Y: -1, Z: -1.034375}, Max: r3.Vec{X: -0.9687500000000001, Y: -0.9375, Z: -0.9687500000000001}},
|
|
center: r3.Vec{X: -1, Y: -1, Z: -1},
|
|
mass: 1,
|
|
},
|
|
},
|
|
center: r3.Vec{X: -1.05, Y: -1, Z: -1.05},
|
|
mass: 2,
|
|
},
|
|
},
|
|
center: r3.Vec{X: -1.05, Y: -1, Z: -1.05},
|
|
mass: 2,
|
|
},
|
|
},
|
|
center: r3.Vec{X: -1.05, Y: -1, Z: -1.05},
|
|
mass: 2,
|
|
},
|
|
},
|
|
center: r3.Vec{X: -1.05, Y: -1, Z: -1.05},
|
|
mass: 2,
|
|
},
|
|
une: {
|
|
particle: particle3{x: 1, y: -1, z: 1, m: 1},
|
|
bounds: r3.Box{Min: r3.Vec{X: -0.050000000000000044, Y: -1, Z: -0.050000000000000044}, Max: r3.Vec{X: 1, Y: 0, Z: 1}},
|
|
center: r3.Vec{X: 1, Y: -1, Z: 1},
|
|
mass: 1,
|
|
},
|
|
usw: {
|
|
particle: particle3{x: -1, y: 1, z: 1, m: 1},
|
|
bounds: r3.Box{Min: r3.Vec{X: -1.1, Y: 0, Z: -0.050000000000000044}, Max: r3.Vec{X: -0.050000000000000044, Y: 1, Z: 1}},
|
|
center: r3.Vec{X: -1, Y: 1, Z: 1},
|
|
mass: 1,
|
|
},
|
|
},
|
|
center: r3.Vec{X: -0.22000000000000003, Y: -0.2, Z: -0.22000000000000003},
|
|
mass: 5,
|
|
},
|
|
Particles: []Particle3{
|
|
particle3{x: 1, y: 1, z: -1, m: 1},
|
|
particle3{x: -1, y: 1, z: 1, m: 1},
|
|
particle3{x: 1, y: -1, z: 1, m: 1},
|
|
particle3{x: -1, y: -1, z: -1, m: 1},
|
|
particle3{x: -1.1, y: -1, z: -1.1, m: 1},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// This case is derived from the 2D example of the same name,
|
|
// but with a monotonic increase in Z position according to name.
|
|
name: "http://arborjs.org/docs/barnes-hut example",
|
|
particles: []particle3{
|
|
{x: 64.5, y: 81.5, z: 0, m: 1, name: "A"},
|
|
{x: 242, y: 34, z: 40, m: 1, name: "B"},
|
|
{x: 199, y: 69, z: 80, m: 1, name: "C"},
|
|
{x: 285, y: 106.5, z: 120, m: 1, name: "D"},
|
|
{x: 170, y: 194.5, z: 160, m: 1, name: "E"},
|
|
{x: 42.5, y: 334.5, z: 200, m: 1, name: "F"},
|
|
{x: 147, y: 309, z: 240, m: 1, name: "G"},
|
|
{x: 236.5, y: 324, z: 280, m: 1, name: "H"},
|
|
},
|
|
want: &Volume{
|
|
root: bucket{
|
|
bounds: r3.Box{Min: r3.Vec{X: 42.5, Y: 34, Z: 0}, Max: r3.Vec{X: 285, Y: 334.5, Z: 280}},
|
|
nodes: [8]*bucket{
|
|
lne: {
|
|
bounds: r3.Box{Min: r3.Vec{X: 163.75, Y: 34, Z: 0}, Max: r3.Vec{X: 285, Y: 184.25, Z: 140}},
|
|
nodes: [8]*bucket{
|
|
lne: {
|
|
particle: particle3{x: 242, y: 34, z: 40, m: 1, name: "B"},
|
|
bounds: r3.Box{Min: r3.Vec{X: 224.375, Y: 34, Z: 0}, Max: r3.Vec{X: 285, Y: 109.125, Z: 70}},
|
|
center: r3.Vec{X: 242, Y: 34, Z: 40},
|
|
mass: 1,
|
|
},
|
|
une: {
|
|
particle: particle3{x: 285, y: 106.5, z: 120, m: 1, name: "D"},
|
|
bounds: r3.Box{Min: r3.Vec{X: 224.375, Y: 34, Z: 70}, Max: r3.Vec{X: 285, Y: 109.125, Z: 140}},
|
|
center: r3.Vec{X: 285, Y: 106.5, Z: 120},
|
|
mass: 1,
|
|
},
|
|
unw: {
|
|
particle: particle3{x: 199, y: 69, z: 80, m: 1, name: "C"},
|
|
bounds: r3.Box{Min: r3.Vec{X: 163.75, Y: 34, Z: 70}, Max: r3.Vec{X: 224.375, Y: 109.125, Z: 140}},
|
|
center: r3.Vec{X: 199, Y: 69, Z: 80},
|
|
mass: 1,
|
|
},
|
|
},
|
|
center: r3.Vec{X: 242, Y: 69.83333333333333, Z: 80},
|
|
mass: 3,
|
|
},
|
|
lnw: {
|
|
particle: particle3{x: 64.5, y: 81.5, z: 0, m: 1, name: "A"},
|
|
bounds: r3.Box{Min: r3.Vec{X: 42.5, Y: 34, Z: 0}, Max: r3.Vec{X: 163.75, Y: 184.25, Z: 140}},
|
|
center: r3.Vec{X: 64.5, Y: 81.5, Z: 0},
|
|
mass: 1,
|
|
},
|
|
(*bucket)(nil),
|
|
use: {
|
|
bounds: r3.Box{Min: r3.Vec{X: 163.75, Y: 184.25, Z: 140}, Max: r3.Vec{X: 285, Y: 334.5, Z: 280}},
|
|
nodes: [8]*bucket{
|
|
lnw: {
|
|
particle: particle3{x: 170, y: 194.5, z: 160, m: 1, name: "E"},
|
|
bounds: r3.Box{Min: r3.Vec{X: 163.75, Y: 184.25, Z: 140}, Max: r3.Vec{X: 224.375, Y: 259.375, Z: 210}},
|
|
center: r3.Vec{X: 170, Y: 194.5, Z: 160},
|
|
mass: 1,
|
|
},
|
|
use: {
|
|
particle: particle3{x: 236.5, y: 324, z: 280, m: 1, name: "H"},
|
|
bounds: r3.Box{Min: r3.Vec{X: 224.375, Y: 259.375, Z: 210}, Max: r3.Vec{X: 285, Y: 334.5, Z: 280}},
|
|
center: r3.Vec{X: 236.5, Y: 324, Z: 280},
|
|
mass: 1,
|
|
},
|
|
},
|
|
center: r3.Vec{X: 203.25, Y: 259.25, Z: 220},
|
|
mass: 2,
|
|
},
|
|
usw: {
|
|
bounds: r3.Box{Min: r3.Vec{X: 42.5, Y: 184.25, Z: 140}, Max: r3.Vec{X: 163.75, Y: 334.5, Z: 280}},
|
|
nodes: [8]*bucket{
|
|
lsw: {
|
|
particle: particle3{x: 42.5, y: 334.5, z: 200, m: 1, name: "F"},
|
|
bounds: r3.Box{Min: r3.Vec{X: 42.5, Y: 259.375, Z: 140}, Max: r3.Vec{X: 103.125, Y: 334.5, Z: 210}},
|
|
center: r3.Vec{X: 42.5, Y: 334.5, Z: 200},
|
|
mass: 1,
|
|
},
|
|
use: {
|
|
particle: particle3{x: 147, y: 309, z: 240, m: 1, name: "G"},
|
|
bounds: r3.Box{Min: r3.Vec{X: 103.125, Y: 259.375, Z: 210}, Max: r3.Vec{X: 163.75, Y: 334.5, Z: 280}},
|
|
center: r3.Vec{X: 147, Y: 309, Z: 240},
|
|
mass: 1,
|
|
},
|
|
},
|
|
center: r3.Vec{X: 94.75, Y: 321.75, Z: 220},
|
|
mass: 2,
|
|
},
|
|
},
|
|
center: r3.Vec{X: 173.3125, Y: 181.625, Z: 140},
|
|
mass: 8,
|
|
},
|
|
|
|
Particles: []Particle3{
|
|
particle3{x: 64.5, y: 81.5, z: 0, m: 1, name: "A"},
|
|
particle3{x: 242, y: 34, z: 40, m: 1, name: "B"},
|
|
particle3{x: 199, y: 69, z: 80, m: 1, name: "C"},
|
|
particle3{x: 285, y: 106.5, z: 120, m: 1, name: "D"},
|
|
particle3{x: 170, y: 194.5, z: 160, m: 1, name: "E"},
|
|
particle3{x: 42.5, y: 334.5, z: 200, m: 1, name: "F"},
|
|
particle3{x: 147, y: 309, z: 240, m: 1, name: "G"},
|
|
particle3{x: 236.5, y: 324, z: 280, m: 1, name: "H"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
func TestVolume(t *testing.T) {
|
|
t.Parallel()
|
|
const tol = 1e-15
|
|
|
|
for _, test := range volumeTests {
|
|
var particles []Particle3
|
|
if test.particles != nil {
|
|
particles = make([]Particle3, len(test.particles))
|
|
}
|
|
for i, p := range test.particles {
|
|
particles[i] = p
|
|
}
|
|
|
|
got, err := NewVolume(particles)
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
continue
|
|
}
|
|
|
|
if test.want != nil && !reflect.DeepEqual(got, test.want) {
|
|
t.Errorf("unexpected result for %q: got:%v want:%v", test.name, got, test.want)
|
|
}
|
|
|
|
// Recursively check all internal centers of mass.
|
|
walkVolume(&got.root, func(b *bucket) {
|
|
var sub []Particle3
|
|
walkVolume(b, func(b *bucket) {
|
|
if b.particle != nil {
|
|
sub = append(sub, b.particle)
|
|
}
|
|
})
|
|
center, mass := centerOfMass3(sub)
|
|
if !floats.EqualWithinAbsOrRel(center.X, b.center.X, tol, tol) || !floats.EqualWithinAbsOrRel(center.Y, b.center.Y, tol, tol) || !floats.EqualWithinAbsOrRel(center.Z, b.center.Z, tol, tol) {
|
|
t.Errorf("unexpected result for %q for center of mass: got:%f want:%f", test.name, b.center, center)
|
|
}
|
|
if !floats.EqualWithinAbsOrRel(mass, b.mass, tol, tol) {
|
|
t.Errorf("unexpected result for %q for total mass: got:%f want:%f", test.name, b.mass, mass)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func centerOfMass3(particles []Particle3) (center r3.Vec, mass float64) {
|
|
for _, p := range particles {
|
|
m := p.Mass()
|
|
mass += m
|
|
c := p.Coord3()
|
|
center.X += c.X * m
|
|
center.Y += c.Y * m
|
|
center.Z += c.Z * m
|
|
}
|
|
if mass != 0 {
|
|
center.X /= mass
|
|
center.Y /= mass
|
|
center.Z /= mass
|
|
}
|
|
return center, mass
|
|
}
|
|
|
|
func walkVolume(t *bucket, fn func(*bucket)) {
|
|
if t == nil {
|
|
return
|
|
}
|
|
fn(t)
|
|
for _, q := range t.nodes {
|
|
walkVolume(q, fn)
|
|
}
|
|
}
|
|
|
|
func TestVolumeForceOn(t *testing.T) {
|
|
t.Parallel()
|
|
const (
|
|
size = 1000
|
|
tol = 1e-3
|
|
)
|
|
for _, n := range []int{3e3, 1e4, 3e4} {
|
|
rnd := rand.New(rand.NewSource(1))
|
|
particles := make([]Particle3, n)
|
|
for i := range particles {
|
|
particles[i] = particle3{x: size * rnd.Float64(), y: size * rnd.Float64(), z: size * rnd.Float64(), m: 1}
|
|
}
|
|
|
|
moved := make([]r3.Vec, n)
|
|
for i, p := range particles {
|
|
var v r3.Vec
|
|
m := p.Mass()
|
|
pv := p.Coord3()
|
|
for _, e := range particles {
|
|
v = v.Add(Gravity3(p, e, m, e.Mass(), e.Coord3().Sub(pv)))
|
|
}
|
|
moved[i] = p.Coord3().Add(v)
|
|
}
|
|
|
|
volume, err := NewVolume(particles)
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
continue
|
|
}
|
|
for _, theta := range []float64{0, 0.3, 0.6, 0.9} {
|
|
theta := theta
|
|
t.Run(fmt.Sprintf("%d-body/theta=%v", len(particles), theta), func(t *testing.T) {
|
|
t.Parallel()
|
|
var ssd, sd float64
|
|
var calls int
|
|
for i, p := range particles {
|
|
v := volume.ForceOn(p, theta, func(p1, p2 Particle3, m1, m2 float64, v r3.Vec) r3.Vec {
|
|
calls++
|
|
return Gravity3(p1, p2, m1, m2, v)
|
|
})
|
|
pos := p.Coord3().Add(v)
|
|
d := moved[i].Sub(pos)
|
|
ssd += d.X*d.X + d.Y*d.Y + d.Z*d.Z
|
|
sd += math.Hypot(math.Hypot(d.X, d.Y), d.Z)
|
|
}
|
|
rmsd := math.Sqrt(ssd / float64(len(particles)))
|
|
if rmsd > tol {
|
|
t.Error("RMSD for approximation too high")
|
|
}
|
|
t.Logf("rmsd=%.4v md=%.4v calls/particle=%.5v",
|
|
rmsd, sd/float64(len(particles)), float64(calls)/float64(len(particles)))
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
var (
|
|
fv3sink r3.Vec
|
|
volumeSink *Volume
|
|
)
|
|
|
|
func BenchmarkNewVolume(b *testing.B) {
|
|
for _, n := range []int{1e3, 1e4, 1e5, 1e6} {
|
|
rnd := rand.New(rand.NewSource(1))
|
|
particles := make([]Particle3, n)
|
|
for i := range particles {
|
|
particles[i] = particle3{x: rnd.Float64(), y: rnd.Float64(), z: rnd.Float64(), m: 1}
|
|
}
|
|
b.ResetTimer()
|
|
var err error
|
|
b.Run(fmt.Sprintf("%d-body", len(particles)), func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
volumeSink, err = NewVolume(particles)
|
|
if err != nil {
|
|
b.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func BenchmarkVolumeForceOn(b *testing.B) {
|
|
for _, n := range []int{1e3, 1e4, 1e5} {
|
|
for _, theta := range []float64{0, 0.1, 0.5, 1, 1.5} {
|
|
if n > 1e4 && theta < 0.5 {
|
|
// Don't run unreasonably long benchmarks.
|
|
continue
|
|
}
|
|
rnd := rand.New(rand.NewSource(1))
|
|
particles := make([]Particle3, n)
|
|
for i := range particles {
|
|
particles[i] = particle3{x: rnd.Float64(), y: rnd.Float64(), z: rnd.Float64(), m: 1}
|
|
}
|
|
volume, err := NewVolume(particles)
|
|
if err != nil {
|
|
b.Fatalf("unexpected error: %v", err)
|
|
}
|
|
b.ResetTimer()
|
|
b.Run(fmt.Sprintf("%d-body/theta=%v", len(particles), theta), func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
for _, p := range particles {
|
|
fv3sink = volume.ForceOn(p, theta, Gravity3)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkVolumeFull(b *testing.B) {
|
|
for _, n := range []int{1e3, 1e4, 1e5} {
|
|
for _, theta := range []float64{0, 0.1, 0.5, 1, 1.5} {
|
|
if n > 1e4 && theta < 0.5 {
|
|
// Don't run unreasonably long benchmarks.
|
|
continue
|
|
}
|
|
rnd := rand.New(rand.NewSource(1))
|
|
particles := make([]Particle3, n)
|
|
for i := range particles {
|
|
particles[i] = particle3{x: rnd.Float64(), y: rnd.Float64(), z: rnd.Float64(), m: 1}
|
|
}
|
|
b.ResetTimer()
|
|
b.Run(fmt.Sprintf("%d-body/theta=%v", len(particles), theta), func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
volume, err := NewVolume(particles)
|
|
if err != nil {
|
|
b.Fatalf("unexpected error: %v", err)
|
|
}
|
|
for _, p := range particles {
|
|
fv3sink = volume.ForceOn(p, theta, Gravity3)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|