From 97f9f6c167b9ed84d0f3f5d2a90424a469e2351c Mon Sep 17 00:00:00 2001 From: algobytewise Date: Sun, 28 Mar 2021 10:37:23 +0530 Subject: [PATCH 01/13] add n_body_simulation.py --- other/n_body_simulation.py | 262 +++++++++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 other/n_body_simulation.py diff --git a/other/n_body_simulation.py b/other/n_body_simulation.py new file mode 100644 index 000000000000..b7aaaee62b9b --- /dev/null +++ b/other/n_body_simulation.py @@ -0,0 +1,262 @@ +""" +In physics and astronomy, a gravitational N-body simulation is a simulation of a +dynamical system of particles under the influence of gravity. The system +consists of a number of bodies, each of which exerts a gravitational force on all +other bodies. These forces are calculated using Newton's law of universal +gravitation. The Euler method is used at each time-step to calculate the change in +velocity and position brought about by these forces. Softening is used to prevent +numerical divergences when a particle comes too close to another (and the force +goes to infinity). +(Description adapted from https://en.wikipedia.org/wiki/N-body_simulation ) +(See also http://www.shodor.org/refdesk/Resources/Algorithms/EulersMethod/ ) +""" + + +from __future__ import annotations + +import random + +from matplotlib import animation # type: ignore +from matplotlib import pyplot as plt + + +class Body: + def __init__( + self: Body, + position_x: float, + position_y: float, + velocity_x: float, + velocity_y: float, + mass: float = 1.0, + size: float = 1.0, + color: str = "blue", + ) -> None: + """ + The parameters "size" & "color" are not relevant for the simulation itself, + they are only used for plotting. + """ + self.position_x = position_x + self.position_y = position_y + self.velocity_x = velocity_x + self.velocity_y = velocity_y + self.mass = mass + self.size = size + self.color = color + + def update_velocity( + self: Body, force_x: float, force_y: float, delta_time: float + ) -> None: + """ + Euler algorithm for velocity + + >>> body = Body(0.,0.,0.,0.) + >>> body.update_velocity(1.,0.,1.) + >>> body.velocity_x + 1.0 + """ + self.velocity_x += force_x * delta_time + self.velocity_y += force_y * delta_time + + def update_position(self: Body, delta_time: float) -> None: + """ + Euler algorithm for position + + >>> body = Body(0.,0.,1.,0.) + >>> body.update_position(1.) + >>> body.position_x + 1.0 + """ + self.position_x += self.velocity_x * delta_time + self.position_y += self.velocity_y * delta_time + + +class BodySystem: + """ + This class is used to hold the bodies, the gravitation constant, the time + factor and the softening factor. The time factor is used to control the speed + of the simulation. The softening factor is used for softening, a numerical + trick for N-body simulations to prevent numerical divergences when two bodies + get too close to each other. + """ + + def __init__( + self: BodySystem, + bodies: list[Body], + gravitation_constant: float = 1.0, + time_factor: float = 1.0, + softening_factor: float = 0.0, + ) -> None: + self.bodies = bodies + self.gravitation_constant = gravitation_constant + self.time_factor = time_factor + self.softening_factor = softening_factor + + def update_system(self: BodySystem, delta_time: float) -> None: + """ + For each body, loop through all other bodies to calculate the total + force they exert on it. Use that force to update the body's velocity. + + >>> body_system = BodySystem([Body(0,0,0,0), Body(10,0,0,0)]) + >>> body_system.update_system(1) + >>> body_system.bodies[0].position_x + 0.01 + """ + for body1 in self.bodies: + force_x = 0.0 + force_y = 0.0 + for body2 in self.bodies: + if body1 != body2: + dif_x = body2.position_x - body1.position_x + dif_y = body2.position_y - body1.position_y + + # Calculation of the distance using Pythagoras's theorem + # Extra factor due to the softening technique + distance = (dif_x ** 2 + dif_y ** 2 + self.softening_factor) ** ( + 1 / 2 + ) + + # Newton's law of universal gravitation. + force_x += ( + self.gravitation_constant * body2.mass * dif_x / distance ** 3 + ) + force_y += ( + self.gravitation_constant * body2.mass * dif_y / distance ** 3 + ) + + # Update the body's velocity once all the force components have been added + body1.update_velocity(force_x, force_y, delta_time * self.time_factor) + + # Update the positions only after all the velocities have been updated + for body in self.bodies: + body.update_position(delta_time * self.time_factor) + + +def plot( + title: str, + body_system: BodySystem, + x_start: float = -1, + x_end: float = 1, + y_start: float = -1, + y_end: float = 1, +) -> None: + """ + Utility function to plot how the given body-system evolves over time. + No doctest provided since this function does not have a return value. + """ + + INTERVAL = 20 # Frame rate of the animation + DELTA_TIME = INTERVAL / 1000 # Time between time steps in seconds + + fig = plt.figure() + fig.canvas.set_window_title(title) + + # Set section to be plotted + ax = plt.axes(xlim=(x_start, x_end), ylim=(y_start, y_end)) + + # Each body is drawn as a patch by the plt-function + patches = [] + for body in body_system.bodies: + patches.append( + plt.Circle((body.position_x, body.position_y), body.size, fc=body.color) + ) + + # Function called once at the start of the animation + def init() -> list[patches.Circle]: + axes = plt.gca() + axes.set_aspect("equal") + + for patch in patches: + ax.add_patch(patch) + return patches + + # Function called at each step of the animation + def update(frame: int) -> list[patches.Circle]: + # Update the positions of the bodies + body_system.update_system(DELTA_TIME) + + # Update the positions of the patches + for patch, body in zip(patches, body_system.bodies): + patch.center = (body.position_x, body.position_y) + return patches + + anim = animation.FuncAnimation( # noqa: F841 + fig, update, init_func=init, interval=INTERVAL, blit=True + ) + + plt.show() + + +if __name__ == "__main__": + # Example 1: figure-8 solution to the 3-body-problem + # This example can be seen as a test of the implementation: given the right + # initial conditions, the bodies should move in a figure-8. + # (initial conditions taken from http://www.artcompsci.org/vol_1/v1_web/node56.html) + position_x = 0.9700436 + position_y = -0.24308753 + velocity_x = 0.466203685 + velocity_y = 0.43236573 + + bodies1 = [ + Body(position_x, position_y, velocity_x, velocity_y, size=0.2, color="red"), + Body(-position_x, -position_y, velocity_x, velocity_y, size=0.2, color="green"), + Body(0, 0, -2 * velocity_x, -2 * velocity_y, size=0.2, color="blue"), + ] + body_system1 = BodySystem(bodies1, time_factor=3) + plot("Figure-8 solution to the 3-body-problem", body_system1, -2, 2, -2, 2) + + # Example 2: Moon's orbit around the earth + # This example can be seen as a test of the implementation: given the right + # initial conditions, the moon should orbit around the earth as it actually does. + # (mass, velocity and distance taken from https://en.wikipedia.org/wiki/Earth + # and https://en.wikipedia.org/wiki/Moon) + moon_mass = 7.3476e22 + earth_mass = 5.972e24 + velocity_dif = 1022 + earth_moon_distance = 384399000 + gravitation_constant = 6.674e-11 + + # Calculation of the respective velocities so that total impulse is zero, + # i.e. the two bodies together don't move + moon_velocity = earth_mass * velocity_dif / (earth_mass + moon_mass) + earth_velocity = moon_velocity - velocity_dif + + moon = Body(-earth_moon_distance, 0, 0, moon_velocity, moon_mass, 10000000, "grey") + earth = Body(0, 0, 0, earth_velocity, earth_mass, 50000000, "blue") + body_system2 = BodySystem([earth, moon], gravitation_constant, time_factor=1000000) + plot( + "Moon's orbit around the earth", + body_system2, + -430000000, + 430000000, + -430000000, + 430000000, + ) + + # Example 3: Random system with many bodies + bodies = [] + for i in range(10): + velocity_x = random.uniform(-0.5, 0.5) + velocity_y = random.uniform(-0.5, 0.5) + + # Bodies are created pairwise with opposite velocities so that the + # total impulse remains zero + bodies.append( + Body( + random.uniform(-0.5, 0.5), + random.uniform(-0.5, 0.5), + velocity_x, + velocity_y, + size=0.05, + ) + ) + bodies.append( + Body( + random.uniform(-0.5, 0.5), + random.uniform(-0.5, 0.5), + -velocity_x, + -velocity_y, + size=0.05, + ) + ) + body_system3 = BodySystem(bodies, 0.01, 10, 0.1) + plot("Random system with many bodies", body_system3, -1.5, 1.5, -1.5, 1.5) From 1812626a9a582607fb030002186879ec6f6d9872 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Sun, 28 Mar 2021 05:07:38 +0000 Subject: [PATCH 02/13] updating DIRECTORY.md --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 42a6c49c735f..5725a73d94cc 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -545,6 +545,7 @@ * [Linear Congruential Generator](https://github.com/TheAlgorithms/Python/blob/master/other/linear_congruential_generator.py) * [Lru Cache](https://github.com/TheAlgorithms/Python/blob/master/other/lru_cache.py) * [Magicdiamondpattern](https://github.com/TheAlgorithms/Python/blob/master/other/magicdiamondpattern.py) + * [N Body Simulation](https://github.com/TheAlgorithms/Python/blob/master/other/n_body_simulation.py) * [Nested Brackets](https://github.com/TheAlgorithms/Python/blob/master/other/nested_brackets.py) * [Password Generator](https://github.com/TheAlgorithms/Python/blob/master/other/password_generator.py) * [Scoring Algorithm](https://github.com/TheAlgorithms/Python/blob/master/other/scoring_algorithm.py) From 719c628600c9fa868ab0b700ca998dec175e005a Mon Sep 17 00:00:00 2001 From: algobytewise Date: Mon, 29 Mar 2021 16:56:50 +0530 Subject: [PATCH 03/13] Rename other/n_body_simulation.py to physics/n_body_simulation.py --- {other => physics}/n_body_simulation.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {other => physics}/n_body_simulation.py (100%) diff --git a/other/n_body_simulation.py b/physics/n_body_simulation.py similarity index 100% rename from other/n_body_simulation.py rename to physics/n_body_simulation.py From d375b741e3b072b17d89a63ca84991d6c370d413 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Mon, 29 Mar 2021 11:27:41 +0000 Subject: [PATCH 04/13] updating DIRECTORY.md --- DIRECTORY.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 5725a73d94cc..e6ce3ae718b3 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -545,13 +545,15 @@ * [Linear Congruential Generator](https://github.com/TheAlgorithms/Python/blob/master/other/linear_congruential_generator.py) * [Lru Cache](https://github.com/TheAlgorithms/Python/blob/master/other/lru_cache.py) * [Magicdiamondpattern](https://github.com/TheAlgorithms/Python/blob/master/other/magicdiamondpattern.py) - * [N Body Simulation](https://github.com/TheAlgorithms/Python/blob/master/other/n_body_simulation.py) * [Nested Brackets](https://github.com/TheAlgorithms/Python/blob/master/other/nested_brackets.py) * [Password Generator](https://github.com/TheAlgorithms/Python/blob/master/other/password_generator.py) * [Scoring Algorithm](https://github.com/TheAlgorithms/Python/blob/master/other/scoring_algorithm.py) * [Sdes](https://github.com/TheAlgorithms/Python/blob/master/other/sdes.py) * [Tower Of Hanoi](https://github.com/TheAlgorithms/Python/blob/master/other/tower_of_hanoi.py) +## Physics + * [N Body Simulation](https://github.com/TheAlgorithms/Python/blob/master/physics/n_body_simulation.py) + ## Project Euler * Problem 001 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_001/sol1.py) From 56e1dc450ea5a4655d646e57ea91e450ca22bee1 Mon Sep 17 00:00:00 2001 From: algobytewise Date: Mon, 29 Mar 2021 16:58:08 +0530 Subject: [PATCH 05/13] Update build.yml --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7273119302e2..c462fcee6669 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,6 +41,7 @@ jobs: machine_learning networking_flow neural_network + physics quantum scheduling sorts From 09237cb3dbb5d3600b47084600c6df578ff69a8d Mon Sep 17 00:00:00 2001 From: algobytewise Date: Mon, 29 Mar 2021 16:59:50 +0530 Subject: [PATCH 06/13] refactor examples & add doctests --- physics/n_body_simulation.py | 171 +++++++++++++++++++++++++---------- 1 file changed, 123 insertions(+), 48 deletions(-) diff --git a/physics/n_body_simulation.py b/physics/n_body_simulation.py index b7aaaee62b9b..9db13b8dc07f 100644 --- a/physics/n_body_simulation.py +++ b/physics/n_body_simulation.py @@ -16,7 +16,7 @@ import random -from matplotlib import animation # type: ignore +from matplotlib import animation from matplotlib import pyplot as plt @@ -49,10 +49,24 @@ def update_velocity( """ Euler algorithm for velocity - >>> body = Body(0.,0.,0.,0.) - >>> body.update_velocity(1.,0.,1.) - >>> body.velocity_x - 1.0 + >>> body_1 = Body(0.,0.,0.,0.) + >>> body_1.update_velocity(1.,0.,1.) + >>> (body_1.velocity_x, body_1.velocity_y) + (1.0, 0.0) + + >>> body_1.update_velocity(1.,0.,1.) + >>> (body_1.velocity_x, body_1.velocity_y) + (2.0, 0.0) + + >>> body_2 = Body(0.,0.,5.,0.) + >>> body_2.update_velocity(0.,-10.,10.) + >>> (body_2.velocity_x, body_2.velocity_y) + (5.0, -100.0) + + >>> body_2.update_velocity(0.,-10.,10.) + >>> (body_2.velocity_x, body_2.velocity_y) + (5.0, -200.0) + """ self.velocity_x += force_x * delta_time self.velocity_y += force_y * delta_time @@ -61,10 +75,23 @@ def update_position(self: Body, delta_time: float) -> None: """ Euler algorithm for position - >>> body = Body(0.,0.,1.,0.) - >>> body.update_position(1.) - >>> body.position_x - 1.0 + >>> body_1 = Body(0.,0.,1.,0.) + >>> body_1.update_position(1.) + >>> (body_1.position_x, body_1.position_y) + (1.0, 0.0) + + >>> body_1.update_position(1.) + >>> (body_1.position_x, body_1.position_y) + (2.0, 0.0) + + >>> body_2 = Body(10.,10.,0.,-2.) + >>> body_2.update_position(1.) + >>> (body_2.position_x, body_2.position_y) + (10.0, 8.0) + + >>> body_2.update_position(1.) + >>> (body_2.position_x, body_2.position_y) + (10.0, 6.0) """ self.position_x += self.velocity_x * delta_time self.position_y += self.velocity_y * delta_time @@ -96,10 +123,19 @@ def update_system(self: BodySystem, delta_time: float) -> None: For each body, loop through all other bodies to calculate the total force they exert on it. Use that force to update the body's velocity. - >>> body_system = BodySystem([Body(0,0,0,0), Body(10,0,0,0)]) - >>> body_system.update_system(1) - >>> body_system.bodies[0].position_x - 0.01 + >>> body_system_1 = BodySystem([Body(0,0,0,0), Body(10,0,0,0)]) + >>> body_system_1.update_system(1) + >>> (body_system_1.bodies[0].position_x, body_system_1.bodies[0].position_y) + (0.01, 0.0) + >>> (body_system_1.bodies[0].velocity_x, body_system_1.bodies[0].velocity_y) + (0.01, 0.0) + + >>> body_system_2 = BodySystem([Body(-10,0,0,0), Body(10,0,0,0, mass=4)], 1, 10) + >>> body_system_2.update_system(1) + >>> (body_system_2.bodies[0].position_x, body_system_2.bodies[0].position_y) + (-9.0, 0.0) + >>> (body_system_2.bodies[0].velocity_x, body_system_2.bodies[0].velocity_y) + (0.1, 0.0) """ for body1 in self.bodies: force_x = 0.0 @@ -131,6 +167,34 @@ def update_system(self: BodySystem, delta_time: float) -> None: body.update_position(delta_time * self.time_factor) +def update_step( + body_system: BodySystem, delta_time: float, patches: list[plt.Circle] +) -> None: + """ + Updates the body-system and applies the change to the patch-list used for plotting + + >>> body_system_1 = BodySystem([Body(0,0,0,0), Body(10,0,0,0)]) + >>> patches_1 = [plt.Circle((body.position_x, body.position_y), body.size, + ... fc=body.color)for body in body_system_1.bodies] #doctest: +ELLIPSIS + >>> update_step(body_system_1, 1, patches_1) + >>> patches_1[0].center + (0.01, 0.0) + + >>> body_system_2 = BodySystem([Body(-10,0,0,0), Body(10,0,0,0, mass=4)], 1, 10) + >>> patches_2 = [plt.Circle((body.position_x, body.position_y), body.size, + ... fc=body.color)for body in body_system_2.bodies] #doctest: +ELLIPSIS + >>> update_step(body_system_2, 1, patches_2) + >>> patches_2[0].center + (-9.0, 0.0) + """ + # Update the positions of the bodies + body_system.update_system(delta_time) + + # Update the positions of the patches + for patch, body in zip(patches, body_system.bodies): + patch.center = (body.position_x, body.position_y) + + def plot( title: str, body_system: BodySystem, @@ -149,48 +213,41 @@ def plot( fig = plt.figure() fig.canvas.set_window_title(title) - - # Set section to be plotted - ax = plt.axes(xlim=(x_start, x_end), ylim=(y_start, y_end)) + ax = plt.axes( + xlim=(x_start, x_end), ylim=(y_start, y_end) + ) # Set section to be plotted + plt.gca().set_aspect("equal") # Fix aspect ratio # Each body is drawn as a patch by the plt-function - patches = [] - for body in body_system.bodies: - patches.append( - plt.Circle((body.position_x, body.position_y), body.size, fc=body.color) - ) - - # Function called once at the start of the animation - def init() -> list[patches.Circle]: - axes = plt.gca() - axes.set_aspect("equal") + patches = [ + plt.Circle((body.position_x, body.position_y), body.size, fc=body.color) + for body in body_system.bodies + ] - for patch in patches: - ax.add_patch(patch) - return patches + for patch in patches: + ax.add_patch(patch) # Function called at each step of the animation - def update(frame: int) -> list[patches.Circle]: - # Update the positions of the bodies - body_system.update_system(DELTA_TIME) - - # Update the positions of the patches - for patch, body in zip(patches, body_system.bodies): - patch.center = (body.position_x, body.position_y) + def update(frame: int) -> list[plt.Circle]: + update_step(body_system, DELTA_TIME, patches) return patches anim = animation.FuncAnimation( # noqa: F841 - fig, update, init_func=init, interval=INTERVAL, blit=True + fig, update, interval=INTERVAL, blit=True ) plt.show() -if __name__ == "__main__": - # Example 1: figure-8 solution to the 3-body-problem - # This example can be seen as a test of the implementation: given the right - # initial conditions, the bodies should move in a figure-8. - # (initial conditions taken from http://www.artcompsci.org/vol_1/v1_web/node56.html) +def example_1() -> None: + """ + Example 1: figure-8 solution to the 3-body-problem + This example can be seen as a test of the implementation: given the right + initial conditions, the bodies should move in a figure-8. + (initial conditions taken from http://www.artcompsci.org/vol_1/v1_web/node56.html) + No doctest provided since this function does not have a return value. + """ + position_x = 0.9700436 position_y = -0.24308753 velocity_x = 0.466203685 @@ -204,11 +261,17 @@ def update(frame: int) -> list[patches.Circle]: body_system1 = BodySystem(bodies1, time_factor=3) plot("Figure-8 solution to the 3-body-problem", body_system1, -2, 2, -2, 2) - # Example 2: Moon's orbit around the earth - # This example can be seen as a test of the implementation: given the right - # initial conditions, the moon should orbit around the earth as it actually does. - # (mass, velocity and distance taken from https://en.wikipedia.org/wiki/Earth - # and https://en.wikipedia.org/wiki/Moon) + +def example_2() -> None: + """ + Example 2: Moon's orbit around the earth + This example can be seen as a test of the implementation: given the right + initial conditions, the moon should orbit around the earth as it actually does. + (mass, velocity and distance taken from https://en.wikipedia.org/wiki/Earth + and https://en.wikipedia.org/wiki/Moon) + No doctest provided since this function does not have a return value. + """ + moon_mass = 7.3476e22 earth_mass = 5.972e24 velocity_dif = 1022 @@ -232,7 +295,13 @@ def update(frame: int) -> list[patches.Circle]: 430000000, ) - # Example 3: Random system with many bodies + +def example_3() -> None: + """ + Example 3: Random system with many bodies. + No doctest provided since this function does not have a return value. + """ + bodies = [] for i in range(10): velocity_x = random.uniform(-0.5, 0.5) @@ -260,3 +329,9 @@ def update(frame: int) -> list[patches.Circle]: ) body_system3 = BodySystem(bodies, 0.01, 10, 0.1) plot("Random system with many bodies", body_system3, -1.5, 1.5, -1.5, 1.5) + + +if __name__ == "__main__": + example_1() + example_2() + example_3() From 7a0081d76a4161cdd67e156251e7a6c8702e8440 Mon Sep 17 00:00:00 2001 From: algobytewise Date: Sun, 4 Apr 2021 10:07:35 +0530 Subject: [PATCH 07/13] removed type-hints from self-parameter --- physics/n_body_simulation.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/physics/n_body_simulation.py b/physics/n_body_simulation.py index 9db13b8dc07f..e288de3b9f2b 100644 --- a/physics/n_body_simulation.py +++ b/physics/n_body_simulation.py @@ -22,7 +22,7 @@ class Body: def __init__( - self: Body, + self, position_x: float, position_y: float, velocity_x: float, @@ -44,7 +44,7 @@ def __init__( self.color = color def update_velocity( - self: Body, force_x: float, force_y: float, delta_time: float + self, force_x: float, force_y: float, delta_time: float ) -> None: """ Euler algorithm for velocity @@ -71,7 +71,7 @@ def update_velocity( self.velocity_x += force_x * delta_time self.velocity_y += force_y * delta_time - def update_position(self: Body, delta_time: float) -> None: + def update_position(self, delta_time: float) -> None: """ Euler algorithm for position @@ -107,7 +107,7 @@ class BodySystem: """ def __init__( - self: BodySystem, + self, bodies: list[Body], gravitation_constant: float = 1.0, time_factor: float = 1.0, @@ -118,7 +118,7 @@ def __init__( self.time_factor = time_factor self.softening_factor = softening_factor - def update_system(self: BodySystem, delta_time: float) -> None: + def update_system(self, delta_time: float) -> None: """ For each body, loop through all other bodies to calculate the total force they exert on it. Use that force to update the body's velocity. From 790277e4361a9f14bf63b0313738885d58382c4e Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 4 Apr 2021 13:04:59 +0200 Subject: [PATCH 08/13] Apply suggestions from code review --- physics/n_body_simulation.py | 78 +++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/physics/n_body_simulation.py b/physics/n_body_simulation.py index e288de3b9f2b..e69b311cfc33 100644 --- a/physics/n_body_simulation.py +++ b/physics/n_body_simulation.py @@ -43,6 +43,14 @@ def __init__( self.size = size self.color = color + @property + def position(self) tuple[float, float]: + return self.position_x, self.position_y + + @property + def velocity(self) tuple[float, float]: + return self.position_x, self.position_y + def update_velocity( self, force_x: float, force_y: float, delta_time: float ) -> None: @@ -51,22 +59,22 @@ def update_velocity( >>> body_1 = Body(0.,0.,0.,0.) >>> body_1.update_velocity(1.,0.,1.) - >>> (body_1.velocity_x, body_1.velocity_y) + >>> body_1.velocity (1.0, 0.0) >>> body_1.update_velocity(1.,0.,1.) - >>> (body_1.velocity_x, body_1.velocity_y) + >>> body_1.velocity + (2.0, 0.0) >>> body_2 = Body(0.,0.,5.,0.) >>> body_2.update_velocity(0.,-10.,10.) - >>> (body_2.velocity_x, body_2.velocity_y) + >>> body_2.velocity (5.0, -100.0) >>> body_2.update_velocity(0.,-10.,10.) - >>> (body_2.velocity_x, body_2.velocity_y) + >>> body_2.velocity (5.0, -200.0) - """ self.velocity_x += force_x * delta_time self.velocity_y += force_y * delta_time @@ -77,20 +85,20 @@ def update_position(self, delta_time: float) -> None: >>> body_1 = Body(0.,0.,1.,0.) >>> body_1.update_position(1.) - >>> (body_1.position_x, body_1.position_y) + >>> body_1.position (1.0, 0.0) >>> body_1.update_position(1.) - >>> (body_1.position_x, body_1.position_y) + >>> body_1.position (2.0, 0.0) >>> body_2 = Body(10.,10.,0.,-2.) >>> body_2.update_position(1.) - >>> (body_2.position_x, body_2.position_y) + >>> body_2.position (10.0, 8.0) >>> body_2.update_position(1.) - >>> (body_2.position_x, body_2.position_y) + >>> body_2.position (10.0, 6.0) """ self.position_x += self.velocity_x * delta_time @@ -118,23 +126,28 @@ def __init__( self.time_factor = time_factor self.softening_factor = softening_factor + def __len__() -> int: + return len(self.bodies) + def update_system(self, delta_time: float) -> None: """ For each body, loop through all other bodies to calculate the total force they exert on it. Use that force to update the body's velocity. >>> body_system_1 = BodySystem([Body(0,0,0,0), Body(10,0,0,0)]) + >>> len(body_system_1) + 2 >>> body_system_1.update_system(1) - >>> (body_system_1.bodies[0].position_x, body_system_1.bodies[0].position_y) + >>> body_system_1.bodies[0].position (0.01, 0.0) - >>> (body_system_1.bodies[0].velocity_x, body_system_1.bodies[0].velocity_y) + >>> body_system_1.bodies[0].velocity (0.01, 0.0) >>> body_system_2 = BodySystem([Body(-10,0,0,0), Body(10,0,0,0, mass=4)], 1, 10) >>> body_system_2.update_system(1) - >>> (body_system_2.bodies[0].position_x, body_system_2.bodies[0].position_y) + >>> body_system_2.bodies[0].position (-9.0, 0.0) - >>> (body_system_2.bodies[0].velocity_x, body_system_2.bodies[0].velocity_y) + >>> body_system_2.bodies[0]velocity (0.1, 0.0) """ for body1 in self.bodies: @@ -239,13 +252,15 @@ def update(frame: int) -> list[plt.Circle]: plt.show() -def example_1() -> None: +def example_1() -> BodySystem: """ Example 1: figure-8 solution to the 3-body-problem This example can be seen as a test of the implementation: given the right initial conditions, the bodies should move in a figure-8. (initial conditions taken from http://www.artcompsci.org/vol_1/v1_web/node56.html) - No doctest provided since this function does not have a return value. + >>> body_system = example_1() + >>> len(body_system) + 3 """ position_x = 0.9700436 @@ -258,11 +273,10 @@ def example_1() -> None: Body(-position_x, -position_y, velocity_x, velocity_y, size=0.2, color="green"), Body(0, 0, -2 * velocity_x, -2 * velocity_y, size=0.2, color="blue"), ] - body_system1 = BodySystem(bodies1, time_factor=3) - plot("Figure-8 solution to the 3-body-problem", body_system1, -2, 2, -2, 2) + return BodySystem(bodies1, time_factor=3) -def example_2() -> None: +def example_2() -> BodySystem: """ Example 2: Moon's orbit around the earth This example can be seen as a test of the implementation: given the right @@ -285,18 +299,10 @@ def example_2() -> None: moon = Body(-earth_moon_distance, 0, 0, moon_velocity, moon_mass, 10000000, "grey") earth = Body(0, 0, 0, earth_velocity, earth_mass, 50000000, "blue") - body_system2 = BodySystem([earth, moon], gravitation_constant, time_factor=1000000) - plot( - "Moon's orbit around the earth", - body_system2, - -430000000, - 430000000, - -430000000, - 430000000, - ) + return BodySystem([earth, moon], gravitation_constant, time_factor=1000000) -def example_3() -> None: +def example_3() -> BodySystem: """ Example 3: Random system with many bodies. No doctest provided since this function does not have a return value. @@ -327,11 +333,17 @@ def example_3() -> None: size=0.05, ) ) - body_system3 = BodySystem(bodies, 0.01, 10, 0.1) - plot("Random system with many bodies", body_system3, -1.5, 1.5, -1.5, 1.5) + return BodySystem(bodies, 0.01, 10, 0.1) if __name__ == "__main__": - example_1() - example_2() - example_3() + plot("Figure-8 solution to the 3-body-problem", example_1(), -2, 2, -2, 2) + plot( + "Moon's orbit around the earth", + example_2(), + -430000000, + 430000000, + -430000000, + 430000000, + ) + plot("Random system with many bodies", example_3(), -1.5, 1.5, -1.5, 1.5) From 9bfafa617d1dff331a2b9ce4763f193c00e1da4f Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 4 Apr 2021 13:07:31 +0200 Subject: [PATCH 09/13] Update physics/n_body_simulation.py --- physics/n_body_simulation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/physics/n_body_simulation.py b/physics/n_body_simulation.py index e69b311cfc33..f14eea702879 100644 --- a/physics/n_body_simulation.py +++ b/physics/n_body_simulation.py @@ -44,7 +44,7 @@ def __init__( self.color = color @property - def position(self) tuple[float, float]: + def position(self) -> tuple[float, float]: return self.position_x, self.position_y @property From c063ff37971112a2837ea69c4655c4c63cfaae17 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 4 Apr 2021 13:08:10 +0200 Subject: [PATCH 10/13] Update physics/n_body_simulation.py --- physics/n_body_simulation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/physics/n_body_simulation.py b/physics/n_body_simulation.py index f14eea702879..e14260806608 100644 --- a/physics/n_body_simulation.py +++ b/physics/n_body_simulation.py @@ -48,7 +48,8 @@ def position(self) -> tuple[float, float]: return self.position_x, self.position_y @property - def velocity(self) tuple[float, float]: + def velocity(self) -> tuple[float, float]: + return self.position_x, self.position_y def update_velocity( From b60597779ebd9255ac75191673735691303f988a Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 4 Apr 2021 13:08:48 +0200 Subject: [PATCH 11/13] Update physics/n_body_simulation.py --- physics/n_body_simulation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/physics/n_body_simulation.py b/physics/n_body_simulation.py index e14260806608..e13ddd2a73d9 100644 --- a/physics/n_body_simulation.py +++ b/physics/n_body_simulation.py @@ -65,7 +65,6 @@ def update_velocity( >>> body_1.update_velocity(1.,0.,1.) >>> body_1.velocity - (2.0, 0.0) >>> body_2 = Body(0.,0.,5.,0.) From 2612bcb63a2327d98e5d6ba7eb710218168177b8 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 4 Apr 2021 13:13:27 +0200 Subject: [PATCH 12/13] Don't forget self --- physics/n_body_simulation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/physics/n_body_simulation.py b/physics/n_body_simulation.py index e13ddd2a73d9..c0d2ab6c1452 100644 --- a/physics/n_body_simulation.py +++ b/physics/n_body_simulation.py @@ -126,7 +126,7 @@ def __init__( self.time_factor = time_factor self.softening_factor = softening_factor - def __len__() -> int: + def __len__(self) -> int: return len(self.bodies) def update_system(self, delta_time: float) -> None: From 0368d9c635b1a958c9ed0f2c2cdd45cb51ca00fa Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 4 Apr 2021 13:18:43 +0200 Subject: [PATCH 13/13] Fix velocity --- physics/n_body_simulation.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/physics/n_body_simulation.py b/physics/n_body_simulation.py index c0d2ab6c1452..045a49f7ff00 100644 --- a/physics/n_body_simulation.py +++ b/physics/n_body_simulation.py @@ -49,8 +49,7 @@ def position(self) -> tuple[float, float]: @property def velocity(self) -> tuple[float, float]: - - return self.position_x, self.position_y + return self.velocity_x, self.velocity_y def update_velocity( self, force_x: float, force_y: float, delta_time: float @@ -147,7 +146,7 @@ def update_system(self, delta_time: float) -> None: >>> body_system_2.update_system(1) >>> body_system_2.bodies[0].position (-9.0, 0.0) - >>> body_system_2.bodies[0]velocity + >>> body_system_2.bodies[0].velocity (0.1, 0.0) """ for body1 in self.bodies: