1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
package forces
import (
"../llog"
"../structs"
"fmt"
"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.Star, s2 structs.Star) structs.Force {
// Gravitational constant
// var G = 6.674 * math.Pow(10, -11)
var G float64 = 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))
// Unit vector pointing from s1 to s2
rhat := structs.Force{s2.C.X - s1.C.X, s2.C.Y - s1.C.Y}
// Calculate how strong the star is affected
var FScalar = G * (s1.Mass * s2.Mass) / math.Pow(math.Abs(r21), 2)
// Calculate the overall force by combining the scalar and the vector
var Fx = FScalar * rhat.X
var Fy = FScalar * rhat.Y
// Pack the forces in a force structur
F := structs.Force{Fx, Fy}
return F
}
// forces calculates the forces acting in between a given star and all the other stars in a given array.
func forces(stars_arr []structs.Star, nr int) structs.Force {
var force structs.Force
// 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
force.X += fa.X
force.Y += fa.Y
}
}
return force
}
// forcesThread calculates the forces 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.Star, localRangeStart int, localRangeEnd int, channel chan structs.Star) {
// iterate over the given range
for index := localRangeStart; index < localRangeEnd; index++ {
// Calculate the force acting inbetween the given star and all other stars
var force = forces(starSlice, index)
// create a new star
newStar := structs.Star{
structs.Coord{starSlice[index].C.X, starSlice[index].C.Y},
structs.Force{force.X, force.Y},
starSlice[index].Mass,
}
// 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.Star, threads int) []structs.Star {
// create a channel for bundling the stars generaten in the go-routines
channel := make(chan structs.Star, 1000)
sliceLength := len(starSlice)
// calculate the local range
// Example: 100 stars with 4 threads = 25 stars / thread
localRangeLen := sliceLength / threads
// generate a new slice for storing the stars
var newSlice []structs.Star
llog.Good(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
localRangeStart := i * localRangeLen
localRangeEnd := (i * localRangeLen) + localRangeLen
// fmt.Printf("starting worker nr. %d, processing %d stars\n", i, localRangeEnd-localRangeStart)
// calculate the forces for all the stars in the given slice in the given range and return them using the
// given channel
go forcesThread(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
remainingStars := sliceLength - (localRangeLen * threads)
localRangeEnd := ((threads - 1) * localRangeLen) + localRangeLen
// Run the Thread
// go forcesThread(starSlice, localRangeEnd, localRangeEnd+remainingStars, channel)
forcesThread(starSlice, localRangeEnd, localRangeEnd+remainingStars, channel)
}
// Initialize a new progress bar
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 newStar structs.Star = <-channel
// append the star from the channel to the newSlice for returning in the end
newSlice = append(newSlice, newStar)
// increment the progress bar and the counter
bar.Increment()
}
bar.Finish()
return newSlice
}
// Calculate the new positions of the stars using the
func NextTimestep(starSlice []structs.Star, deltat int) []structs.Star {
// create a new slice for storing the "new" stars
var newSlice []structs.Star
// 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
newStar := structs.Star{
C: structs.Coord{X: newX, Y: newY},
F: structs.Force{},
Mass: 50000,
}
// append the new star to the newSlice
newSlice = append(newSlice, newStar)
}
return newSlice
}
|