Description
Introduction
The purpose of this software design exercise is to write a Python program that follows the given formal specification. The given specification is for simulating the physics of a scene where a shape moves through 2D space. Simulations of this sort are used for animations and video game physics.
The simulation is based on the familiar equation for Newton’s second law:
where F is the unbalanced force, m is the mass, r is the position of the body, v is the velocity of the body and a is the acceleration of the body. The motion of the body is calculated about the centre of mass (cm). Figure 1 provides an example of the possible output generated by the modules in this assignment.
Figure 1: Motion simulation for an object shown as plots of x vs t, y vs t and y vs x
For those that are interested, additional information can be found in the following papers: Physics, The Next Frontier and Physics, Part 2: Angular Effects.
As for the previous assignment, you will use doxygen, make, LaTeX and Python (version 3). In addition, this assignment will use pytest for unit testing and flake8 to verify that its pep8-inspired standard is enforced. This assignment also takes advantage of functional programming in Python.
All of your code, except for the testing files, should be documented using doxygen. Using doxygen on the testing files is optional. Your report should be written using LATEX. Your code should follow the given specification exactly. In particular, you should not add public methods or procedures that are not specified and you should not change the number or order of parameters for methods or procedures. If you need private methods or procedures, please use the Python convention of naming the files with the double underscore ( methodName ) (dunders)
For the purpose of understandability, the specification provided at the end of the assignment uses notation that is slightly different from the Hoffman and Strooper notation. Specifically the types for real and natural numbers are represented by R and N, respectively. (In this specification, the natural numbers are assumed to include 0.) Booleans are represented by B. Also, subscripts are used for indexing a sequence. For instance, xi means the same thing as x[i]. An overview of our mathematical notation and MIS templates can be found in the MIS Format document.
2.1 Installing scipy and matplotlib
This assignment uses two external modules that we have not previously installed: scipy and matplotlib. To install them on your virtual machine, do the following:
pip3 install scipy pip3 install matplotlib
2.2 Installing flake8
We will use flake8 to ensure your Python code meets the style conventions of the course. You have likely already installed flake8 on your VM or local machine. If not, you will need to install two ‘pip’ packages. This is the standard way to install packages for Python. First run the following command in your terminal:
pip –version
If the output includes ‘Python 3,’ then run the following instructions:
pip install flake8 pip install pep8-naming
If it does not, then use pip3 for the following alternate instructions.
pip3 install flake8 pip3 install pep8-naming
2.3 Running flake8
Run the following command in your A2 directory. This will inform you of the location and types of style violations. You can find more information on what each error means here: https://lintlyci.github.io/Flake8Rules/ flake8
Part 1
Step 1
Write the modules Shape.py, CircleT.py, TriangleT.py, BodyT.py, Scene.py, Plot.py following the specification given at the end of the assignment.
Step 2
Write a module (named test driver.py), using pytest, that tests the following modules: CircleT.py, TriangleT.py, BodyT.py and Scene.py. (Plot.py should be tested manually.) For Scene.py you do not have to test the getters or setters for the functions Fx and Fy. The given makefile Makefile has a rule test for running your tests. Each procedure/method should have at least one test case. Record your rationale for test case selection and the results of using this module to test the procedures in your modules. (You will submit your rationale with your report in Step 6.) Please make an effort to test normal cases, boundary cases, and exception cases. Your test program should compare the calculated output to the expected output and provide a summary of the number of test case that have passed or failed.
Testing the output of the sim method from Scene.py is made challenging because the output is sequences of real numbers. Use the following relative error formula when comparing a calculated sequence xcalc to the true solution xtrue:
(1)
Subtracting two sequences is done by subtracting corresponding elements to create a new sequence. The double vertical bars around a sequence mean taking the norm of the sequence, where the norm is a measure of the magnitude of the sequence. There are many different possible norms. The simplest is to find the maximum absolute value in the sequence. The two sequences are considered close to being equal if the left hand side of the equation is less than some small number . You are free to select a value for that you feel is reasonable.
In testing you may want to consider some common cases, as follows:
- The shape falling under the force of gravity in the negative y direction: Fx(t) = 0, Fy(t) = −gm, vx = 0, vy = 0.
- Projectile or ballistics motion: Fx(t) = 0, Fy(t) = −gm, vx = v cos(θ), vy = v sin(θ) where v is the magnitude of the initial velocity and θ is the angle from the horizontal.
To get more interesting plots, you may want to experiment with conditional expressions, like that shown in test expt.py. You can run this file via the make rule (make expt).
Step 3
Test the supplied Makefile rule for doc. This rule should compile your documentation into an html and LATEX version. Along with the supplied Makefile, a doxygen configuration file is also given in your initial repo. You should not have to change these files.
Your CircleT.py, TriangleT.py, BodyT.py and Scene.py files will automatically be pushed to your partner’s repo. Including your name in your partner code files is optional.
Step 4
After you have received your partner’s files, temporarily replace your corresponding files with your partner’s. (You are just moving the files for the purpose of testing; you will not replace your version of the code in the git repo.) Do not initially make any modifications to any of the code. Run your test module and record the results. Your evaluation for this step does not depend on the quality of your partner’s code, but only on your discussion of the testing results. If the tests fail, for the purposes of understanding what happened, you are allowed to modify your partner’s code.
Shape Interface Module
Interface Module
Shape
Uses
None
Syntax
Exported Constants
None
Exported Types
None
Exported Access Programs
| Routine name | In | Out | Exceptions |
| cm x | R | ||
| cm y | R | ||
| mass | R | ||
| m inert | R |
CircleT Module
Template Module inherits Shape
CircleT
Uses
Shape
Syntax
Exported Constants
None
Exported Types CircleT = ?
Exported Access Programs
| Routine name | In | Out | Exceptions |
| new CircleT | xs : R, ys : R, rs : R, ms : R | CircleT | ValueError |
| cm x | R | ||
| cm y | R | ||
| mass | R | ||
| m inert | R |
Semantics
State Variables
x : R y : R r : R m : R
State Invariant
r > 0 ∧ m > 0
new CircleT(xs,ys,rs,ms):
- transition: x,y,r,m := xs,ys,rs,ms
- output: out := self
- exception: (¬(rs > 0 ∧ ms > 0) ⇒ ValueError) cm x():
- output: out := x
- exception: none cm y():
- output: out := y
- exception: none mass():
- output: out := m
- exception: none m inert():
- output:
- exception: none
TriangleT Module
Template Module inherits Shape
TriangleT
Uses
Shape
Syntax
Exported Constants
None
Exported Types TriangleT = ?
Exported Access Programs
| Routine name | In | Out | Exceptions |
| new TriangleT | xs : R, ys : R, ss : R, ms : R | TriangleT | ValueError |
| cm x | R | ||
| cm y | R | ||
| mass | R | ||
| m inert | R |
Semantics
State Variables
x : R y : R
s : R #side length (equilateral triangle)
m : R
State Invariant
s > 0 ∧ m > 0
new TriangleT(xs,ys,ss,ms):
- transition: x,y,s,m := xs,ys,ss,ms
- output: out := self
- exception: (¬(ss > 0 ∧ ms > 0) ⇒ ValueError) cm x():
- output: out := x
- exception: none cm y():
- output: out := y
- exception: none mass():
- output: out := m
- exception: none m inert():
- output:
- exception: none
BodyT Module
Template Module inherits Shape
BodyT
Uses
Shape
Syntax
Exported Constants
None
Exported Types BodyT = ?
Exported Access Programs
| Routine name | In | Out | Exceptions |
| new BodyT | xs : seq of R, ys : seq of R, ms : seq of R | BodyT | ValueError |
| cm x | R | ||
| cm y | R | ||
| mass | R | ||
| m inert | R |
Semantics
State Variables
cmx : R cmy : R m : R
moment : R
State Invariant
moment ≥ 0 ∧ m > 0
new BodyT(xs,ys,ms):
- transition:
cmx,cmy,m,moment := cm(xs,ms),cm(ys,ms),sum(ms),
mmom(xs,ys,ms) − sum(ms)(cm(xs,ms)2 + cm(ys,ms)2)
- output: out := self
- exception: (¬(|xs| = |ys| = |ms|) ⇒ ValueError |¬(∧µ : R|µ ∈ ms : µ > 0) ⇒
ValueError) cm x():
- output: out := cmx
- exception: none cm y():
- output: out := cmy
- exception: none mass():
- output: out := m
- exception: none m inert():
- output: out := moment
- exception: none
Local Functions sum : seq of R → R sum(ms) ≡ (+µ : R|µ ∈ ms : µ)
cm : seq of R × seq of R → R
cm(z,m) ≡ (+i : N|i ∈ [0..|ms| − 1] : zimi)/sum(m)
mmom : seq of R × seq of R × seq of R → R
mmom(x,y,m) ≡ (+i : N|i ∈ [0..|m| − 1] : mi(x2i + yi2))
Scene Module
Template Module
Scene
Uses
Shape, SciPy
Syntax
Exported Constants
None
Exported Types Scene = ?
Exported Access Programs
| name | In | Out | Exceptions |
| new Scene | Shape, R → R, R → R, R, R | Scene | |
| get shape | Shape | ||
| get unbal forces | R → R, R → R | ||
| get init velo | R, R | ||
| set shape | Shape | ||
| set unbal forces | R → R, R → R | ||
| set init velo | R, R | ||
| sim | R, N | seq of R, seq of (seq [4] of R) |
Semantics
State Variables
s : Shape
Fx : R → R # unbalanced force function in x dir Fy : R → R # unbalanced force function in y dir vx : R # initial velocity in x dir vy : R # initial velocity in y dir
State Invariant
None
Assumptions
None
Access Routine Semantics new Scene(
- transition:
- output: out := self
- exception: None get shape():
- output: out := s
- exception: none get unbal forces():
- output: out := Fx,Fy
- exception: none get init velo():
- output: out := vx,vy
- exception: none set shape(s’):
- transition: s := s0
- exception: none set unbal forces(
- transition:
- exception: none
set init velo(
- transition:
- exception: none
sim(tfinal, nsteps):
- output: out := t,odeint(ode,[cm x(),s.cm y(),vx,vy],t) where t = [i : N|i ∈ [0..nsteps
# for the sequence of t values the values should be in the natural ascending order for the i values
- exception: none
Local Functions
ode : (seq [4] of R) × R → seq [4] of R
ode(w,t) ≡ [w[2],w[3],Fx(t)/s.mass(),Fy(t)/s.mass()]
Plot Module
Module
Plot
Uses
None
Syntax
Exported Constants
None
Exported Access Programs
| Routine name | In | Out | Exceptions |
| plot | seq of (seq [4] of R), seq of R | ValueError |
Semantics
Environment Variables win: two dimensional sequence of coloured pixels
State Variables
None
State Invariant
None
Assumptions
None
Access Routine Semantics
plot(w,t)
- transition: modify win so that it displays three appropriately labelled x–y graphs of the data points in w showing the following plots
- x versus t
- y versus t
- y versus x where t is the supplied argument and
- x = hi : N|i ∈ [0..|w| − 1] : w[i][0]i
- y = hi : N|i ∈ [0..|w| − 1] : w[i][1]i
(We are abusing the sequence notation with the assumption that the sequence will be built in the order of increasing i values.) The plot should have the same structure as Figure 1.
- exception: (¬(|w| = |t|) ⇒ ValueError)
2.3.1 Considerations
Implementation of Plot.py is facilitated by the matplotlib package.
SciPy Module
2.4 Module
SciPy
2.5 Uses
None
2.6 Syntax
2.6.1 Exported Access Programs
Name In Out Except.
odeint (seq [4] of R) × R → seq [4] of R, seq of (seq [4] of R) ODE ERR
seq [4] of R, seq of R
2.7 Semantics
2.7.1 State Variables
None
2.7.2 Access Routine Semantics
#Solving
# Bold font is used to indicate variables that are a sequence type odeint(f, y0, t):
- output: out := hi : N|i ∈ [0..|t| − 1] : hyo(ti),y1(ti),y2(ti),y3(ti)ii where
Z tfin
y(t) = y0 + f(s,y(s))ds
t0
- exception: exc := ( ODE Solver Fails ⇒ ODE ERR)
2.7.3 Considerations
This function is implemented by the Python library scipy. The syntax of odeint exactly matches what is given here. The output is a sequence of sequences. ODE ERR stands for any error that can be raised by the scipy implementation of odeint.




