© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2021
M. Cywiak, D. CywiakMulti-Platform Graphics Programming with Kivyhttps://doi.org/10.1007/978-1-4842-7113-1_14

14. Stereoscopic Simple Numerical Method for Gravitational N-body

Moisés Cywiak1   and David Cywiak2
(1)
Leon, Guanajuato, Mexico
(2)
Queretaro, Mexico
 
In this chapter, we provide a simple numerical approach to the gravitational N-body problem to be observed in a stereoscopic view. A screenshot of the program running on an Android cell phone is shown in Figure 14-1.
../images/510726_1_En_14_Chapter/510726_1_En_14_Fig1_HTML.jpg
Figure 14-1

Screenshot of the program running on an Android cell phone

The functionality of the buttons is described as follows.

Button1 shows the scene. It does not suspend calculations. Button2 stops the calculations, clears the scene, and displays a text message on the screen. Button3 reactivates computations to show the evolution of the masses orbiting in real-time. Finally, Button4 suspends the calculations so that the last scene remains on the screen.

In the following section, we describe the N-body problem.

14.1 The Gravitational N-body Problem

The gravitational N-body-problem consists of determining the dynamic parameters of N masses whose interaction is described by Newton’s gravitational law. Let’s exemplify the physical situation by considering three of these masses in a three-dimensional Cartesian coordinate system, depicted in Figure 14-2.
../images/510726_1_En_14_Chapter/510726_1_En_14_Fig2_HTML.jpg
Figure 14-2

Three punctual masses and their corresponding position vectors in a three-dimensional Cartesian coordinate system

Figure 14-2 depicts three punctual masses, M1, M2, and M3, pointed by position vectors r1, r2, and r3, respectively. The relative position vectors between them are r1, 2r1, 3, and r2, 3.

Let i, j, and k represent the unit director vectors in the x-, y-, and z-directions, respectively. Then, each position vector in Figure 14-2 can be expressed as follows:
$$ {r}_n={x}_ni+{y}_nj+{z}_nk $$
(14.1)

In Equation (14.1), the value of n ranges between 1 and 3.

Analogously, the relative position vectors between masse, can be written as follows:
$$ {r}_{n,m}=left({x}_m-{x}_n
ight)i+left({y}_m-{y}_n
ight)j+left({z}_m-{z}_n
ight)k $$
(14.2)

In Equation (14.2) n, m can take the values 1, 2, 3, and n ≠ m.

In the gravitational N-body problem, we assume that the masses are in a three-dimensional free space. No other forces are acting on them except for the gravitational forces between them. For example, mass M1 interacts only with masses M2 and M3 through Newton’s gravitational law. Therefore, to calculate the total force exerted on mass M1, we have to sum the forces due to M2 and M3 on M1. The analytical equation to calculate this force is written as follows:
$$ {F}_1=G {M}_1{M}_2frac{r_{1,2}}{{leftVert {r}_{1,2}
ightVert}^{{}^3}}+G {M}_1{M}_3frac{r_{1,3}}{{leftVert {r}_{1,3}
ightVert}^{{}^3}} $$
(14.3)
In Equation (14.3), F1 represents the total force acting on mass M1. We emphasize that the forces between masses are attractive. The ‖ ‖3 symbol represents the cube of the magnitude of a vector. For example, for the case of vector r1, 2, we can write the following:
$$ {leftVert {r}_{1,2}
ightVert}^{{}^3}={left[left({x}_2-{x}_1
ight)2+{left({y}_2-{y}_1
ight)}^{{}^2}+{left({z}_2-{z}_1
ight)}^{{}^2}
ight]}^{3/2} $$
(14.4)
For two masses, Mn and Mm, the general expression of Equation (14.4) can be written as follows:
$$ {leftVert {r}_{n,m}
ightVert}^{{}^3}={left[{left({x}_m-{x}_n
ight)}^{{}^2}+{left({y}_m-{y}_n
ight)}^{{}^2}+{left({z}_m-{z}_n
ight)}^{{}^2}
ight]}^{3/2} $$
(14.5)

In Equation (14.5), m ≠ n should hold; otherwise, we would have the trivial case in which the distance from one particle to itself is zero.

Now that the net force acting on M1 has been calculated, we apply Newton’s law of motion to this mass, as follows.
$$ {F}_1={M}_1frac{d^{{}^2}{r}_1(t)}{dt^{{}^2}} $$
(14.6)

In Equation (14.6), we have explicitly expressed r1 as a function of time. The second derivative of this vector gives the three components of the acceleration vector that correspond to M1.

14.2 Motion Equations

We will now focus on calculating the dynamic conditions of M1. Equating Equations (14.3) and (14.6) gives this:
$$ frac{d^{{}^2}{r}_1(t)}{dt^{{}^2}}=G {M}_2frac{r_{1,2}}{{leftVert {r}_{1,2}
ightVert}^{{}^3}}+G {M}_3frac{r_{1,3}}{{leftVert {r}_{1,3}
ightVert}^{{}^3}} $$
(14.7)

In Equation (14.7), the second derivative of the position vector represents the acceleration vector acting on M1. For programming purposes, it will be useful to note that this expression is independent of M1. Therefore, we conclude that, in general, the formula for calculating the acceleration vector acting on a given mass is a function of all the others present in the system and, in turn, independent of its value.

We now express vector Equation (14.7) in terms of their spatial components, as follows:
$$ frac{d^{{}^2}{x}_1(t)}{dt^{{}^2}}=frac{G {M}_2left({x}_2-{x}_1
ight)}{{left({left({x}_2-{x}_1
ight)}^{{}^2}+{left({y}_2-{y}_1
ight)}^{{}^2}+{left({z}_2-{z}_1
ight)}^{{}^2}
ight)}^{frac{3}{2}}}+frac{G {M}_3left({x}_3-{x}_1
ight)}{{left({left({x}_3-{x}_1
ight)}^{{}^2}+{left({y}_3-{y}_1
ight)}^{{}^2}+{left({z}_3-{z}_1
ight)}^{{}^2}
ight)}^{frac{3}{2}}} $$
(14.8)
$$ frac{d^{{}^2}{y}_1(t)}{dt^{{}^2}}=frac{G {M}_2left({y}_2-{y}_1
ight)}{{left({left({x}_2-{x}_1
ight)}^{{}^2}+{left({y}_2-{y}_1
ight)}^{{}^2}+{left({z}_2-{z}_1
ight)}^{{}^2}
ight)}^{frac{3}{2}}}+frac{G {M}_3left({y}_3-{y}_1
ight)}{{left({left({x}_3-{x}_1
ight)}^{{}^2}+{left({y}_3-{y}_1
ight)}^{{}^2}+{left({z}_3-{z}_1
ight)}^{{}^2}
ight)}^{frac{3}{2}}} $$
(14.9)
$$ frac{d^{{}^2}{z}_1(t)}{dt^{{}^2}}=frac{G {M}_2left({z}_2-{z}_1
ight)}{{left({left({x}_2-{x}_1
ight)}^{{}^2}+{left({y}_2-{y}_1
ight)}^{{}^2}+{left({z}_2-{z}_1
ight)}^{{}^2}
ight)}^{frac{3}{2}}}+frac{G {M}_3left({z}_3-{z}_1
ight)}{{left({left({x}_3-{x}_1
ight)}^{{}^2}+{left({y}_3-{y}_1
ight)}^{{}^2}+{left({z}_3-{z}_1
ight)}^{{}^2}
ight)}^{frac{3}{2}}} $$
(14.10)

Equations (14.8) through (14.10) are the dynamic equations for M1. Analog equations can be derived for M2 and M3. It is straightforward to extend this derivation to the case of N masses.

In the following section, we describe the method to numerically solve the dynamic equations.

14.3 Numerical Solution to the Dynamic Equations

We now continue with our example of three masses. We will numerically solve the dynamic equations that correspond to mass M1. For the description, let’s associate the following acceleration components to M1:
$$ {a}_{x1}=frac{d^{{}^2}{x}_1(t)}{dt^{{}^2}} $$
(14.11)
$$ {a}_{y1}=frac{d^{{}^2}{y}_1(t)}{dt^{{}^2}} $$
(14.12)
$$ {a}_{z1}=frac{d^{{}^2}{z}_1(t)}{dt^{{}^2}} $$
(14.13)
Now, let’s introduce the components of velocity of mass M1 as follows:
$$ {v}_{x1}(t)=frac{d{x}_1(t)}{dt}=frac{x_1left(t+varDelta t
ight)-{x}_1(t)}{varDelta t} $$
(14.14)
$$ {v}_{y1}(t)=frac{d{y}_1(t)}{dt}=frac{y_1left(t+varDelta t
ight)-{y}_1(t)}{varDelta t} $$
(14.15)
$$ {v}_{z1}(t)=frac{d{z}_1(t)}{dt}=frac{z_1left(t+varDelta t
ight)-{z}_1(t)}{varDelta t} $$
(14.16)

In writing Equations (14.14) through (14.16), we have approximated the derivatives using a small increment value, called Δt.

We will represent the path followed by mass M1 by the discrete sequence, x1(0), x1(1). . x1(n). Here, n corresponds to an integer. Equations (14.14)-(14.16) give the following recurrence equations:
$$ {x}_1left(n+1
ight)={x}_1(n)+{v}_{x1}(n)varDelta t $$
(14.17)
$$ {y}_1left(n+1
ight)={y}_1(n)+{v}_{y1}(n)varDelta t $$
(14.18)
$$ {z}_1left(n+1
ight)={z}_1(n)+{v}_{z1}(n)varDelta t $$
(14.19)

Now, if the initial spatial coordinates and component velocities at state n=0 are known, we can use Equations (14.17) through (14.19) to calculate the position of mass M1 at the state n=1. However, for the state n=2, we need the values of the component velocities of state n=1. These values can be calculated as follows.

First, we rewrite Equations (14.11) through (14.13) as follows:
$$ {a}_{x1}=frac{d^{{}^2}{x}_1(t)}{dt^{{}^2}}=frac{dv_{x1}}{dt}=frac{v_{x1}left(t+varDelta t
ight)-{v}_{x1}(t)}{varDelta t} $$
(14.20)
$$ {a}_{y1}=frac{d^{{}^2}{y}_1(t)}{dt^{{}^2}}=frac{dv_{y1}}{dt}=frac{v_{y1}left(t+varDelta t
ight)-{v}_{y1}(t)}{varDelta t} $$
(14.21)
$$ {a}_{z1}=frac{d^{{}^2}{z}_1(t)}{dt^{{}^2}}=frac{dv_{z1}}{dt}=frac{v_{z1}left(t+varDelta t
ight)-{v}_{z1}(t)}{varDelta t} $$
(14.22)
Equations (14.20) through (14.22) lead to this:
$$ {v}_{x1}left(n+1
ight)={v}_{x1}(n)+{a}_{x1}(n)varDelta t $$
(14.23)
$$ {v}_{y1}left(n+1
ight)={v}_{y1}(n)+{a}_{y1}(n)varDelta t $$
(14.24)
$$ {v}_{z1}left(n+1
ight)={v}_{z1}(n)+{a}_{z1}(n)varDelta t $$
(14.25)

In Equations (14.23) through (14.25), the values for the component accelerations in the n-state are calculated by means of Equations (14.8) through (14.10).

This procedure allows us to calculate the dynamic components of M1. To obtain the dynamic equations for the other masses, we proceed in a similar way. The method can be applied, in principle, to any number of masses.

In the following section, we describe the numerical approach to calculating the dynamic parameters of the N-body problem.

14.4 Capturing Numerical Data

To visualize how we perform the calculations, we present an example with five planets, which corresponds to the screenshot in Figure 14-1.

To track all of the parameters described in the previous section, we need a memory region for storing the values that correspond to the position, velocity, and acceleration components that correspond to each mass. We also need to track the three-dimensional values calculated with Equations (14.17) through (14.25). Finally, we also need the value of each mass. All this data can be manageable if we associate a one-dimensional array to each mass.

We use NumPy to create N one-dimensional arrays, with 16 entries each, to store all the values required for each punctual mass. The arrays will be numbered from 0 to N-1. The code corresponding to the case N=5 is shown in Listing 14-1.
import numpy as np
N=5; #Number of planets
# Array Planet:
# [x,y,z,  vx,vy,vz,  Mass,
# xP,yP,zP, vxP,vyP,vzP, ax,ay,az]
Planet=np.zeros( (N,16) );
Listing 14-1

Creating Array Planets

In this code, we refer to each array as a planet. In this example, we set the parameter N=5 to generate five planets in the system, each with 16 entries, as follows:
  • The first three entries, 0 to 2, correspond to the position of the planet, given by coordinates (x,y,z).

  • The second three entries, 3 to 5, correspond to the three velocity components, given by (vx,vy,vz).

  • Entry 6 corresponds to the mass of the planet.

  • Entries 7 to 9 correspond to the (xP, yP, zP) values calculated using Equations (14.17)-(14.19). These values will replace (x, y, z), entries 0 to 2. However, we will perform this action only after finishing all the calculations over all the planets. Otherwise, we would disturb the system, and the results would be incorrect.

  • Entries 10 to 12 store the new velocity components, (vxP,vyP,vzP), calculated with Equations (14.23)-(14.25). These values will replace the values stored in entries 3 to 5, after completing the calculations over all the planets.

Entries 13 to 15 store the three acceleration components, (ax,ay,az), calculated by Equations (14.8) through (14.10). These values will be used in Equations (14.23) through (14.25) to calculate the new velocity components.

This program will keep track of all the relative distances between the masses. To store them, we create a two-dimensional array of size NxN. However, Equation (14.7) suggests storing ‖rn, m³ instead of ‖rn, m‖ in the array. To illustrate how the array looks, let’s define Sn, m = ‖rn, m³. Table 14-1 depicts the array for the case N=5.
Table 14-1

5x5 Array Storing Sn, m Values for the Case N=5

mass

0

1

2

3

4

0

X

S0, 1

S0, 2

S0, 3

S0, 4

1

S1, 0

X

S

Sr1, 3

S1, 4

2

S2, 0

S2, 1

X

S2, 3

S2, 4

3

S3, 0

S3, 1

S3, 2

X

S3, 4

4

S4, 0

S4, 1

S4, 2

S4, 3

X

In Table 14-1, there are N=5 masses present in the system, numbered from 0 to 4. The 5x5 array stores all possible combinations of parameter Sn, m. Since Sn, m = Sm, n, the array stores redundant entries that we will keep to simplify the code. Additionally, we will not use the entries marked with the X symbol.

The following section discusses the working example.

14.5 Five Planets Working Example

In principle, there is no restriction on the number of punctual masses our N-body program can handle. In our working example, N=5.

The initial values that we provide to our planets must be based on our geometrical situation. We have assigned to the horizontal and vertical axes the coordinates (x and y), respectively. The z-axis corresponds to the depth.

For simplicity, in our working example, we will position the planets on the x-z plane. The initial velocities will only have components in the z-direction. Additionally, in trying to mimic the solar system, we will position one of the planets at the center and the rest of them will orbit around this one. The masses of the orbiting planets will be small compared with the one placed at the center.

As it is not simple to provide initial conditions that guarantee stable orbits, we will try some values obtained with the following simple approach. Given the fact that each orbiting mass follows a circular path, we will equate the centrifugal force corresponding to its gravitational one. The corresponding analytical expression reads as follows:
$$ frac{GM_0{M}_1}{r^{{}^2}}=frac{M_1{v}^{{}^2}}{r} $$
(14.26)

In writing Equation (14.26), we assume that M0 ≫ M1, and as mentioned, that the small mass orbits around the biggest one following approximately a circular path. The left side of the equation represents the attractive force between the two masses. The right side of the equation represents the centrifugal force acting on mass M1.

For our working example, we will normalize the value of the gravitational constant to G=1. Next, we will assign a large value to the mass at the center; in this example, 10.0x104. Next, we will provide some distances to the planets in the x-z plane. Finally, using Equation (14.26), we will provide initial velocities to the five planets. The corresponding code is shown in Listing 14-2.
import numpy as np
D=400; # Perspective distance
P=0.5; # Percentage distance from screen
N=5; # Number of planets
# Array Planet:
# [x,y,z,  vx,vy,vz,  Mass,
# xP,yP,zP, vxP,vyP,vzP, ax,ay,az]
Planet=np.zeros( (N,16) );
Planet[0]=(  0,0,P*D,    0,0,0,  10.0e4,
                         0,0,0,  0,0,0, 0,0,0  );
Planet[1]=( -40,0,P*D,   0,0,50,   14.0,
                         0,0,0,  0,0,0, 0,0,0  );
Planet[2]=(  40,0,P*D,   0,0,-50,  14.0,
                         0,0,0,  0,0,0, 0,0,0  );
Planet[3]=(  60,0,P*D,   0,0, -41,  8.0,
                         0,0,0,  0,0,0, 0,0,0  );
Planet[4]=(  -60,0,P*D,   0,0, 41,  8.0,
                         0,0,0,  0,0,0, 0,0,0  );
#Array to store distances between planets: r(n,m)
R=np.zeros( (N,N) );
Listing 14-2

Setting the Initial Conditions for the Planets

In this code, we have assigned to the percentage variable P the value 0.5, as in previous chapters. This value will place the initial z-coordinate of the planets centered between the screen and the plane of projection. The first planet, Planet[0], is placed at the center of the screen and it is initially static, with zero velocity. The other four, Planet[1] to Planet[4], have been assigned initial speeds different from zero. Their masses are small compared to Planet[0].

The NxN array, R, will store the relative distances between planets, each elevated to 3. The code that assigns the corresponding values is shown in Listing 14-3.
#Filling array R with values r(n,m)**3/2
    for n in range(0,N):
        for m in range(0,N):
            if (n>=m):
                continue;
            R[n][m]=((Planet[n][0]-Planet[m][0])**2
                    +(Planet[n][1]-Planet[m][1])**2
        +(Planet[n][2]-Planet[m][2])**2  )**(3/2);
            R[m][n]=R[n][m];
Listing 14-3

Calculating the Initial Distances Between Planets

The DrawPlanets(VX, VY, VZ, Which) function is in charge of drawing the planets. As in previous chapters, two PIL images are used for the stereoscopic scene. If the Which parameter is equal to 0, the left image is drawn in red using the left point of projection. If the Which parameter is equal to 1, the right point of projection is used and the right PIL image is colored in cyan. The function starts by reading the value stored in entry 6, which corresponds to the mass. If the mass is greater than 1000, a value equal to 44 is assigned to the local variable M; otherwise, this variable takes the integer value of the mass. The variable M is used to indicate the radius of the circles, depending on the mass of the planet. The directive that draws a planet looks as follows:
Draw.ellipse( (x1-M,y1-M,x1+M,y1+M),  fill=(r,g,b) )

This directive draws a circle with radius equal to M, filled with the corresponding red or cyan color.

The code for drawing the planets is shown in Listing 14-4.
Factor0 =(D-VZ1)/(D/2-VZ1);
N=5; D=400;
for n in range(0,N):
    M=int(Planet[n][6]);
    if (M>1000):
        M=44;
    else:
        M=int(M);
    Factor=(D-VZ)/(D-Planet[n][2]-VZ);
    x1=XC+Factor*(Planet[n][0]-VX)+Factor0*VX;
    y1=YC-Factor*(Planet[n][1]-VY)-Factor0*VY;
    Draw.ellipse( (x1-M,y1-M,x1+M,y1+M),
        fill=(r,g,b));
Listing 14-4

Code for Representing Planets with Ellipses

In this code, the function utilizes the coordinates of one of the two points of projection given in (VX, VY, VZ) together with the x-, y-, and z-coordinates available in the Planet array to draw the corresponding circle in Draw1 or Draw2, according to the value of the parameter Which.

The MovePlanets() function , at every clock tick, updates all the parameters of the planets. The function uses a normalized gravitational constant with a value of 1 and a positive parameter dt with a value equal to 0.02. This parameter corresponds to Δt in the analytical equations in Section 14.3.

The value of dt must be as small as possible to attain accurate solutions of the differential equations. However, lowering this value too much will reduce the rate of change in the planet’s position, so that the displacements may not be appreciated.

The first task for MovePlanets() is to calculate the values of the new positions using Equations (14.17) through (14.19) and then store them in their corresponding array. The code is shown in Listing 14-5.
      for n in range(0,N):
        Planet[n][7]=Planet[n][0]+Planet[n][3]*dt;
        Planet[n][8]=Planet[n][1]+Planet[n][4]*dt;
        Planet[n][9]=Planet[n][2]+Planet[n][5]*dt;
Listing 14-5

Calculating Planets with the New Position

MovePlanets() now calculates all the distances between planets. According to Equations (14.8) through (14.10), the values that will be stored in the R array correspond to ‖rn, m3. The code is shown in Listing 14-6.
#Filling array R with values r(n,m)**3/2
    for n in range(0,N):
        for m in range(0,N):
            if (n>=m):
                continue;
            R[n][m]=((Planet[n][0]-Planet[m][0])**2
                    +(Planet[n][1]-Planet[m][1])**2
        +(Planet[n][2]-Planet[m][2])**2  )**(3/2);
            R[m][n]=R[n][m];
Listing 14-6

Calculating Dynamic Distances Between Planets

MovePlanets() will now use Equations (14.8) through (14.10) to calculate the three acceleration components for each planet. The calculations require us to sum the contributions over all the planets. The corresponding code is shown in Listing 14-7.
    for n in range(0,N):
        Planet[n][13]=0;
        Planet[n][14]=0;
        Planet[n][15]=0;
        for m in range(0,N):
            if (n==m):
                continue;
            Planet[n][13]=Planet[n][13]
                           + G*Planet[m][6]
               *(Planet[m][0]-Planet[n][0])/R[m][n];
            Planet[n][14]=Planet[n][14]
                           + G*Planet[m][6]
                *(Planet[m][1]-Planet[n][1])/R[m][n];
            Planet[n][15]=Planet[n][15]
                + G*Planet[m][6]
                *(Planet[m][2]-Planet[n][2])/R[m][n];
Listing 14-7

Calculating Acceleration Terms ax, ay, and az for Each Planet Using the Gravitational Formula (Positions 13, 14, and 15)

MovePlanets() now calculates the new velocities with the code shown in Listing 14-8.
    for n in range(0,N):
        Planet[n][10]=Planet[n][3]+Planet[n][13]*dt;
        Planet[n][11]=Planet[n][4]+Planet[n][14]*dt;
        Planet[n][12]=Planet[n][5]+Planet[n][15]*dt;
Listing 14-8

Calculating the New Velocities: vxP=vx+ax*dt, vyP=vy+ay*dt, and vzP=vz+az*dt, (Positions 10, 11, and 12)

Finally, the new position and velocity of each planet can be updated at its corresponding position in the array, using the code in Listing 14-9.
#Actualizing (x,y,z) and  (vx,vy,vz).
for n in range(0,N):
    Planet[n][0:6]=(Planet[n][7],
    Planet[n][8], Planet[n][9],
    Planet[n][10],Planet[n][11],
    Planet[n][12]);
Listing 14-9

Actualizing the Positions and Velocities of the Planets

At each clock tick, MovePlanets() repeats the actions described previously. Next, DrawPlanets(VX,VY,VZ,Which) clears the old scene and updates the screen with the new values, giving the appearance of real-time movement. The Temporal(B *args) function is in charge of coordinating these actions by using the code in Listing 14-10.
def Temporal(B, *args):
    global Flag, NUMBER, p, q;
    if (Flag==True):
        ClearObjects();
        MovePlanets();
        DrawPlanets(VX1,VY1,VZ1,0);
        DrawPlanets(VX2,VY2,VZ2,1);
        ShowScene(B);
Listing 14-10

Function to Process the Movements of the Planets

The code listings for the main.py and File.kv files are shown in Listings 14-11 and 14-12, respectively.
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.graphics import Line, Color
from kivy.clock import Clock
from kivy.core.image import Image as CoreImage
from PIL import Image, ImageDraw, ImageFont
import io
import os
import numpy as np
from kivy.lang import Builder
Builder.load_file(
    os.path.join(os.path.dirname(os.path.abspath(
                            __file__)), 'File.kv')
                  );
#Avoid Form1 of being resizable
from kivy.config import Config
Config.set("graphics","resizable", False)
Config.set('graphics', 'width',  '480');
Config.set('graphics', 'height', '680');
Flag=False; # Do not move planets until requested.
D=400;
VX1=-5; VY1=120; VZ1=0;
VX2=5; VY2=120; VZ2=0;
Factor0 =(D-VZ1) / (D/2-VZ1);
P=0.5; #percentage distance from screen
N=5; #Number of planets
# Array Planet:
# [x,y,z,  vx,vy,vz,  Mass,
# xP,yP,zP, vxP,vyP,vzP, ax,ay,az]
Planet=np.zeros( (N,16) );
Planet[0]=(  0,0,P*D,    0,0,0,  10.0e4,
                         0,0,0,  0,0,0, 0,0,0  );
Planet[1]=( -40,0,P*D,   0,0,50,   14.0,
                         0,0,0,  0,0,0, 0,0,0  );
Planet[2]=(  40,0,P*D,   0,0,-50,  14.0,
                         0,0,0,  0,0,0, 0,0,0  );
Planet[3]=(  60,0,P*D,   0,0, -41,  8.0,
                         0,0,0,  0,0,0, 0,0,0  );
Planet[4]=(  -60,0,P*D,   0,0, 41,  8.0,
                         0,0,0,  0,0,0, 0,0,0  );
#Array to store distances between planets: r(n,m)
R=np.zeros( (N,N) )
#---------------------------------------------------
def DrawPlanets(VX,VY,VZ,Which):
    global  Draw1,Draw2,Factor0;
    if (Which==0):
        r,g,b = 255, 0, 0; #red Image
        Draw=Draw1;
    else:
        r,g,b = 0, 255, 255; #blue image
        Draw=Draw2;
    for n in range(0,N):
        M=int(Planet[n][6]);
        if (M>1000):
            M=44;
        else:
            M=int(M);
        Factor=(D-VZ)/(D-Planet[n][2]-VZ);
        x1=XC+Factor*(Planet[n][0]-VX)+Factor0*VX;
        y1=YC-Factor*(Planet[n][1]-VY)-Factor0*VY;
        Draw.ellipse( (x1-M,y1-M,x1+M,y1+M), fill=(r,g,b));
def MovePlanets():
    G=1; dt=0.02;
    #Calculating x(n+1), y(n+1) and z(n+1).
    #values are stored in their
    #corresponding Planet array.
    #x(n+1)=x(n) +vx(n)*dt;
    #y(n+1)=y(n)+vy(n)*dt;
    #z(n+1)=z(n)+vz(n)*dt
    #These values will be stored at the end of
    #the calculations, in the initial positions.
    for n in range(0,N):
        Planet[n][7]=Planet[n][0]+Planet[n][3]*dt;
        Planet[n][8]=Planet[n][1]+Planet[n][4]*dt;
        Planet[n][9]=Planet[n][2]+Planet[n][5]*dt;
    #Filling array R with values r(n,m)**3/2
    for n in range(0,N):
        for m in range(0,N):
            if (n>=m):
                continue;
            R[n][m]=((Planet[n][0]-Planet[m][0])**2
                    +(Planet[n][1]-Planet[m][1])**2
        +(Planet[n][2]-Planet[m][2])**2  )**(3/2);
            R[m][n]=R[n][m];
    #Calculating acceleration terms ax,ay,az,
    #for each planet using the gravitational formula.
    for n in range(0,N):
        Planet[n][13]=0;
        Planet[n][14]=0;
        Planet[n][15]=0;
        for m in range(0,N):
            if (n==m):
                continue;
            Planet[n][13]=Planet[n][13]
                           + G*Planet[m][6]
               *(Planet[m][0]-Planet[n][0])/R[m][n];
            Planet[n][14]=Planet[n][14]
                           + G*Planet[m][6]
                *(Planet[m][1]-Planet[n][1])/R[m][n];
            Planet[n][15]=Planet[n][15]
                + G*Planet[m][6]
                *(Planet[m][2]-Planet[n][2])/R[m][n];
    #Calculating the new velocities vxP= vx+ ax*dt,
    #vyP=vy+ay*dt, and vzP=vz+az*dt.
    for n in range(0,N):
        Planet[n][10]=Planet[n][3]+Planet[n][13]*dt;
        Planet[n][11]=Planet[n][4]+Planet[n][14]*dt;
        Planet[n][12]=Planet[n][5]+Planet[n][15]*dt;
    #Actualizing (x,y,z) and  (vx,vy,vz).
    for n in range(0,N):
        Planet[n][0:6]=(Planet[n][7],
                        Planet[n][8],
                        Planet[n][9],
                        Planet[n][10],
                        Planet[n][11],
                        Planet[n][12]);
def ShowScene(B):
    Array1=np.array(PilImage1);
    Array2=np.array(PilImage2);
    Array3=Array1 | Array2;
    PilImage3=Image.fromarray(Array3);
    Memory=io.BytesIO();
    PilImage3.save(Memory, format="png");
    Memory.seek(0);
    ImagePNG=CoreImage(Memory, ext="png");
    B.ids.Screen1.texture=ImagePNG.texture;
    ImagePNG.remove_from_cache()
    Memory.close();
    PilImage3.close();
    Array1=None;
    Array2=None;
    Array3=None;
#---------------------------------------------------
def ClearObjects():
    Draw1.rectangle( (0, 0, H-10, W-10),
                             fill=(60, 70, 30, 1) );
    Draw2.rectangle( (0, 0, H-10, W-10),
                             fill=(60, 70, 30, 1) );
class Form1(FloatLayout):
    def __init__(Handle, **kwargs):
        super(Form1, Handle).__init__(**kwargs);
        Event1=Clock.schedule_once(Handle.Initialize);
        Event2=Clock.schedule_interval(
                                 Handle.Temporal,0.1);
    def Initialize(B, *args):
        global W,H, XC,YC;
        global PilImage1,PilImage2, Draw1,Draw2;
        W,H=B.ids.Screen1.size;
        XC=int (W/2)
        YC=int(H/2)
        PilImage1= Image.new('RGB', (W-10, H-10),
                                     (60, 70, 30));
        Draw1 = ImageDraw.Draw(PilImage1);
        PilImage2= Image.new('RGB', (W-10, H-10),
                                     (60, 70, 30));
        Draw2 = ImageDraw.Draw(PilImage2);
        Font = ImageFont.truetype('Gargi.ttf', 70)
        Draw1.text( (30,200), "3D Images", fill =
                            (255,0,0,1), font=Font);
        Draw2.text( (50,200), "3D Images", fill =
                          (0,255,255,1), font=Font);
        ShowScene(B);
    def Temporal(B, *args):
        global Flag, NUMBER, p, q;
        if (Flag==True):
            ClearObjects();
            MovePlanets();
            DrawPlanets(VX1,VY1,VZ1,0);
            DrawPlanets(VX2,VY2,VZ2,1);
            ShowScene(B);
#--------------------------------------------------------------
    def Button1_Click(B):
        global Draw1, Draw2, Flag;
        Flag=False;
        #  Clearing Draw1 and Draw2
        ClearObjects();
        DrawPlanets(VX1,VY1,VZ1,0);
        DrawPlanets(VX2,VY2,VZ2,1);
        ShowScene(B);
    def Button2_Click(B):
        global Draw1, Draw2, Flag;
        Flag=False;
        ClearObjects(); #  Clearing Draw1 and Draw2
        Font = ImageFont.truetype('Gargi.ttf', 70);
        Draw1.text( (30,200), "3D Images", fill =
                            (255,0,0,1), font=Font);
        Draw2.text( (50,200), "3D Images", fill =
                          (0,255,255,1), font=Font);
        ShowScene(B);
    def Button3_Click(B):
        global Flag;
        Flag=True;
    def Button4_Click(B):
        global Flag;
        Flag=False;
# This is the Start Up code .
class StartUp (App):
    def build (BU):
        BU.title="Form1"
        return Form1();
if __name__ =="__main__":
    StartUp().run();
Listing 14-11

Code for the main.py File

#:set W 440
#:set H 440
<Form1>:
    id : Form1
    Image:
        id: Screen1
        size_hint: None,None
        pos_hint: {"x":0.04, "y":0.34}
        size: W,H
        canvas.before:
            Color:
                rgba: 0.8 ,0.8, 0.0 ,1
            RoundedRectangle:
                pos:  self.pos
                size: self.size
    Button:
        id: Button1
        on_press: Form1.Button1_Click()
        text: "Button1"
        size_hint: None,None
        pos_hint: {"x": 0.2, "y":0.03}
        size: 100,30
    Button:
        id: Button2
        on_press: Form1.Button2_Click()
        text: "Button2"
        size_hint: None,None
        pos_hint: {"x": 0.63, "y":0.03}
        size: 100,30
    Button :
        id: Button3
        on_press: Form1.Button3_Click()
        text: "Button3"
        size_hint: None,None
        pos_hint: {"x": 0.05, "y":0.12}
        size: 100,30
    Button:
        id: Button4
        on_press: Form1.Button4_Click()
        text: "Button4"
        size_hint: None,None
        pos_hint: {"x": 0.73, "y":0.12}
        size: 100,30
Listing 14-12

Code for the file.kv File

14.6 Summary

In this chapter, we described a simple numerical approach to the gravitational N-body problem. We derived basic equations for calculating the interaction between masses under the influence of Newton’s gravitational law. We presented numerical solutions of the dynamic equations and illustrated their use by employing a working five-planet example. This approach allows us to appreciate the dynamics of the system in stereoscopic view.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset