From 0525bc463f53487a7de8409697fabec28471a0bd Mon Sep 17 00:00:00 2001 From: emile Date: Mon, 15 Oct 2018 16:07:22 +0200 Subject: for detailed information about the changes, see https://git.darknebu.la/GalaxySimulator/Source/pulls/1 --- Source | Bin 0 -> 3302760 bytes csv/csv.go | 56 +++++-------------------- draw/draw.go | 69 ++++++++++++++++--------------- file/file.go | 31 ++++++++++++++ forces/forces.go | 117 ++++++++++++++++++++++++---------------------------- main.go | 31 +++++++------- out_0.png | Bin 0 -> 3367531 bytes structs/star.go | 29 +++++++++++++ structs/structs.go | 23 ----------- structs/vector2D.go | 63 ++++++++++++++++++++++++++++ 10 files changed, 239 insertions(+), 180 deletions(-) create mode 100755 Source create mode 100644 file/file.go create mode 100644 out_0.png create mode 100644 structs/star.go delete mode 100644 structs/structs.go create mode 100644 structs/vector2D.go diff --git a/Source b/Source new file mode 100755 index 0000000..b35d91d Binary files /dev/null and b/Source differ diff --git a/csv/csv.go b/csv/csv.go index 7a0fa9f..aca131b 100644 --- a/csv/csv.go +++ b/csv/csv.go @@ -1,75 +1,39 @@ package csv import ( + "../file" "../structs" - "encoding/csv" - "log" - "math/rand" - "os" + "git.darknebu.la/bit/logplus" "strconv" - "time" ) -// openStar2DCSV opens the file at the given path and reads its content. It then returns the content inform of a slice -// of slices -func openStar2DCSV(path string) [][]string { - - // Open the file located at the given path - b, err := os.Open(path) - - // Handle errors - if err != nil { - log.Printf("openStar2DsCSV Panic! (cannot read file from %s)", path) - } - - // Close the file afre reading it's content - defer b.Close() - - // Parse the files conten usin a csv-reader - lines, err := csv.NewReader(b).ReadAll() - - // Handle errors - if err != nil { - log.Println("openStar2DsCSV Panic! (cannot read the files content)") - } - - return lines -} - // Import gets a file, a starting line, an ending line and a struct. It then adds the content of the file to the struct // For finding the length of the .csv, you can use the following command in linux: // $ cat | wc -l func Import(path string, start int, end int, slice []structs.Star2D) []structs.Star2D { - lines := openStar2DCSV(path) - - // seed the random number generator - rand.Seed(time.Now().UTC().UnixNano()) + f, _ := file.Open(path) + lines, _ := f.ReadCSV() - // iterate over all the lines in the given range for linenr, line := range lines[start:end] { x, errx := strconv.ParseFloat(line[0], 64) y, erry := strconv.ParseFloat(line[1], 64) // Handle errors if errx != nil { - log.Printf("error reading value from csv in line nr. %d (%s)", linenr, errx) + logplus.LogFError("error reading value from csv in line nr. %d (%s)", linenr, errx) } if erry != nil { - log.Printf("error reading value from csv in line nr. %d (%s)", linenr, erry) + logplus.LogFError("error reading value from csv in line nr. %d (%s)", linenr, erry) } - // TODO: Export the code below to its own function - var mass float64 = float64(10000 + rand.Intn(100000-10000)) - // Create a temporary star for assembling the star - tempStar2D := structs.Star2D{ - structs.Coord{X: x, Y: y}, - structs.Force{X: 0, Y: 0}, - mass, + tempStar := structs.Star2D{ + C: structs.Vec2{X: x, Y: y}, + M: 50000, } // Add the Temporary star to the slice - slice = append(slice, tempStar2D) + slice = append(slice, tempStar) } diff --git a/draw/draw.go b/draw/draw.go index 1823493..a5d5f0d 100644 --- a/draw/draw.go +++ b/draw/draw.go @@ -33,41 +33,32 @@ func saveImage(dc *gg.Context, path string) { dc.SavePNG(path) } -// drawStar2D draws the given stars to the given context -func drawStar2D(dc *gg.Context, star structs.Star2D) { - // the radius can be any value inbetween 1e4 and 1e5 - - // Define the default star radius as 1 and change it according to the stars mass - radius := 1 - switch { - case star.Mass < 10000: - radius = 1 - case star.Mass < 50000 && star.Mass > 10000: - radius = 2 - case star.Mass < 100000 && star.Mass > 50000: - radius = 3 +// drawStar draws the given stars to the given context +func drawStar(dc *gg.Context, star structs.Star2D) { + + // set radius of the star to 2 + R := 2.0 + + // if the star has a greater mass than the default 5E4, it's radius is increased to 5 + if star.M > 5E4 { + R = 5 } - // Draw the star - dc.DrawPoint(star.C.X/50, star.C.Y/50, float64(radius)) + // draw the star / point + dc.DrawPoint(star.C.X/50, star.C.Y/50, R) dc.Fill() dc.Stroke() } -// vectorLength calculates the length of the given vector -func vectorLength(force structs.Force) float64 { - return math.Sqrt(math.Pow(force.X, 2) + math.Pow(force.Y, 2)) -} - -func drawForce(dc *gg.Context, star structs.Star2D) { - // controll the length of the vector +func drawVelocity(dc *gg.Context, star structs.Star2D) { + // scaling factor for a better view of the velocity difference var scalingFactor float64 = 15 // Move the "cursor" to the start position of the vector dc.MoveTo(star.C.X/50, star.C.Y/50) // calculate the length of the vector - vecLength := vectorLength(star.F) + vecLength := star.V.GetLength() // Use a sigmoid function to generate useful values for coloring the vectors according to their // strength @@ -76,31 +67,31 @@ func drawForce(dc *gg.Context, star structs.Star2D) { // Set the color to a blue / red dc.SetRGB(val, 0, 1-val) - // trace the Vector - FxUnit := star.F.X / math.Abs(vecLength) - FyUnit := star.F.Y / math.Abs(vecLength) + // calculate the direction vector + FUnit := (&star.V).Divide(vecLength) - dc.LineTo(star.C.X/50+(FxUnit*scalingFactor), star.C.Y/50+(FyUnit*scalingFactor)) + // set end-position of the vector line + dc.LineTo(star.C.X/50+(FUnit.X*scalingFactor), star.C.Y/50+(FUnit.Y*scalingFactor)) - // css + // set line width dc.SetLineWidth(3) // And finally: DRAW (stroke) the vector dc.Stroke() } -// drawStar2Ds draws all the stars in the given slice to the given context -func drawStars2D(dc *gg.Context, slice []structs.Star2D) { - // draw all the forces in the given slice +// drawStars draws all the stars in the given slice to the given context +func drawStars(dc *gg.Context, slice []structs.Star2D) { + // draw all the velocity in the given slice for _, star := range slice { - drawForce(dc, star) + drawVelocity(dc, star) } dc.SetRGB(1, 1, 1) // draw all the stars in the given slice for _, star := range slice { - drawStar2D(dc, star) + drawStar(dc, star) } } @@ -111,7 +102,17 @@ func Slice(slice []structs.Star2D, path string) { dc := initializePlot() // draw all the stars in the given slice - drawStars2D(dc, slice) + drawStars(dc, slice) + + dc.SetRGB(1, 1, 1) + + // drawing the 4 big stars as bigger white dots + //dc.DrawCircle(600, 600, 5) + //dc.DrawCircle(-600, 600, 5) + //dc.DrawCircle(-600, 0, 5) + //dc.DrawCircle(600, -600, 5) + + dc.Fill() // save the plot to the given path saveImage(dc, path) diff --git a/file/file.go b/file/file.go new file mode 100644 index 0000000..ce6ac9c --- /dev/null +++ b/file/file.go @@ -0,0 +1,31 @@ +package file + +import ( + "encoding/csv" + "git.darknebu.la/bit/logplus" + "os" +) + +type File struct { + f *os.File +} + +func Open(path string) (File, error) { + file, err := os.Open(path) + if err != nil { + logplus.LogFError("openStarsCSV Panic! (cannot read file from %s)", path) + } + return File{f: file}, err +} + +func (file *File) ReadCSV() ([][]string, error) { + lines, err := csv.NewReader(file.f).ReadAll() + if err != nil { + logplus.LogError("openStarsCSV Panic! (cannot read the files content)") + } + return lines, err +} + +func (file *File) Close() error { + return file.f.Close() +} diff --git a/forces/forces.go b/forces/forces.go index 7763f87..a994334 100644 --- a/forces/forces.go +++ b/forces/forces.go @@ -1,87 +1,81 @@ package forces import ( - "../llog" "../structs" "fmt" + "git.darknebu.la/bit/logplus" "gopkg.in/cheggaaa/pb.v1" "math" ) // forces_acting calculates the force inbetween the two given stars s1 and s2 // The function return the force -func forceActing(s1 structs.Star2D, s2 structs.Star2D) structs.Force { +func accelerationActing(s1 structs.Star2D, s2 structs.Star2D) structs.Vec2 { + // Gravitational constant - var G float64 = 6.674e-11 + var G = 6.674E-11 - // Distance between the stars - var r21 = math.Sqrt(math.Pow(s2.C.X-s1.C.X, 2) + math.Pow(s2.C.Y-s1.C.Y, 2)) + // the vector from star s1 to star s2 + var r12 = s2.C.Subtract(s1.C) - // Unit vector pointing from s1 to s2 - rhat := structs.Force{s2.C.X - s1.C.X, s2.C.Y - s1.C.Y} + // the distance between the stars + var deltaR = r12.GetLength() - // Calculate how strong the star is affected - var FScalar = G * (s1.Mass * s2.Mass) / math.Pow(math.Abs(r21), 2) + // the scalar acceleration of star s1 by star s2 + scalarA := G * (s2.M) / math.Pow(deltaR, 2) - // Calculate the overall force by combining the scalar and the vector - var Fx = FScalar * rhat.X - var Fy = FScalar * rhat.Y + // calculate the direction vector of the vector from s1 to s2 + directionVectorA := r12.GetDirVector() - // Pack the forces in a force structur - F := structs.Force{Fx, Fy} + // the vector acceleration of scalarA + A := directionVectorA.Multiply(scalarA) - return F + return A } -// forces calculates the forces acting in between a given star and all the other stars in a given array. -func forces(stars_arr []structs.Star2D, nr int) structs.Force { +// accelerations calculates the acceleration acting in between a given star and all the other stars in a given array. +func accelerations(stars_arr []structs.Star2D, nr int) structs.Vec2 { - var force structs.Force + var a = /*acceleration*/ structs.Vec2{} // Iterate over all the stars in the stars_arr for index := range stars_arr { // If the current star is not the star itself if index != nr { - // generate a new force and add it to the overall force of the star - fa := forceActing(stars_arr[nr], stars_arr[index]) - stars_arr[nr].F.X += fa.X - stars_arr[nr].F.Y += fa.Y + // calculate the acceleration and add it to the overall acceleration of the star + aa := accelerationActing(stars_arr[nr], stars_arr[index]) + a = a.Add(aa) - force.X += fa.X - force.Y += fa.Y } } - return force + return a } -// forcesThread calculates the forces acting on a given amount of stars in a given range for a given slice of stars +// accelerationThread calculates the acceleration acting on a given amount of stars in a given range for a given slice of stars // as a go-routine -func forcesThread(starSlice []structs.Star2D, localRangeStar2Dt int, localRangeEnd int, channel chan structs.Star2D) { +func accelerationThread(starSlice []structs.Star2D, localRangeStart int, localRangeEnd int, channel chan structs.Star2D) { // iterate over the given range - for index := localRangeStar2Dt; index < localRangeEnd; index++ { + for index := localRangeStart; index < localRangeEnd; index++ { - // Calculate the force acting inbetween the given star and all other stars - var force = forces(starSlice, index) + // Calculate the acceleration acting inbetween the given star and all other stars + var a = accelerations(starSlice, index) // create a new star - newStar2D := structs.Star2D{ - structs.Coord{starSlice[index].C.X, starSlice[index].C.Y}, - structs.Force{force.X, force.Y}, - starSlice[index].Mass, - } + newStar := starSlice[index].Copy() + newStar.AccelerateVelocity(a, 1) - // push the new Star2D into the channel - channel <- newStar2D + // push the new Star into the channel + channel <- newStar } } -// CalcAllForces calculates all the forces acting inbetween all the stars in the given starSlice slice and -// returns a "new" slice contaning the forces -func CalcAllForces(starSlice []structs.Star2D, threads int) []structs.Star2D { - // create a channel for bundling the stars generaten in the go-routines +// CalcAllAccelerations calculates all the accelerations acting in between all the stars in the given starSlice slice and +// returns a "new" slice containing the stars with their new velocities +func CalcAllAccelerations(starSlice []structs.Star2D, threads int) []structs.Star2D { + // create a channel for bundling the stars generated in the go-routines channel := make(chan structs.Star2D, 1000) sliceLength := len(starSlice) @@ -93,43 +87,47 @@ func CalcAllForces(starSlice []structs.Star2D, threads int) []structs.Star2D { // generate a new slice for storing the stars var newSlice []structs.Star2D - llog.Good(fmt.Sprintf("Starting %d workers, each processing %d stars", threads, localRangeLen)) + logplus.LogNeutral(fmt.Sprintf("Starting %d workers, each processing %d stars", threads, localRangeLen)) // start n go threads for i := 0; i < threads; i++ { // define the local range - localRangeStar2Dt := i * localRangeLen + localRangeStart := i * localRangeLen localRangeEnd := (i * localRangeLen) + localRangeLen - // calculate the forces for all the stars in the given slice in the given range and return them using the + // fmt.Printf("starting worker nr. %d, processing %d stars\n", i, localRangeEnd-localRangeStart) + + // calculate the accelerations for all the stars in the given slice in the given range and return them using the // given channel - go forcesThread(starSlice, localRangeStar2Dt, localRangeEnd, channel) + go accelerationThread(starSlice, localRangeStart, localRangeEnd, channel) } // Handle errors (10004 stars, but 1250 stars per thread, so 4 stars are not calculate and block the queue) if sliceLength > localRangeLen { // Calculate the amount of stars and their range - remainingStar2Ds := sliceLength - (localRangeLen * threads) + remainingStars := sliceLength - (localRangeLen * threads) localRangeEnd := ((threads - 1) * localRangeLen) + localRangeLen // Run the Thread - // go forcesThread(starSlice, localRangeEnd, localRangeEnd+remainingStar2Ds, channel) - forcesThread(starSlice, localRangeEnd, localRangeEnd+remainingStar2Ds, channel) + // go accelerationThread(starSlice, localRangeEnd, localRangeEnd+remainingStars, channel) + accelerationThread(starSlice, localRangeEnd, localRangeEnd+remainingStars, channel) } // Initialize a new progress bar - bar := pb.New(len(starSlice)).Prefix("Star2Ds: ") + bar := pb.New(len(starSlice)).Prefix("Stars: ") + + bar.Start() // iterate over the amount of stars for i := 0; i < sliceLength; i++ { // block until a star is finisehd - var newStar2D structs.Star2D = <-channel + var newStar = <-channel // append the star from the channel to the newSlice for returning in the end - newSlice = append(newSlice, newStar2D) + newSlice = append(newSlice, newStar) // increment the progress bar and the counter bar.Increment() @@ -141,26 +139,19 @@ func CalcAllForces(starSlice []structs.Star2D, threads int) []structs.Star2D { } // Calculate the new positions of the stars using the -func NextTimestep(starSlice []structs.Star2D, deltat int) []structs.Star2D { +func NextTimestep(starSlice []structs.Star2D, deltat float64) []structs.Star2D { // create a new slice for storing the "new" stars var newSlice []structs.Star2D // iterate over all the stars in the old slice for index := range starSlice { - // calculate the new position - newX := starSlice[index].C.X + starSlice[index].F.X*float64(deltat) - newY := starSlice[index].C.Y + starSlice[index].F.Y*float64(deltat) - - // assemble the new star - newStar2D := structs.Star2D{ - C: structs.Coord{X: newX, Y: newY}, - F: structs.Force{}, - Mass: starSlice[index].Mass, - } + // move the star with it's velocity for time deltat + newStar := starSlice[index].Copy() + newStar.Move(deltat) // append the new star to the newSlice - newSlice = append(newSlice, newStar2D) + newSlice = append(newSlice, newStar) } return newSlice diff --git a/main.go b/main.go index 0ad2e69..7aa389f 100644 --- a/main.go +++ b/main.go @@ -7,37 +7,40 @@ import ( "./structs" "fmt" "git.darknebu.la/bit/logplus" + "math" ) func main() { + logplus.SetLogLevel(logplus.LevelAll) var threads int = 8 + var path1 string = "out_0.png" var frames int = 1 - var stars int = 800 // the slice starsSlice stores the star structures - starsSlice := []structs.Star2D{} + starsSlice := []structs.Star2D{ + // {C: structs.Vec2{X: 30000, Y: 30000},M: 5E8}, + // {C: structs.Vec2{X: -30000, Y: 30000},M: 5E8}, + // {C: structs.Vec2{X: -30000 },M: 5E8}, + // {C: structs.Vec2{X: 30000, Y: -30000},M: 5E8}, + {C: structs.Vec2{}, M: 5E8}, + } - logplus.LogPositive("Opening the csv") - starsSlice = csv.Import("data/U_ALL.csv", 0, stars, starsSlice) + logplus.LogNeutral("Opening the csv") + starsSlice = csv.Import("data/U_ALL.csv", 0, 25000, starsSlice) // Simulate the position of the stars after a specific time for i := 0; i < frames; i++ { - - // Print the iterator logplus.LogPositive("--- --- --- --- ---") logplus.LogPositive(fmt.Sprintf("Frames %d/%d", i, frames)) - // Calculate the position of stars after one timestep - logplus.LogPositive("Calculate the new Star positions") - starsSlice = forces.NextTimestep(starsSlice, 500000) + logplus.LogNeutral("Calculate the new Star positions") + starsSlice = forces.NextTimestep(starsSlice, 25*math.Pow(10, 4+7)) - // Calculate all the forces acting inside of the galaxy - logplus.LogPositive("Calculate the acting forces") - starsSlice = forces.CalcAllForces(starsSlice, threads) + logplus.LogNeutral("Calculate the acting accelerations") + starsSlice = forces.CalcAllAccelerations(starsSlice, threads) - // Save the slice outputName := fmt.Sprintf("out_%d.png", i+1) - logplus.LogPositive(fmt.Sprintf("draw the slice and save it to %s\n", outputName)) + logplus.LogNeutral(fmt.Sprintf("draw the slice and save it to %s\n", outputName)) draw.Slice(starsSlice, outputName) } } diff --git a/out_0.png b/out_0.png new file mode 100644 index 0000000..830315d Binary files /dev/null and b/out_0.png differ diff --git a/structs/star.go b/structs/star.go new file mode 100644 index 0000000..72afc00 --- /dev/null +++ b/structs/star.go @@ -0,0 +1,29 @@ +package structs + +import "C" + +type Star2D struct { + C Vec2 // coordinates of the star + V Vec2 // velocity of the star + M float64 // mass of the star +} + +func (s *Star2D) Copy() Star2D { + return Star2D{s.C.Copy(), s.V.Copy(), s.M} +} + +// accelerate the star with the acceleration a for the time t +func (s *Star2D) AccelerateVelocity(a Vec2, t float64) { + s.V = s.V.Add(a.Multiply(t)) +} + +// move the star with it's velocity for the time t +func (s *Star2D) Move(t float64) { + s.C = s.C.Add(s.V.Multiply(t)) +} + +// accelerate and move the star with it's velocity and the acceleration a for the time t +func (s *Star2D) Accelerate(a Vec2, t float64) { + s.AccelerateVelocity(a, t) + s.Move(t) +} diff --git a/structs/structs.go b/structs/structs.go deleted file mode 100644 index ed70a84..0000000 --- a/structs/structs.go +++ /dev/null @@ -1,23 +0,0 @@ -package structs - -/* - The structs package defines structs that are used to store information that is used for processing - star related data. -*/ - -// Force struct soring a force vector -type Force struct { - X, Y float64 -} - -// Coord struct storing coordinates -type Coord struct { - X, Y float64 -} - -// Star struct storing all necessary star information -type Star2D struct { - C Coord - F Force - Mass float64 -} diff --git a/structs/vector2D.go b/structs/vector2D.go new file mode 100644 index 0000000..47e457f --- /dev/null +++ b/structs/vector2D.go @@ -0,0 +1,63 @@ +package structs + +import ( + "math" +) + +type Vec2 struct { + X, Y float64 +} + +// creates a copy of the vector +func (v *Vec2) Copy() Vec2 { + return Vec2{v.X, v.Y} +} + +func (v *Vec2) Split() (x float64, y float64) { + return v.X, v.Y +} + +// changes the length of the vector to the length l +func (v *Vec2) SetLength(l float64) { + var k = l / v.GetLength() + var newV = v.Multiply(k) + // v = newV + v.X, v.Y = newV.Split() +} + +// changes the length of the vector to the length 1 +func (v *Vec2) SetLengthOne() { + v.SetLength(1) +} + +// returns the direction Vector of this vector. This means a copy of this vector with a length of 1 +func (v *Vec2) GetDirVector() Vec2 { + var dirV = v.Copy() + dirV.SetLengthOne() + return dirV +} + +// returns the length of the vector +func (v *Vec2) GetLength() float64 { + return math.Sqrt(math.Pow(v.X, 2) + math.Pow(v.Y, 2)) +} + +// returns the product of the vector and a scalar s +func (v *Vec2) Multiply(s float64) Vec2 { + return Vec2{v.X * s, v.Y * s} +} + +// returns the quotient of the vector and a scalar s +func (v *Vec2) Divide(s float64) Vec2 { + return Vec2{v.X / s, v.Y / s} +} + +// returns the sum of this vector and the vector v2 +func (v1 *Vec2) Add(v2 Vec2) Vec2 { + return Vec2{v1.X + v2.X, v1.Y + v2.Y} +} + +// returns the difference of this vector minus the vector v2 +func (v1 *Vec2) Subtract(v2 Vec2) Vec2 { + return Vec2{v1.X - v2.X, v1.Y - v2.Y} +} -- cgit 1.4.1