// TimeStepParallel does the same as TimeStep but with channels func TimeStepParallel(R, V [][3]float64, L, M, h float64) ([][3]float64, [][3]float64) { N := len(R) A := make([][3]float64, N) nR := make([][3]float64, N) nV := make([][3]float64, N) c := make(chan ForceReturn, N) for i := 0; i < N; i++ { go InternalForceParallel(i, R, L, c) } for n := 0; n < N; n++ { info := <-c i := info.i Fi := info.F A[i] = vector.Scale(Fi, 1.0/M) nR[i] = space.PutInBox(verlet.NextR(R[i], V[i], A[i], h), L) } for i := 0; i < N; i++ { go InternalForceParallel(i, nR, L, c) } for n := 0; n < N; n++ { info := <-c i := info.i nFi := info.F nAi := vector.Scale(nFi, 1.0/M) nV[i] = verlet.NextV(V[i], A[i], nAi, h) } return nR, nV }
// InitPositionFCC initializes particle positions in a face-centered cubic configuration func InitPositionFCC(N int, L float64) [][3]float64 { R := make([][3]float64, N) Ncube := 1 for N > 4*Ncube*Ncube*Ncube { Ncube++ } o := -L / 2 origin := [3]float64{o, o, o} rs := L / float64(Ncube) roffset := rs / 2 i := 0 for x := 0; x < Ncube; x++ { x := float64(x) for y := 0; y < Ncube; y++ { y := float64(y) for z := 0; z < Ncube; z++ { z := float64(z) pos := vector.Scale([3]float64{x, y, z}, rs) pos = vector.Sum(pos, origin) R[i] = pos i++ R[i] = vector.Sum(pos, [3]float64{roffset, roffset, 0}) i++ R[i] = vector.Sum(pos, [3]float64{roffset, 0, roffset}) i++ R[i] = vector.Sum(pos, [3]float64{0, roffset, roffset}) i++ } } } return R }
// TimeStep evolves the system by one unit of time using the Velocity Verlet algorithm for molecular dynamics. func TimeStep(R, V [][3]float64, L, M, h float64) ([][3]float64, [][3]float64) { N := len(R) A := make([][3]float64, N) nR := make([][3]float64, N) nV := make([][3]float64, N) for i := 0; i < N; i++ { Fi := InternalForce(i, R, L) A[i] = vector.Scale(Fi, 1.0/M) nR[i] = space.PutInBox(verlet.NextR(R[i], V[i], A[i], h), L) } for i := 0; i < N; i++ { nFi := InternalForce(i, nR, L) nAi := vector.Scale(nFi, 1.0/M) nV[i] = verlet.NextV(V[i], A[i], nAi, h) } return nR, nV }
// PairwiseLennardJonesForce calculates the force vector on particle Ri due to Rj using the Lennard Jones potential. func PairwiseLennardJonesForce(Ri, Rj [3]float64, L float64) [3]float64 { if space.PointsAreEqual(Ri, Rj, L) { panic(fmt.Sprintf("%v and %v are equal, the pairwise force is infinite", Ri, Rj)) } r := space.Displacement(Ri, Rj, L) magR := vector.Length(r) f := 4 * (-12*math.Pow(magR, -13) + 6*math.Pow(magR, -7)) return vector.Scale(r, f/magR) }
// InitVelocity initializes particle velocities selected from a random distribution. // Ensures that the net momentum of the system is zero and scales the average kinetic energy to match a given temperature. func InitVelocity(N int, T0 float64, M float64) [][3]float64 { V := make([][3]float64, N) rand.Seed(1) netP := [3]float64{0, 0, 0} netE := 0.0 for n := 0; n < N; n++ { for i := 0; i < 3; i++ { newP := rand.Float64() - 0.5 netP[i] += newP netE += newP * newP V[n][i] = newP } } netP = vector.Scale(netP, 1.0/float64(N)) vscale := math.Sqrt(3.0 * float64(N) * T0 / (M * netE)) for i, v := range V { correctedV := vector.Scale(vector.Difference(v, netP), vscale) V[i] = correctedV } return V }
// InitPositionCubic initializes particle positions in a simple cubic configuration. func InitPositionCubic(N int, L float64) [][3]float64 { R := make([][3]float64, N) Ncube := 1 for N > Ncube*Ncube*Ncube { Ncube++ } rs := L / float64(Ncube) roffset := (L - rs) / 2 i := 0 for x := 0; x < Ncube; x++ { x := float64(x) for y := 0; y < Ncube; y++ { y := float64(y) for z := 0; z < Ncube; z++ { z := float64(z) pos := vector.Scale([3]float64{x, y, z}, rs) offset := [3]float64{roffset, roffset, roffset} R[i] = vector.Difference(pos, offset) i++ } } } return R }
// NextR calculates the next position vector based on current position, velocity, and acceleration. func NextR(r, v, a [3]float64, h float64) [3]float64 { return vector.Sum(vector.Sum(r, vector.Scale(v, h)), vector.Scale(a, 0.5*h*h)) }
// NextV calculates the next velocity vector based on current velocity and acceleration and future acceleration. func NextV(v, a1, a2 [3]float64, h float64) [3]float64 { return vector.Sum(v, vector.Scale(vector.Sum(a1, a2), 0.5*h)) }