Commit 35d14116 authored by Amaury BARRAL's avatar Amaury BARRAL

Lint & docstrings

parent f2183658
......@@ -9,7 +9,13 @@ class Bird:
lookV: np.ndarray
speedV: np.ndarray
def __init__(self, pos: np.ndarray, vel: float, ang_vel: float, angle: float):
def __init__(self, pos: np.ndarray, vel: float, ang_vel: float, angle: float) -> None:
"""
:param pos: position of bird
:param vel: norm of bird's velocity
:param ang_vel: norm of bird's angular velocity
:param angle: angle of bird (rad), counter-clockwise, 0 = to the right
"""
self.vel = vel
self.angle = angle
self.ang_vel = ang_vel
......@@ -18,12 +24,15 @@ class Bird:
self.speedV = None
self.update_calculated_props()
def clone(self):
def clone(self) -> "Bird":
return Bird(self.pos, self.vel, self.ang_vel, self.angle)
def __repr__(self) -> str:
return "Bird(%.2f, %.2f)" % (self.pos[0], self.pos[1])
def update_calculated_props(self) -> None:
"""
update lookV and speedV based on current properties
"""
self.lookV = np.array([np.cos(self.angle), np.sin(self.angle)])
self.speedV = self.lookV * self.vel
This diff is collapsed.
This diff is collapsed.
import datetime
import logging
import time
from typing import Callable
import numpy as np
......@@ -13,8 +14,16 @@ log.setLevel(logging.INFO)
def simulate(physics: Physics, dt: float, total_time: float, verbose_prop: float = .01,
output_file: str = "simulation_data/data.json",
evolve=lambda sky: None):
output_file: str = "simulation_data/data.json", evolve=lambda sky: Callable) -> None:
"""
Simulate bird flocking in a given sky (set in physics), following given physics
:param physics: the physics to follow
:param dt: time interval between each update
:param total_time: total simulation time
:param verbose_prop: how often to print progress
:param output_file: path where to output simulation results
:param evolve: function to call before each physics update (typically to make the environment evolve)
"""
timestamps = np.arange(0, total_time, dt)
total_frames = len(timestamps)
frames = []
......
import json
from typing import Any
import numpy as np
class NumpyEncoder(json.JSONEncoder):
def default(self, obj):
def default(self, obj) -> Any:
"""
Converts numpy types to python default types for easy storage in json files
:param obj:
:return:
"""
if isinstance(obj, np.ndarray):
return obj.tolist()
if isinstance(obj, np.int64) or isinstance(obj, np.int32):
......@@ -12,6 +19,11 @@ class NumpyEncoder(json.JSONEncoder):
def short_angle_dist(a0: float, a1: float) -> float:
"""
Shortest scalar difference between two angles
:param a0:
:param a1:
:return:
"""
da = (a1 - a0)
return (da + np.pi) % (2 * np.pi) - np.pi
import numpy as np
import itertools
from typing import Tuple, List
from typing import Tuple, List, Dict
import numpy as np
from God.Pandora import short_angle_dist
from God.Bird import Bird
from God.Pandora import short_angle_dist
from God.Sky import Sky
class Physics:
def __init__(self, sky: Sky, interaction_radius: float, eta: float):
"""
:param sky: Sky in which the birds evolve
:param interaction_radius: interaction radius of each bird
:param eta: temperature of the simulation, = angular randomness of angles
"""
self.sky = sky
sky.update_grid()
self.interaction_radius = interaction_radius
self.eta = eta
def get_groups(self) -> Tuple[List[list], dict]:
def get_groups(self) -> Tuple[List[List[Bird]], Dict[Bird, int]]:
"""
Get all groups in birds
:return: [i] = birds in group i, [bird] -> group of bird
"""
interactions = {}
for bird in self.sky.birds:
# Collect all bird in interaction range
......@@ -29,8 +39,8 @@ class Physics:
interactions[bird] = interact_with_close
# find groups
bird_to_group = {}
groups_to_bird = []
bird_to_group: Dict[Bird, int] = {}
groups_to_bird: List[List[Bird]] = []
curr_group = 0
def connect(bird_, group_):
......@@ -71,8 +81,13 @@ class Physics:
return groups_to_bird, bird_to_group
def get_interact_with(self, bird: Bird) -> list:
interact_with = []
def get_interact_with(self, bird: Bird) -> List[Bird]:
"""
Get all the birds that can interact with a given bird, according to the sky grid
:param bird: the bird whose interactions we are fetching
:return: all the birds that can interact with the initial bird, according to the sky grid
"""
interact_with: List[Bird] = []
gridpos = bird.pos // self.sky.gridstep
grid_interaction_radius = self.interaction_radius / self.sky.gridstep
......@@ -89,6 +104,13 @@ class Physics:
return interact_with
def get_interact_with_radius(self, bird: Bird) -> list:
"""
Get all the birds that directly interact with a given bird, according to the interaction radius
:param bird: the bird whose interactions we are fetching
:return: all the birds that are directly interacting with the initial bird, according to the interaction radius
:param bird:
:return:
"""
interact_with = self.get_interact_with(bird)
interact_with_close = []
......@@ -100,6 +122,10 @@ class Physics:
return interact_with_close
def advance2(self, dt: float) -> None:
"""
Advance one frame. Uses the updated physics
:param dt: time interval increment
"""
self.sky.update_grid()
birds_after_update = [bird.clone() for bird in self.sky.birds]
......@@ -132,6 +158,10 @@ class Physics:
bird.pos = (bird.pos + bird.speedV * dt) % self.sky.L
def advance(self, dt: float) -> None:
"""
Advance one frame. Uses the olds physics
:param dt: time interval increment
"""
self.sky.update_grid()
birds_after_update = [bird.clone() for bird in self.sky.birds]
......@@ -153,7 +183,8 @@ class Physics:
t_move = min(dt, t_move)
fluctuation_angle = bird_new.ang_vel * self.eta * (np.random.rand() - .5) * dt
bird_new.angle = (bird_new.angle + np.sign(diff) * bird_new.ang_vel * t_move + fluctuation_angle) % (2 * np.pi)
bird_new.angle = (bird_new.angle + np.sign(diff) * bird_new.ang_vel * t_move + fluctuation_angle) % (
2 * np.pi)
self.sky.birds = birds_after_update
# Verlet movement *after* updating directions
......
import json
import os
import pathlib
from typing import List, Union
from typing import List, Union, Iterable
import numpy as np
......@@ -10,9 +10,12 @@ from God.Pandora import NumpyEncoder
from God.Sky import Sky
def read_in_chunks(file_object, chunk_size=1024*1000*100):
def read_in_chunks(file_object, chunk_size=1024 * 1000 * 100) -> Iterable:
"""Lazy function (generator) to read a file piece by piece.
Default chunk size: 100M."""
Default chunk size: 100M.
:param file_object: python file object to read from
:param chunk_size: size of the chunks to load, in bytes
"""
while True:
data = file_object.read(chunk_size)
if not data:
......@@ -21,24 +24,43 @@ def read_in_chunks(file_object, chunk_size=1024*1000*100):
def make_path_available(path: str) -> None:
"""
Makes sure the path (folder or file) is available to save to
Will create the path if it doesn4t exist
Will *not* warn if a file already exists with the same name
"""
pathlib.Path(os.path.dirname(path)).mkdir(parents=True, exist_ok=True)
def save_data_dirname(data: Union[list, dict], output_dir: str, output_file: str) -> None:
"""
Save the data in the directory output_dir, with the name output_file
"""
return save_data(data, os.path.join(output_dir, output_file))
def save_data(data: Union[list, dict], output_file: str) -> None:
"""
Save the data in the path output_file
"""
make_path_available(output_file)
with open(output_file, "w") as f:
json.dump(data, f, cls=NumpyEncoder)
def load_data_dirname(input_dir: str, input_file: str) -> Union[list, dict]:
"""
Load the data in the path input_dir, with the name input_file
:return: the data as python types
"""
return load_data(os.path.join(input_dir, input_file))
def load_data(input_file: str) -> Union[list, dict]:
"""
Load the data in the path input_file
:return: the data as python types
"""
f = open(input_file, "r")
content = ""
for piece in read_in_chunks(f):
......@@ -47,6 +69,13 @@ def load_data(input_file: str) -> Union[list, dict]:
def recreate_frame(frame: List[List], L: float, grid_step: float) -> Sky:
"""
Recreate a python frame (Sky) from loaded data
:param frame: the bird's data
:param L: size of the sky
:param grid_step: size of the sky's grid step
:return: recreated Sky frame
"""
sky = Sky(L, grid_step)
for bird in frame:
pos, angle, vel, ang_vel = bird
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment