Introduction

In programming, a module is a piece of software that has a specific functionality. As our program grows bigger, it may contain many lines of code. Instead of putting everything in a single file, we can use modules to separate codes in separate files as per their functionality. This makes our code organized and easier to maintain.

Packages are namespaces containing multiple packages and modules. They’re just directories, but with certain requirements.

In order to import a module/package we use the keyword import follow by the package name.

1
import numpy

Namespace in python are quick peculiar. We can call a specific function as well as a whole module.

1
2
3
4
5
#for a specific module
from numpy import linalg

#for a specific function
from numpy.linalg import eigvals

It can be memory heavy, but we can invoke all functions, variables and objects in a module/package natively in the environment with the * construct. We can also associate a package/module to a new name using the keyword structure:

import module name as new name

1
2
3
4
5
#to import all functions, variables and objects
from numpy import *

# import module by renaming it
import numpy as np

The Python standard library contains well over 200 modules. We can import a module according to our needs.

A full example of a python code using most of the things seen just here. Taking advantages of the scientific (scipy), numerical (numpy), data visualisation (matplotlib) packages

 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
import numpy as np
import matplotlib.pyplot as plt

class Projectile():
    def __init__(self,r0,v0, g, t_span):
        self.t = t_span
        self.x0, self.y0 = r0
        self.vx0, self.vy0 = v0
        self.x = self.x0 + self.vx0*self.t
        self.y = self.y0 + self.vy0*self.t + -(1/2)*g*self.t**2
    def get_peak_y(self):
        return max(self.y)
    def get_peak_x(self):
        return self.x[np.argmax(self.y)]
    def get_peak_t(self):
        return self.t[np.argmax(self.y)]
    def get_x_landing(self):
        x_after_peak = self.x[self.t>self.get_peak_t()]
        y_after_peak = self.y[self.t>self.get_peak_t()]
        return x_after_peak[np.argmin(np.abs(y_after_peak))]

r0=[0,0]
v0=[5,6]
m=1
g=9.81
t = np.linspace(0,2,100)

p = Projectile(r0, v0, g, t)  

plt.figure(figsize=(6,3))
plt.plot(p.x, p.y,'blue')
plt.axhline(p.get_peak_y(), color='r', ls='--')
plt.axvline(p.get_peak_x(), color='r', ls='--')
plt.xlabel('X Position [m]')
plt.ylabel('Y Position [m]')
plt.grid()
plt.show()
Output:

A more complex example using multiple packages and a special function for solving ODE’s (solve_ivp):

 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
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp

class Projectile():
    def __init__(self,r0,v0,t,F,m,*args):
        self.t = t
        self.x0, self.y0 = r0
        self.vx0, self.vy0 = v0
        self.F = F
        self.m = m
        self.args = args
    def solve_motion(self):
        def dSdt(t, S, *args):
            x, vx, y, vy = S
            return [vx, self.F(t,x,y,vx,vy,m,*args)[0]/self.m,
                    vy, self.F(t,x,y,vx,vy,m,*args)[1]/self.m]
        self.solution = solve_ivp(dSdt, [min(self.t), max(self.t)],
                                  [self.x0,self.vx0,self.y0,self.vy0],
                                  t_eval=self.t, args=self.args)
        self.x, self.vx, self.y, self.vy = self.solution.y
    def get_peak_y(self):
        return max(self.y)
    def get_peak_x(self):
        return self.x[np.argmax(self.y)]
    def get_peak_t(self):
        return self.t[np.argmax(self.y)]
    def get_x_landing(self):
        x_after_peak = self.x[self.t>self.get_peak_t()]
        y_after_peak = self.y[self.t>self.get_peak_t()]
        return x_after_peak[np.argmin(np.abs(y_after_peak))]

# Conditions
r0=[0,0]
v0=[5,6]
m=1
g=9.81,
t = np.linspace(0,1.3,100)
m=1
g = 9.8
b = 0
b1 = 1
b2 = 5

# Physics effects
def force(t,x,vx,y,vy,m,g):
    return 0
def force_1(t,x,vx,y,vy,m,g,b):
    return [-b*np.sqrt(vx**2+vy**2)*vx, -b*np.sqrt(vx**2+vy**2)*vy - m*g]
def force_2(t,x,vx,y,vy,m,g,b):
    return [-b*vx, -b*vy - m*g]

# Paricle Motion
p = Projectile(r0, v0, t, force, m, g)  
p1 = Projectile(r0, v0, t, force_1, m, g, b1)  
p2 = Projectile(r0, v0, t, force_2, m, g, b2)  
p.x, p.y = r0[0] + v0[0]*t, r0[1] + v0[1]*t  -(1/2)*g*t**2
p1.solve_motion()
p2.solve_motion()

plt.figure(figsize=(6,3))
plt.plot(p.x,p.y)
plt.plot(p1.x,p1.y)
plt.plot(p2.x,p2.y)
plt.axvline(p.get_peak_x(), color='k', ls='--', label='No Friction')
plt.axvline(p1.get_peak_x(), color='k', ls='--', label='Quadratic Friction')
plt.axvline(p2.get_peak_x(), color='k', ls='--', label='Linear Friction')
plt.ylim(bottom=0)
plt.xlim(0,max(p.x))
plt.grid()
plt.xlabel('X-Distance')
plt.ylabel('Y-Distance')
plt.show()
Output: