Commit a19e16bd authored by Hippalectryon's avatar Hippalectryon

docstrings, minor fixes, advance <-> advance2

parent 3bb3a118
......@@ -58,6 +58,17 @@ class Processor:
"""
updates self.to_process with features to process
:param to_process: features to process
groups: get all the birds groups
group_size: get the distribution of group sizes
group_to_size: get the size of each group
group_size_avg: avg size distribution
group_size_avg_fit: power law fit of avg size distribution
group_hulls: get the convex hulls of each group
avg_speed: avg speed of all the birds
avg_angle: avg angle of all the birds
correlations: angle spatial correlations
correlations_fit: exponential fit of sparial angle correlations
"""
self.to_process["groups"] = "groups" in to_process
self.to_process["group_size"] = "group_size" in to_process
......@@ -427,7 +438,7 @@ class Processor:
Save results to file
"""
def save_prop_name(prop_name: str)-> None:
def save_prop_name(prop_name: str) -> None:
SaveAndLoad.save_data_dirname(self.data_holders[prop_name], output_file, "%s.json" % prop_name)
# save the actual data
......
......@@ -173,6 +173,22 @@ class Visualiser:
"""
Chose which features to draw
:param to_draw: list fo features to draw
quiver: draw the birds
avg_speed: draw the avg speed @ time
avg_angle: draw the avg angle @ time
avg_polar: draw the avg speed and angle in polar projection
angle_pdf: draw the current angle pdf in polar projection, normalized for a projected polar area of 1
correlations: draw the angle correlations @ space
correlations_fit: draw the angle correlations fit @ space
correlation_length: draw the fitted correlation length @ time
group_size: draw the current group size disribution
group_size_avg: draw the avg group size disribution
group_size_avg_fit: draw the power law fit of avg group size disribution
group_dimension: draw the current group length disribution
group_dimension_avg: draw the avg group length disribution
group_dimension_avg_fit: draw the power law avg group length disribution
evolution_group_size: draw the group size of bird 0 @ time
"""
self.to_draw["quiver"] = "quiver" in to_draw
self.to_draw["avg_speed"] = "avg_speed" in to_draw
......@@ -314,7 +330,7 @@ class Visualiser:
ax_group_dimension = self.subplots["group_dimension"]
max_group_size = self.options["max_group_size"]
ax_group_dimension.set_xlim(1, max_group_size)
ax_group_dimension.set_ylim(0, L**2/100)
ax_group_dimension.set_ylim(0, L ** 2 / 100)
self.layout_artists["group_dimension_text"] = ax_group_dimension.text(
0.02, 1.05, '', transform=ax_group_dimension.transAxes)
if self.to_draw["group_dimension"]:
......@@ -390,12 +406,12 @@ class Visualiser:
"""
frame = self.processed_data["_simulation"]["frames"][frame_num]
angles = np.array([bird[1] for bird in frame])
angles_extended = np.concatenate((angles, angles+2*np.pi, angles-2*np.pi))
angles_extended = np.concatenate((angles, angles + 2 * np.pi, angles - 2 * np.pi))
kde = gaussian_kde(angles_extended)
n_points = 100
dist_space = np.linspace(0, 2*np.pi, n_points)
rescaling = np.sqrt(np.sum(kde(dist_space)**2)*np.pi/n_points)
self.layout_artists["angle_pdf"].set_data(dist_space, kde(dist_space)/rescaling)
dist_space = np.linspace(0, 2 * np.pi, n_points)
rescaling = np.sqrt(np.sum(kde(dist_space) ** 2) * np.pi / n_points)
self.layout_artists["angle_pdf"].set_data(dist_space, kde(dist_space) / rescaling)
def plot_correlations(self, frame_num: int) -> None:
"""
......@@ -515,7 +531,8 @@ class Visualiser:
a, b, r_squared = data
x_data = np.array(range(len(self.processed_data["group_hulls_dimensions_avg"][frame_num]) + 1))
self.layout_artists["group_dimension_avg_fit"].set_data(x_data, fit(x_data, a, b))
self.layout_artists["group_dimension_text"].set_text("$R^2$ = %.5f\n $bx^a$: a=%.2f, b=%.2f" % (r_squared, a, b))
self.layout_artists["group_dimension_text"].set_text(
"$R^2$ = %.5f\n $bx^a$: a=%.2f, b=%.2f" % (r_squared, a, b))
def plot_evolution_group_size(self, frame_num: int) -> None:
"""
......
......@@ -121,7 +121,7 @@ class Physics:
return interact_with_close
def advance2(self, dt: float) -> None:
def advance(self, dt: float) -> None:
"""
Advance one frame. Uses the updated physics
:param dt: time interval increment
......@@ -157,7 +157,7 @@ class Physics:
bird.update_calculated_props()
bird.pos = (bird.pos + bird.speedV * dt) % self.sky.L
def advance(self, dt: float) -> None:
def advance2(self, dt: float) -> None:
"""
Advance one frame. Uses the olds physics
:param dt: time interval increment
......
import io
import json
import os
import pathlib
import io
from typing import List, Union, Iterable
import dropbox
......
from typing import List, Tuple
import numpy as np
from typing import List
from God.Bird import Bird
......@@ -8,6 +9,10 @@ class Sky:
birds: List[Bird]
def __init__(self, L: float, gridstep: float):
"""
:param L: length of square space
:param gridstep: length of gridstep for computing interactions
"""
self.L = L
self.gridstep = gridstep
......@@ -19,13 +24,24 @@ class Sky:
self.birds = []
def truedistance(self, v1: np.ndarray, v2: np.ndarray) -> float:
"""
:param v1: first position
:param v2: second position
:return: true distance in periodic space between two positions
"""
diff = v1 - v2
return np.sqrt(min(diff[0], self.L - diff[0]) ** 2 + min(diff[1], self.L - diff[1]) ** 2)
def get_avg_speed(self) -> float:
"""
:return: norm of avg speed of all birds
"""
return np.linalg.norm(np.mean([bird.speedV for bird in self.birds], axis=0))
def get_avg_angle(self) -> float:
"""
:return: avg angle of all birds
"""
median_cos = 0
median_sin = 0
for bird in self.birds:
......@@ -33,7 +49,12 @@ class Sky:
median_sin += np.sin(bird.angle)
return np.arctan2(median_sin, median_cos)
def get_angles_correlations(self, n: int = np.inf) -> tuple:
def get_angles_correlations(self, n: int = np.inf) -> Tuple[np.ndarray, np.ndarray]:
"""
Angle spatial correlations
:param n: number of points on which to calculate the correlation
:return: (distances, corresponding correlations)
"""
pos = np.array([bird.pos for bird in self.birds])
speedV = np.array([bird.speedV for bird in self.birds])
......@@ -58,12 +79,21 @@ class Sky:
return np.array(sorted(distances)), np.array([x for _, x in sorted(zip(distances, correlations))])
def add_bird(self, bird: Bird) -> None:
"""
Add given bird to sky
"""
self.birds.append(bird)
def init_grid(self) -> None:
"""
Initialize interaction grid
"""
self.grid = np.empty((self.gridL, self.gridL), dtype=object)
def update_grid(self) -> None:
"""
Update interaction grid from bird positions
"""
self.init_grid()
for bird in self.birds:
gridpos = bird.pos // self.gridstep
......@@ -73,10 +103,19 @@ class Sky:
self.grid[i, j].append(bird)
def add_n_random_birds(self, n: int, vel: float, ang_vel: float) -> None:
"""
Adds birds randomly in space
:param n: number of birds to add
:param vel: velocity of all birds
:param ang_vel: angular velocity of all birds
"""
for _ in range(n):
theta = np.random.rand() * np.pi * 2
bird = Bird(np.random.rand(2) * self.L, vel, ang_vel, theta)
self.add_bird(bird)
def get_max_vel(self) -> float:
"""
:return: max velocity of all birds
"""
return np.max([bird.vel for bird in self.birds])
import logging
from typing import Callable
from typing import Callable, Tuple
import numpy as np
import paramiko
......@@ -19,6 +19,19 @@ log.setLevel(logging.INFO)
def launch_simulation_random(output_file: str, L: float, n_birds: int, vel: float = 1, ang_vel: float = np.pi / 2,
interaction_radius: float = 1,
eta: float = .5, dt: float = 1, total_time: float = 100, evolve: Callable = None) -> None:
"""
Launches a simulation where all birds have random initial position and angles
:param output_file: path to save results
:param L: length of sky
:param n_birds: number of birds
:param vel: bird velocity
:param ang_vel: bird max angular velocity
:param interaction_radius: bird interaction radius
:param eta: temperature
:param dt: interval between each frame
:param total_time: total experiment time
:param evolve: function called at each frame, can be used to create/remove birds
"""
gridstep = interaction_radius
sky = Sky(L, gridstep)
sky.add_n_random_birds(n_birds, vel, ang_vel)
......@@ -27,10 +40,30 @@ def launch_simulation_random(output_file: str, L: float, n_birds: int, vel: floa
def launch_two_groups(output_file: str, L: float, n_birds_1: int, n_birds_2: int, radius_1: int, radius_2: int,
center_1: list, center_2: list, angle_1: float, angle_2: float, vel: float = 1,
center_1: Tuple[float, float], center_2: Tuple[float, float], angle_1: float, angle_2: float,
vel: float = 1,
ang_vel: float = np.pi / 2,
interaction_radius: float = 1,
eta: float = .1, dt: float = 1, total_time: float = 100) -> None:
"""
Launches two groups of birds with given initial positions and angle. Great for studying head-on collisions.
:param output_file: path to save results
:param L: length of sky
:param n_birds_1: number of birds in 1st group
:param n_birds_2: number of birds in 2nd group
:param radius_1: radius of 1st group
:param radius_2: radius of 2nd group
:param center_1: center of 1st group (x, y)
:param center_2: center of 2nd group (x, y)
:param angle_1: angle of 1st group initial velocity
:param angle_2: angle of 2nd group initial velocity
:param vel: velocity of all birds
:param ang_vel: max angular velocity of all birds
:param interaction_radius: interaction radius of all birds
:param eta: temperature
:param dt: interval between each frame
:param total_time: total simulation time
"""
gridstep = interaction_radius
sky = Sky(L, gridstep)
group_1_positions = (np.random.rand(n_birds_1, 2) - .5) * radius_1 + np.array(center_1)
......@@ -45,9 +78,10 @@ def launch_two_groups(output_file: str, L: float, n_birds_1: int, n_birds_2: int
def start_experiment(output_dir: str, L: float, n_birds: int, eta: float = .5, total_time:
float = 100, num_exp: int = 1, vel: float = 0.1) -> None:
"""subdir = ""
if nb_experiments > 1:"""
float = 100, num_exp: int = 1, vel: float = 0.1) -> None:
"""
Start an experiment with given parameters, called via remote SSH master, see doc.
"""
subdir = "N_" + str(n_birds) + "_L_" + str(L) + "_Eta_" + str(eta) + "/"
name = subdir + "N_" + str(n_birds) + "_L_" + str(L) + "_Eta_" + str(eta) + "_angle_vel_" + "pi" + "_T_" + str(
......@@ -60,7 +94,6 @@ def start_experiment(output_dir: str, L: float, n_birds: int, eta: float = .5, t
to_process = ["avg_speed", "avg_angle", "group_size", "group_size_avg", "group_size_avg_fit", "groups",
"correlations",
"correlations_fit", "group_to_size", "group_hulls"]
Processor().process("phy571/simulation_data/" + output_dir + "/" + name + ".json", "phy571/processing_data/" +
output_dir + "/" + name, verbose_prop=.1,
to_process=to_process,
......@@ -68,6 +101,10 @@ def start_experiment(output_dir: str, L: float, n_birds: int, eta: float = .5, t
def ssh_experiments():
"""
Launch SSH experiments on any number of remote hosts
"""
addresses = ['129.104.254.97', '129.104.254.98']
""", '129.104.254.99', '129.104.254.100', '129.104.254.101',
'129.104.254.102', '129.104.254.103', '129.104.254.104', '129.104.254.105', '129.104.254.106',
......@@ -129,8 +166,8 @@ def ssh_experiments():
ssh.connect(current_server_ip, username=login, password=passw)
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(
f"./phy571-birds-flocking/venv/bin/python3.6 ./phy571-birds-flocking/Amaury/remote.py --file {file} --n {Ns[i]} --l {Ls[i]} --eta {Eta[k]} "
f"--t {t} --nb_exp {nb_exp} --vel {vel}")
f"./phy571-birds-flocking/venv/bin/python3.6 ./phy571-birds-flocking/Amaury/remote.py --file {file}"
f" --n {Ns[i]} --l {Ls[i]} --eta {Eta[k]} --t {t} --nb_exp {nb_exp} --vel {vel}")
# ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(f"pkill ./phy571-birds-flocking/venv/bin/python3.6 2> \&1 1> \&1")
# ssh_stdout, ssh_stderr = ssh_stdout.readlines(), ssh_stderr.readlines()
# ssh.close()
......@@ -138,4 +175,21 @@ def ssh_experiments():
if __name__ == "__main__":
ssh_experiments()
launch_simulation_random("simulation_data/test.json", 100, 10000, eta=.45, total_time=500)
to_process = ["avg_speed", "avg_angle", "group_size", "group_size_avg", "group_size_avg_fit", "groups",
"correlations", "correlations_fit", "group_to_size", "group_hulls"]
Processor().process("simulation_data/test.json", "processing_data/test", verbose_prop=.1,
to_process=to_process, options={"correlations_stochastic_points": 5000})
to_draw = ["quiver", "avg_speed", "avg_angle", "avg_polar", "angle_pdf", "correlations", "correlations_fit",
"correlations_length",
"group_size", "group_size_avg", "group_size_avg_fit", "group_dimension", "group_dimension_avg",
"group_dimension_avg_fit",
"evolution_group_size"]
options = {
"max_num_groups": 1000,
"max_group_size": 800,
}
Visualiser("processing_data/test", "visualisations/test.mp4", to_draw, "simulation_data/test.json",
options=options).vizualize()
......@@ -13,11 +13,21 @@ log.setLevel(logging.INFO)
@click.option('--file', help="output_dir", type=str)
@click.option('--n', help="number of birds", type=int)
@click.option('--l', help="size of sky", type=float)
@click.option('--eta', help="noise", type=float)
@click.option('--t', help="simulation time", type=float)
@click.option('--eta', help="noise", type=float)
@click.option('--t', help="simulation time", type=float)
@click.option('--nb_exp', help="number of experiments", type=int)
@click.option('--vel', help="velocity", type=float)
def main(file, n, l, eta, t, nb_exp, vel):
def main(file, n, l, eta, t, nb_exp, vel) -> None:
"""
Called from command-line, launches experiments
:param file: output file
:param n: number of birds
:param l: length of sky
:param eta: temperature
:param t: total time
:param nb_exp: number of repeated experiments
:param vel: velocity of each bird
"""
name = f"N_{n}_L_{l}_Eta_{eta}_T_{t}"
log.info(f"Starting remote experiment {name}")
for i in range(nb_exp):
......
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