# Simplified three-body simulation

The code below simulates a given Newtonian 3 body system. Each row per slice is supposed to represent a Cartesian component for the initial trajectory vector and for the distance vectors to the other two objects.

What are some possible improvements to this? For instance is there any benefit to implement something to cut the redundancy with the duplicate distance vectors? How about using something like NumPy instead of vanilla?

# GRAV_MASS_CONSTANT = 1
# masses = [1, 1, 1]

def updated_trajectory_component(row):
# a = masses * GRAV_MASS_CONSTANT
a = 1
return row[0] + sum([a / i for i in row[1:]])

def updated_distance_component(row):
return [i - row[0] for i in row[1:]]

def iterate_row(row):
dist = updated_distance_component(row)
return [updated_trajectory_component(row)] + dist

def iterate_slice(slice):
return [iterate_row(i) for i in slice]

def iterate_cube(cube):
return [iterate_slice(i) for i in cube]

def num_iter(cube, iter):
print(cube)
if iter == 1: return iterate_cube(cube)
else: return num_iter(iterate_cube(cube), iter - 1)

cube = [
[
[0, 0.1, 1],
[0, 1, 0.1],
[0, 1, 1]
],
[
[0, 0.1, 1],
[0, -1, 1],
[0, -1, 0.1]
],
[
[0, -1, -1],
[0, 0.1, -1],
[0, -1, 0.1]
]
]


• Docstrings: You should include a docstring at the beginning of every method, class, and module you write. This will allow documentation to identify what your program is supposed to do.
• Constants Naming: Any constants that you have in your program (eg. cube) should be in UPPERCASE, like so: CUBE.
• Main Guard: Any code that isn't inside a method/class should be inside an if __name__ == '__main__' guard. This will prevent this code from being executed if you decide to import this module from other programs. This StackOverflow answer provides more insight and explanation.
• Unnecessary if/else with returns: It is unnecessary to have an else after an if, if you return something in the if. Simply put the else return statement outside the if statement. This return will be run if the if evaluates to be False, which removes the necessity for the else statement.
• Reserved Names: You shouldn't use reserved names for variable names, such as iter and slice, to avoid confusion.
• Variable/Parameter Naming: You have a parameter named iter which you use as the number of times you iterate over the cube. It took a bit to identify this, as I see iter as an iterator, not the number of times you iterate. Renaming it iterations makes it clearer about what that parameter is supposed to be.

Updated Code

"""
Module Docstring
A description of your program goes here
"""

def updated_trajectory_component(row):
"""
Updated the trajectory component of the passed row

:param row: Row to be updated

"""
num = 1
return row[0] + sum([num / i for i in row[1:]])

def updated_distance_component(row):
"""
Updated the distance component of the passed row

:param row: Row to be updated

"""
return [i - row[0] for i in row[1:]]

def iterate_row(row):
"""
Iterates over the passed row

:param row: Row to be iterated

"""
dist = updated_distance_component(row)
return [updated_trajectory_component(row)] + dist

def iterate_slice(_slice):
"""
Iterates over the rows in the passed slice

:param _slice: Slice to be iterated over

"""
return [iterate_row(i) for i in _slice]

def iterate_cube(cube):
"""
Iterates over the slices in the passed cube

:param cube: Cube to be iterated over

"""
return [iterate_slice(i) for i in cube]

def num_iterator(cube, iterations):
"""
Recursive iterator, iterates over the cube iterations times

:param cube: Cube to iterate
:param iterations: Number of times to iterate
"""
print(cube)
if iterations == 1:
return iterate_cube(cube)
return num_iterator(iterate_cube(cube), iterations - 1)

if __name__ == '__main__':
CUBE = [
[
[0, 0.1, 1],
[0, 1, 0.1],
[0, 1, 1]
],
[
[0, 0.1, 1],
[0, -1, 1],
[0, -1, 0.1]
],
[
[0, -1, -1],
[0, 0.1, -1],
[0, -1, 0.1]
]
]

#Sample Cases
num_iterator(CUBE, 5)
num_iterator(CUBE, 2)
num_iterator(CUBE, 7)
num_iterator(CUBE, 4)