From 07a4e26087c1241358c910e9cff218032b2939c8 Mon Sep 17 00:00:00 2001 From: Emile Date: Wed, 13 Feb 2019 22:36:23 +0100 Subject: working! --- db_actions.go | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++++- db_actions_test.go | 73 +++++++++++++++++++++++ 2 files changed, 238 insertions(+), 2 deletions(-) create mode 100644 db_actions_test.go diff --git a/db_actions.go b/db_actions.go index d93728b..1cadf77 100644 --- a/db_actions.go +++ b/db_actions.go @@ -25,6 +25,7 @@ import ( "io" "io/ioutil" "log" + "math" "strconv" "strings" "time" @@ -907,8 +908,8 @@ func getCenterOfMass(nodeID int64) structs.Vec2 { return structs.Vec2{X: CenterOfMass[0], Y: CenterOfMass[1]} } -// getStarCoordinates gets the star coordinates of a star using a given nodeID. It returns a vector describing the -// coordinates +// getStarCoordinates gets the star coordinates of a star using a given nodeID. +// It returns a vector describing the coordinates func getStarCoordinates(nodeID int64) structs.Vec2 { var Coordinates [2]float64 @@ -925,3 +926,165 @@ func getStarCoordinates(nodeID int64) structs.Vec2 { return structs.Vec2{X: Coordinates[0], Y: Coordinates[1]} } + +// CalcAllForces calculates all the forces acting on the given star. +// The theta value it receives is used by the Barnes-Hut algorithm to determine what +// stars to include into the calculations +func CalcAllForces(database *sql.DB, star structs.Star2D, theta float64) structs.Vec2 { + db = database + + // calculate all the forces and add them to the list of all forces + // this is done recursively + // first of all, get the root id + log.Println("getting the root ID") + rootID := getRootNodeID(1) + log.Println("done getting the root ID") + + log.Printf("Calculating the forces acting on the star %v", star) + force := CalcAllForcesNode(star, rootID, theta) + log.Printf("Done calculating the forces acting on the star %v", star) + log.Printf("Force: %v", force) + + return force +} + +// calcAllForces nodes calculates the forces in between a sta log.Printf("Calculating the forces acting on the star %v", star)r and a node and returns the overall force +// TODO: implement the calcForce(star, centerOfMass) {...} function +// TODO: implement the getSubtreeIDs(nodeID) []int64 {...} function +func CalcAllForcesNode(star structs.Star2D, nodeID int64, theta float64) structs.Vec2 { + fmt.Println("-----------------------------------------------------------") + log.Printf("NodeID: %d \t star: %v \t theta: %f \t nodeboxwidth: %f", nodeID, star, theta, getBoxWidth(nodeID)) + var forceX float64 + var forceY float64 + var localTheta float64 + + if nodeID != 0 { + log.Println("Calculating the localtheta") + localTheta = calcTheta(star, nodeID) + log.Printf("Done calculating theta: %v", localTheta) + } + + if localTheta < theta { + log.Printf("localTheta < theta") + var force structs.Vec2 + + // if the nodeID is not zero, use the center of mass as the other star + if nodeID != 0 { + pseudoStarCoodinates := getCenterOfMass(nodeID) + PseudoStar := structs.Star2D{ + C: structs.Vec2{ + X: pseudoStarCoodinates.X, + Y: pseudoStarCoodinates.Y, + }, + V: structs.Vec2{ + X: 0, + Y: 0, + }, + M: 1000, + } + log.Printf("PseudoStar: %v", PseudoStar) + force = calcForce(star, PseudoStar) + + // else, use the star in the node as the other star + } else { + if getStarID(nodeID) != 0 { + var pseudoStar = getStar(getStarID(nodeID)) + force = calcForce(star, pseudoStar) + } + } + + forceX = force.X + forceY = force.X + } else { + log.Printf("localTheta > theta") + // iterate over all subtrees and add the forces acting through them + var subtreeIDs [4]int64 + subtreeIDs = getSubtreeIDs(nodeID) + for _, subtreeID := range subtreeIDs { + + // don't recurse into + if subtreeID != 0 { + var force = CalcAllForcesNode(star, subtreeID, theta) + log.Printf("force: %v", force) + forceX += force.X + forceY += force.Y + } + } + } + return structs.Vec2{forceX, forceY} +} + +// calcTheta calculates the theat for a given star and a node +func calcTheta(star structs.Star2D, nodeID int64) float64 { + d := getBoxWidth(nodeID) + r := distance(star, nodeID) + theta := d / r + return theta +} + +// calculate the distance in between the star and the node with the given ID +func distance(star structs.Star2D, nodeID int64) float64 { + var starX float64 = star.C.X + var starY float64 = star.C.Y + var node structs.Vec2 = getNodeCenterOfMass(nodeID) + var nodeX float64 = node.X + var nodeY float64 = node.Y + + var tmpX = math.Pow(starX-nodeX, 2) + var tmpY = math.Pow(starY-nodeY, 2) + + var distance float64 = math.Sqrt(tmpX + tmpY) + return distance +} + +// getNodeCenterOfMass returns the center of mass of the node with the given ID +func getNodeCenterOfMass(nodeID int64) structs.Vec2 { + var Coordinates [2]float64 + + // get the star from the stars table + query := fmt.Sprintf("SELECT center_of_mass[1], center_of_mass[2] FROM nodes WHERE node_id=%d", nodeID) + err := db.QueryRow(query).Scan(&Coordinates[0], &Coordinates[1]) + if err != nil { + log.Fatalf("[ E ] getNodeCenterOfMass query: %v \n\t\t\tquery: %s\n", err, query) + } + + return structs.Vec2{X: Coordinates[0], Y: Coordinates[1]} +} + +// getSubtreeIDs returns the id of the subtrees of the nodeID +func getSubtreeIDs(nodeID int64) [4]int64 { + + var subtreeIDs [4]int64 + + // get the star from the stars table + query := fmt.Sprintf("SELECT subnode[1], subnode[2], subnode[3], subnode[4] FROM nodes WHERE node_id=%d", nodeID) + err := db.QueryRow(query).Scan(&subtreeIDs[0], &subtreeIDs[1], &subtreeIDs[2], &subtreeIDs[3]) + if err != nil { + log.Fatalf("[ E ] getSubtreeIDs query: %v \n\t\t\tquery: %s\n", err, query) + } + + return subtreeIDs +} + +func calcForce(s1 structs.Star2D, s2 structs.Star2D) structs.Vec2 { + log.Println("+++++++++++++++++++++++++") + log.Printf("Calculating the force acting inbetween %f and %f", s1, s2) + G := 6.6726 * math.Pow(10, -11) + + // calculate the force acting + var combinedMass float64 = s1.M * s2.M + var distance float64 = math.Sqrt(math.Pow(math.Abs(s1.C.X-s2.C.X), 2) + math.Pow(math.Abs(s1.C.Y-s2.C.Y), 2)) + + var scalar float64 = G * ((combinedMass) / math.Pow(distance, 2)) + + // define a unit vector pointing from s1 to s2 + var vector structs.Vec2 = structs.Vec2{s2.C.X - s1.C.X, s2.C.Y - s1.C.Y} + var UnitVector structs.Vec2 = structs.Vec2{vector.X / distance, vector.Y / distance} + + // multiply the vector with the force to get a vector representing the force acting + var force structs.Vec2 = UnitVector.Multiply(scalar) + log.Println("+++++++++++++++++++++++++") + + // return the force exerted on s1 by s2 + return force +} diff --git a/db_actions_test.go b/db_actions_test.go new file mode 100644 index 0000000..7b96426 --- /dev/null +++ b/db_actions_test.go @@ -0,0 +1,73 @@ +// db_actions defines actions on the database +// Copyright (C) 2019 Emile Hansmaennel +// +// This program 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. +// +// This program 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 this program. If not, see . + +package db_actions + +import ( + "database/sql" + "reflect" + "testing" + + "git.darknebu.la/GalaxySimulator/structs" + _ "github.com/lib/pq" +) + +func TestCalcAllForces(t *testing.T) { + // define a database + db = ConnectToDB() + db.SetMaxOpenConns(75) + + type args struct { + database *sql.DB + star structs.Star2D + theta float64 + } + tests := []struct { + name string + args args + want structs.Vec2 + }{ + { + name: "force acting on a single star", + args: args{ + database: db, + star: structs.Star2D{ + C: structs.Vec2{ + X: 100, + Y: 100, + }, + V: structs.Vec2{ + X: 0, + Y: 0, + }, + M: 1000, + }, + theta: 0.5, + }, + want: structs.Vec2{ + X: 0, + Y: 0, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := CalcAllForces(tt.args.database, tt.args.star, tt.args.theta); !reflect.DeepEqual(got, tt.want) { + t.Errorf("CalcAllForces() = %v, want %v", got, tt.want) + } + }) + } +} -- cgit 1.4.1