From da243f946c109259e4c75943bd24515dd4d9e516 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Wed, 20 May 2020 21:31:20 -0700 Subject: [PATCH 01/89] Add several accelerations (with/without noise, with/without failsafes) to custom output --- flow/controllers/base_controller.py | 18 +++++++++------ flow/core/kernel/vehicle/base.py | 20 ++++++++++++++-- flow/core/kernel/vehicle/traci.py | 36 +++++++++++++++++++++++------ flow/data_pipeline/data_pipeline.py | 19 ++++++++++----- 4 files changed, 71 insertions(+), 22 deletions(-) diff --git a/flow/controllers/base_controller.py b/flow/controllers/base_controller.py index 7adcdf310..c417bb73a 100755 --- a/flow/controllers/base_controller.py +++ b/flow/controllers/base_controller.py @@ -88,8 +88,10 @@ def get_action(self, env): float the modified form of the acceleration """ - # clear the current stored accel_without_noise of this vehicle None - env.k.vehicle.update_accel_without_noise(self.veh_id, None) + # clear the current stored accel_no_noise_no_failsafe of this vehicle None + env.k.vehicle.update_accel_no_noise_no_failsafe(self.veh_id, None) + env.k.vehicle.update_accel_no_noise_with_failsafe(self.veh_id, None) + env.k.vehicle.update_accel_with_noise_no_failsafe(self.veh_id, None) # this is to avoid abrupt decelerations when a vehicle has just entered # a network and it's data is still not subscribed @@ -110,23 +112,25 @@ def get_action(self, env): # store the acceleration without noise to each vehicle # run fail safe if requested - accel_without_noise = accel + env.k.vehicle.update_accel_no_noise_no_failsafe(self.veh_id, accel) if self.fail_safe == 'instantaneous': - accel_without_noise = self.get_safe_action_instantaneous(env, accel_without_noise) + accel_no_noise_with_failsafe = self.get_safe_action_instantaneous(env, accel) elif self.fail_safe == 'safe_velocity': - accel_without_noise = self.get_safe_velocity_action(env, accel_without_noise) - env.k.vehicle.update_accel_without_noise(self.veh_id, accel_without_noise) + accel_no_noise_with_failsafe = self.get_safe_velocity_action(env, accel) + env.k.vehicle.update_accel_no_noise_with_failsafe(self.veh_id, accel_no_noise_with_failsafe) # add noise to the accelerations, if requested if self.accel_noise > 0: accel += np.sqrt(env.sim_step) * np.random.normal(0, self.accel_noise) + env.k.vehicle.update_accel_with_noise_no_failsafe(self.veh_id, accel) # run the fail-safes, if requested if self.fail_safe == 'instantaneous': accel = self.get_safe_action_instantaneous(env, accel) elif self.fail_safe == 'safe_velocity': accel = self.get_safe_velocity_action(env, accel) - + env.k.vehicle.update_accel_with_noise_with_failsafe(self.veh_id, accel) + return accel def get_safe_action_instantaneous(self, env, action): diff --git a/flow/core/kernel/vehicle/base.py b/flow/core/kernel/vehicle/base.py index 647ef37fe..ed53773cb 100644 --- a/flow/core/kernel/vehicle/base.py +++ b/flow/core/kernel/vehicle/base.py @@ -693,7 +693,15 @@ def get_accel(self, veh_id): """Return the acceleration of vehicle with veh_id.""" raise NotImplementedError - def update_accel_without_noise(self, veh_id, accel_without_noise): + def update_accel_no_noise_no_failsafe(self, veh_id, accel_no_noise_no_failsafe): + """Update stored acceleration without noise of vehicle with veh_id.""" + raise NotImplementedError + + def update_accel_no_noise_with_failsafe(self, veh_id, accel_no_noise_with_failsafe): + """Update stored acceleration without noise of vehicle with veh_id.""" + raise NotImplementedError + + def update_accel_with_noise_no_failsafe(self, veh_id, accel_with_noise_no_failsafe): """Update stored acceleration without noise of vehicle with veh_id.""" raise NotImplementedError @@ -701,7 +709,15 @@ def get_2d_position(self, veh_id, error=-1001): """Return (x, y) position of vehicle with veh_id.""" raise NotImplementedError - def get_accel_without_noise(self, veh_id): + def get_accel_no_noise_no_failsafe(self, veh_id): + """Return the acceleration without noise of vehicle with veh_id.""" + raise NotImplementedError + + def get_accel_no_noise_with_failsafe(self, veh_id): + """Return the acceleration without noise of vehicle with veh_id.""" + raise NotImplementedError + + def get_accel_with_noise_no_failsafe(self, veh_id): """Return the acceleration without noise of vehicle with veh_id.""" raise NotImplementedError diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index 2a4e06257..4a1916617 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -113,7 +113,9 @@ def initialize(self, vehicles): self.__vehicles[veh_id] = dict() self.__vehicles[veh_id]['type'] = typ['veh_id'] self.__vehicles[veh_id]['initial_speed'] = typ['initial_speed'] - self.__vehicles[veh_id]["accel_without_noise"] = None + self.__vehicles[veh_id]["accel_no_noise_no_failsafe"] = None + self.__vehicles[veh_id]["accel_no_noise_with_failsafe"] = None + self.__vehicles[veh_id]["accel_with_noise_no_failsafe"] = None self.num_vehicles += 1 if typ['acceleration_controller'][0] == RLController: self.num_rl_vehicles += 1 @@ -1130,15 +1132,35 @@ def get_accel(self, veh_id): self.__vehicles[veh_id]["accel"] = None return self.__vehicles[veh_id]["accel"] - def update_accel_without_noise(self, veh_id, accel_without_noise): + def update_accel_no_noise_no_failsafe(self, veh_id, accel_no_noise_no_failsafe): """See parent class.""" - self.__vehicles[veh_id]["accel_without_noise"] = accel_without_noise + self.__vehicles[veh_id]["accel_no_noise_no_failsafe"] = accel_no_noise_no_failsafe - def get_accel_without_noise(self, veh_id): + def update_accel_no_noise_with_failsafe(self, veh_id, accel_no_noise_with_failsafe): """See parent class.""" - if "accel_without_noise" not in self.__vehicles[veh_id]: - self.__vehicles[veh_id]["accel_without_noise"] = None - return self.__vehicles[veh_id]["accel_without_noise"] + self.__vehicles[veh_id]["accel_no_noise_with_failsafe"] = accel_no_noise_with_failsafe + + def update_accel_with_noise_no_failsafe(self, veh_id, accel_with_noise_no_failsafe): + """See parent class.""" + self.__vehicles[veh_id]["accel_with_noise_no_failsafe"] = accel_with_noise_no_failsafe + + def get_accel_no_noise_no_failsafe(self, veh_id): + """See parent class.""" + if "accel_no_noise_no_failsafe" not in self.__vehicles[veh_id]: + self.__vehicles[veh_id]["accel_no_noise_no_failsafe"] = None + return self.__vehicles[veh_id]["accel_no_noise_no_failsafe"] + + def get_accel_no_noise_with_failsafe(self, veh_id): + """See parent class.""" + if "accel_no_noise_with_failsafe" not in self.__vehicles[veh_id]: + self.__vehicles[veh_id]["accel_no_noise_with_failsafe"] = None + return self.__vehicles[veh_id]["accel_no_noise_with_failsafe"] + + def get_accel_with_noise_no_failsafe(self, veh_id): + """See parent class.""" + if "accel_with_noise_no_failsafe" not in self.__vehicles[veh_id]: + self.__vehicles[veh_id]["accel_with_noise_no_failsafe"] = None + return self.__vehicles[veh_id]["accel_with_noise_no_failsafe"] def get_realized_accel(self, veh_id): """See parent class.""" diff --git a/flow/data_pipeline/data_pipeline.py b/flow/data_pipeline/data_pipeline.py index a999b6eb1..11d85cb0d 100644 --- a/flow/data_pipeline/data_pipeline.py +++ b/flow/data_pipeline/data_pipeline.py @@ -89,9 +89,11 @@ def upload_to_s3(bucket_name, bucket_key, file_path, only_query): def extra_init(): - """Return the dictionary with all the feild pre-populated with empty list.""" - extra_info = {"time_step": [], "id": [], "x": [], "y": [], "speed": [], "headway": [], "acceleration": [], - "accel_without_noise": [], "realilzed_accel": [], "leader_id": [], "follower_id": [], + """Return the dictionary with all the field pre-populated with empty list.""" + extra_info = {"time_step": [], "id": [], "x": [], "y": [], "speed": [], "headway": [], + "target_accel_with_noise_with_failsafe": [], "target_accel_no_noise_no_failsafe": [], + "target_accel_with_noise_no_failsafe": [], "target_accel_no_noise_with_failsafe": [], + "realized_accel": [], "leader_id": [], "follower_id": [], "leader_rel_speed": [], "road_grade": [], "source_id": []} return extra_info @@ -102,13 +104,18 @@ def get_extra_info(veh_kernel, extra_info, veh_ids): extra_info["time_step"].append(veh_kernel.get_timestep(vid) / 1000) extra_info["id"].append(vid) extra_info["headway"].append(veh_kernel.get_headway(vid)) - extra_info["acceleration"].append(veh_kernel.get_accel(vid)) + extra_info["target_accel_with_noise_with_failsafe"].append(veh_kernel.get_accel(vid)) extra_info["leader_id"].append(veh_kernel.get_leader(vid)) extra_info["follower_id"].append(veh_kernel.get_follower(vid)) extra_info["leader_rel_speed"].append(veh_kernel.get_speed( veh_kernel.get_leader(vid)) - veh_kernel.get_speed(vid)) - extra_info["accel_without_noise"].append(veh_kernel.get_accel_without_noise(vid)) - extra_info["realilzed_accel"].append(veh_kernel.get_realized_accel(vid)) + extra_info["target_accel_no_noise_no_failsafe"].append( + veh_kernel.get_accel_no_noise_no_failsafe(vid)) + extra_info["target_accel_with_noise_no_failsafe"].append( + veh_kernel.get_accel_with_noise_no_failsafe(vid)) + extra_info["target_accel_no_noise_with_failsafe"].append( + veh_kernel.get_accel_no_noise_with_failsafe(vid)) + extra_info["realized_accel"].append(veh_kernel.get_realized_accel(vid)) extra_info["road_grade"].append(veh_kernel.get_road_grade(vid)) position = veh_kernel.get_2d_position(vid) extra_info["x"].append(position[0]) From 951c755672a37bffcec0ac723545746b5e1d0e73 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Wed, 20 May 2020 21:44:15 -0700 Subject: [PATCH 02/89] update queries with new column names --- flow/data_pipeline/query.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index 2ee794507..43ad45216 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -27,14 +27,14 @@ class QueryStrings(Enum): SAMPLE = """ SELECT * - FROM trajectory_table + FROM fact_vehicle_trace WHERE date = \'{date}\' AND partition_name=\'{partition}\' LIMIT 15; """ UPDATE_PARTITION = """ - ALTER TABLE trajectory_table + ALTER TABLE fact_vehicle_trace ADD IF NOT EXISTS PARTITION (date = \'{date}\', partition_name=\'{partition}\'); """ @@ -44,10 +44,10 @@ class QueryStrings(Enum): id, time_step, speed, - acceleration, + target_accel_with_noise_with_failsafe AS acceleration, road_grade, source_id - FROM trajectory_table + FROM fact_vehicle_trace WHERE 1 = 1 AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' @@ -60,10 +60,10 @@ class QueryStrings(Enum): id, time_step, speed, - accel_without_noise AS acceleration, + target_accel_no_noise_with_failsafe AS acceleration, road_grade, source_id - FROM trajectory_table + FROM fact_vehicle_trace WHERE 1 = 1 AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' @@ -75,14 +75,14 @@ class QueryStrings(Enum): SELECT id, time_step, - accel_without_noise, + target_accel_no_noise_with_failsafe, road_grade, source_id, time_step - LAG(time_step, 1) OVER (PARTITION BY id ORDER BY time_step ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS sim_step, LAG(speed, 1) OVER (PARTITION BY id ORDER BY time_step ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_speed - FROM trajectory_table + FROM fact_vehicle_trace WHERE 1 = 1 AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' @@ -90,8 +90,8 @@ class QueryStrings(Enum): SELECT id, time_step, - prev_speed + accel_without_noise * sim_step AS speed, - accel_without_noise AS acceleration, + prev_speed + target_accel_no_noise_with_failsafe * sim_step AS speed, + target_accel_no_noise_with_failsafe AS acceleration, road_grade, source_id FROM lagged_timestep From df0bb664e80e5fe9819c6246663b8602212da243 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Wed, 20 May 2020 21:47:44 -0700 Subject: [PATCH 03/89] fix flake8 issues --- flow/controllers/base_controller.py | 2 +- flow/data_pipeline/query.py | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/flow/controllers/base_controller.py b/flow/controllers/base_controller.py index c417bb73a..3f6a0f4ae 100755 --- a/flow/controllers/base_controller.py +++ b/flow/controllers/base_controller.py @@ -130,7 +130,7 @@ def get_action(self, env): elif self.fail_safe == 'safe_velocity': accel = self.get_safe_velocity_action(env, accel) env.k.vehicle.update_accel_with_noise_with_failsafe(self.veh_id, accel) - + return accel def get_safe_action_instantaneous(self, env, action): diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index 43ad45216..e403b51f8 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -2,7 +2,13 @@ from enum import Enum # tags for different queries -tags = {"energy": ["POWER_DEMAND_MODEL", "POWER_DEMAND_MODEL_DENOISED_ACCEL", "POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL"]} +tags = { + "energy": [ + "POWER_DEMAND_MODEL", + "POWER_DEMAND_MODEL_DENOISED_ACCEL", + "POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL" + ] + } VEHICLE_POWER_DEMAND_FINAL_SELECT = """ SELECT @@ -52,7 +58,8 @@ class QueryStrings(Enum): AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' ) - {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL', 'regular_cte')) + {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL', + 'regular_cte')) POWER_DEMAND_MODEL_DENOISED_ACCEL = """ WITH denoised_accel_cte AS ( @@ -68,7 +75,8 @@ class QueryStrings(Enum): AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' ) - {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL_DENOISED_ACCEL', 'denoised_accel_cte')) + {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL_DENOISED_ACCEL', + 'denoised_accel_cte')) POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL = """ WITH lagged_timestep AS ( @@ -96,4 +104,5 @@ class QueryStrings(Enum): source_id FROM lagged_timestep ) - {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL', 'denoised_speed_cte')) + {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL', + 'denoised_speed_cte')) From 863f360809eac6fad1ae26eba0b197759a7c666c Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Wed, 20 May 2020 21:51:46 -0700 Subject: [PATCH 04/89] remove trailing whitespaces --- flow/data_pipeline/query.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index e403b51f8..8e8196f6f 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -4,8 +4,8 @@ # tags for different queries tags = { "energy": [ - "POWER_DEMAND_MODEL", - "POWER_DEMAND_MODEL_DENOISED_ACCEL", + "POWER_DEMAND_MODEL", + "POWER_DEMAND_MODEL_DENOISED_ACCEL", "POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL" ] } @@ -58,7 +58,7 @@ class QueryStrings(Enum): AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' ) - {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL', + {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL', 'regular_cte')) POWER_DEMAND_MODEL_DENOISED_ACCEL = """ @@ -104,5 +104,5 @@ class QueryStrings(Enum): source_id FROM lagged_timestep ) - {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL', + {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL', 'denoised_speed_cte')) From 87dcff271ac1aa5450516f2592940eddeb149fe4 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Wed, 20 May 2020 21:31:20 -0700 Subject: [PATCH 05/89] Add several accelerations (with/without noise, with/without failsafes) to custom output --- flow/controllers/base_controller.py | 18 +++++++++------ flow/core/kernel/vehicle/base.py | 20 ++++++++++++++-- flow/core/kernel/vehicle/traci.py | 36 +++++++++++++++++++++++------ flow/data_pipeline/data_pipeline.py | 19 ++++++++++----- 4 files changed, 71 insertions(+), 22 deletions(-) diff --git a/flow/controllers/base_controller.py b/flow/controllers/base_controller.py index 7adcdf310..c417bb73a 100755 --- a/flow/controllers/base_controller.py +++ b/flow/controllers/base_controller.py @@ -88,8 +88,10 @@ def get_action(self, env): float the modified form of the acceleration """ - # clear the current stored accel_without_noise of this vehicle None - env.k.vehicle.update_accel_without_noise(self.veh_id, None) + # clear the current stored accel_no_noise_no_failsafe of this vehicle None + env.k.vehicle.update_accel_no_noise_no_failsafe(self.veh_id, None) + env.k.vehicle.update_accel_no_noise_with_failsafe(self.veh_id, None) + env.k.vehicle.update_accel_with_noise_no_failsafe(self.veh_id, None) # this is to avoid abrupt decelerations when a vehicle has just entered # a network and it's data is still not subscribed @@ -110,23 +112,25 @@ def get_action(self, env): # store the acceleration without noise to each vehicle # run fail safe if requested - accel_without_noise = accel + env.k.vehicle.update_accel_no_noise_no_failsafe(self.veh_id, accel) if self.fail_safe == 'instantaneous': - accel_without_noise = self.get_safe_action_instantaneous(env, accel_without_noise) + accel_no_noise_with_failsafe = self.get_safe_action_instantaneous(env, accel) elif self.fail_safe == 'safe_velocity': - accel_without_noise = self.get_safe_velocity_action(env, accel_without_noise) - env.k.vehicle.update_accel_without_noise(self.veh_id, accel_without_noise) + accel_no_noise_with_failsafe = self.get_safe_velocity_action(env, accel) + env.k.vehicle.update_accel_no_noise_with_failsafe(self.veh_id, accel_no_noise_with_failsafe) # add noise to the accelerations, if requested if self.accel_noise > 0: accel += np.sqrt(env.sim_step) * np.random.normal(0, self.accel_noise) + env.k.vehicle.update_accel_with_noise_no_failsafe(self.veh_id, accel) # run the fail-safes, if requested if self.fail_safe == 'instantaneous': accel = self.get_safe_action_instantaneous(env, accel) elif self.fail_safe == 'safe_velocity': accel = self.get_safe_velocity_action(env, accel) - + env.k.vehicle.update_accel_with_noise_with_failsafe(self.veh_id, accel) + return accel def get_safe_action_instantaneous(self, env, action): diff --git a/flow/core/kernel/vehicle/base.py b/flow/core/kernel/vehicle/base.py index 647ef37fe..ed53773cb 100644 --- a/flow/core/kernel/vehicle/base.py +++ b/flow/core/kernel/vehicle/base.py @@ -693,7 +693,15 @@ def get_accel(self, veh_id): """Return the acceleration of vehicle with veh_id.""" raise NotImplementedError - def update_accel_without_noise(self, veh_id, accel_without_noise): + def update_accel_no_noise_no_failsafe(self, veh_id, accel_no_noise_no_failsafe): + """Update stored acceleration without noise of vehicle with veh_id.""" + raise NotImplementedError + + def update_accel_no_noise_with_failsafe(self, veh_id, accel_no_noise_with_failsafe): + """Update stored acceleration without noise of vehicle with veh_id.""" + raise NotImplementedError + + def update_accel_with_noise_no_failsafe(self, veh_id, accel_with_noise_no_failsafe): """Update stored acceleration without noise of vehicle with veh_id.""" raise NotImplementedError @@ -701,7 +709,15 @@ def get_2d_position(self, veh_id, error=-1001): """Return (x, y) position of vehicle with veh_id.""" raise NotImplementedError - def get_accel_without_noise(self, veh_id): + def get_accel_no_noise_no_failsafe(self, veh_id): + """Return the acceleration without noise of vehicle with veh_id.""" + raise NotImplementedError + + def get_accel_no_noise_with_failsafe(self, veh_id): + """Return the acceleration without noise of vehicle with veh_id.""" + raise NotImplementedError + + def get_accel_with_noise_no_failsafe(self, veh_id): """Return the acceleration without noise of vehicle with veh_id.""" raise NotImplementedError diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index 2a4e06257..4a1916617 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -113,7 +113,9 @@ def initialize(self, vehicles): self.__vehicles[veh_id] = dict() self.__vehicles[veh_id]['type'] = typ['veh_id'] self.__vehicles[veh_id]['initial_speed'] = typ['initial_speed'] - self.__vehicles[veh_id]["accel_without_noise"] = None + self.__vehicles[veh_id]["accel_no_noise_no_failsafe"] = None + self.__vehicles[veh_id]["accel_no_noise_with_failsafe"] = None + self.__vehicles[veh_id]["accel_with_noise_no_failsafe"] = None self.num_vehicles += 1 if typ['acceleration_controller'][0] == RLController: self.num_rl_vehicles += 1 @@ -1130,15 +1132,35 @@ def get_accel(self, veh_id): self.__vehicles[veh_id]["accel"] = None return self.__vehicles[veh_id]["accel"] - def update_accel_without_noise(self, veh_id, accel_without_noise): + def update_accel_no_noise_no_failsafe(self, veh_id, accel_no_noise_no_failsafe): """See parent class.""" - self.__vehicles[veh_id]["accel_without_noise"] = accel_without_noise + self.__vehicles[veh_id]["accel_no_noise_no_failsafe"] = accel_no_noise_no_failsafe - def get_accel_without_noise(self, veh_id): + def update_accel_no_noise_with_failsafe(self, veh_id, accel_no_noise_with_failsafe): """See parent class.""" - if "accel_without_noise" not in self.__vehicles[veh_id]: - self.__vehicles[veh_id]["accel_without_noise"] = None - return self.__vehicles[veh_id]["accel_without_noise"] + self.__vehicles[veh_id]["accel_no_noise_with_failsafe"] = accel_no_noise_with_failsafe + + def update_accel_with_noise_no_failsafe(self, veh_id, accel_with_noise_no_failsafe): + """See parent class.""" + self.__vehicles[veh_id]["accel_with_noise_no_failsafe"] = accel_with_noise_no_failsafe + + def get_accel_no_noise_no_failsafe(self, veh_id): + """See parent class.""" + if "accel_no_noise_no_failsafe" not in self.__vehicles[veh_id]: + self.__vehicles[veh_id]["accel_no_noise_no_failsafe"] = None + return self.__vehicles[veh_id]["accel_no_noise_no_failsafe"] + + def get_accel_no_noise_with_failsafe(self, veh_id): + """See parent class.""" + if "accel_no_noise_with_failsafe" not in self.__vehicles[veh_id]: + self.__vehicles[veh_id]["accel_no_noise_with_failsafe"] = None + return self.__vehicles[veh_id]["accel_no_noise_with_failsafe"] + + def get_accel_with_noise_no_failsafe(self, veh_id): + """See parent class.""" + if "accel_with_noise_no_failsafe" not in self.__vehicles[veh_id]: + self.__vehicles[veh_id]["accel_with_noise_no_failsafe"] = None + return self.__vehicles[veh_id]["accel_with_noise_no_failsafe"] def get_realized_accel(self, veh_id): """See parent class.""" diff --git a/flow/data_pipeline/data_pipeline.py b/flow/data_pipeline/data_pipeline.py index a999b6eb1..11d85cb0d 100644 --- a/flow/data_pipeline/data_pipeline.py +++ b/flow/data_pipeline/data_pipeline.py @@ -89,9 +89,11 @@ def upload_to_s3(bucket_name, bucket_key, file_path, only_query): def extra_init(): - """Return the dictionary with all the feild pre-populated with empty list.""" - extra_info = {"time_step": [], "id": [], "x": [], "y": [], "speed": [], "headway": [], "acceleration": [], - "accel_without_noise": [], "realilzed_accel": [], "leader_id": [], "follower_id": [], + """Return the dictionary with all the field pre-populated with empty list.""" + extra_info = {"time_step": [], "id": [], "x": [], "y": [], "speed": [], "headway": [], + "target_accel_with_noise_with_failsafe": [], "target_accel_no_noise_no_failsafe": [], + "target_accel_with_noise_no_failsafe": [], "target_accel_no_noise_with_failsafe": [], + "realized_accel": [], "leader_id": [], "follower_id": [], "leader_rel_speed": [], "road_grade": [], "source_id": []} return extra_info @@ -102,13 +104,18 @@ def get_extra_info(veh_kernel, extra_info, veh_ids): extra_info["time_step"].append(veh_kernel.get_timestep(vid) / 1000) extra_info["id"].append(vid) extra_info["headway"].append(veh_kernel.get_headway(vid)) - extra_info["acceleration"].append(veh_kernel.get_accel(vid)) + extra_info["target_accel_with_noise_with_failsafe"].append(veh_kernel.get_accel(vid)) extra_info["leader_id"].append(veh_kernel.get_leader(vid)) extra_info["follower_id"].append(veh_kernel.get_follower(vid)) extra_info["leader_rel_speed"].append(veh_kernel.get_speed( veh_kernel.get_leader(vid)) - veh_kernel.get_speed(vid)) - extra_info["accel_without_noise"].append(veh_kernel.get_accel_without_noise(vid)) - extra_info["realilzed_accel"].append(veh_kernel.get_realized_accel(vid)) + extra_info["target_accel_no_noise_no_failsafe"].append( + veh_kernel.get_accel_no_noise_no_failsafe(vid)) + extra_info["target_accel_with_noise_no_failsafe"].append( + veh_kernel.get_accel_with_noise_no_failsafe(vid)) + extra_info["target_accel_no_noise_with_failsafe"].append( + veh_kernel.get_accel_no_noise_with_failsafe(vid)) + extra_info["realized_accel"].append(veh_kernel.get_realized_accel(vid)) extra_info["road_grade"].append(veh_kernel.get_road_grade(vid)) position = veh_kernel.get_2d_position(vid) extra_info["x"].append(position[0]) From d192a9f5dc1ca2622875cd26997198c1889d213c Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Wed, 20 May 2020 21:44:15 -0700 Subject: [PATCH 06/89] update queries with new column names --- flow/data_pipeline/query.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index 8dde9474d..689818e65 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -30,14 +30,14 @@ class QueryStrings(Enum): SAMPLE = """ SELECT * - FROM trajectory_table + FROM fact_vehicle_trace WHERE date = \'{date}\' AND partition_name=\'{partition}\' LIMIT 15; """ UPDATE_PARTITION = """ - ALTER TABLE trajectory_table + ALTER TABLE fact_vehicle_trace ADD IF NOT EXISTS PARTITION (date = \'{date}\', partition_name=\'{partition}\'); """ @@ -47,10 +47,10 @@ class QueryStrings(Enum): id, time_step, speed, - acceleration, + target_accel_with_noise_with_failsafe AS acceleration, road_grade, source_id - FROM trajectory_table + FROM fact_vehicle_trace WHERE 1 = 1 AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' @@ -63,10 +63,10 @@ class QueryStrings(Enum): id, time_step, speed, - accel_without_noise AS acceleration, + target_accel_no_noise_with_failsafe AS acceleration, road_grade, source_id - FROM trajectory_table + FROM fact_vehicle_trace WHERE 1 = 1 AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' @@ -78,14 +78,14 @@ class QueryStrings(Enum): SELECT id, time_step, - accel_without_noise, + target_accel_no_noise_with_failsafe, road_grade, source_id, time_step - LAG(time_step, 1) OVER (PARTITION BY id ORDER BY time_step ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS sim_step, LAG(speed, 1) OVER (PARTITION BY id ORDER BY time_step ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_speed - FROM trajectory_table + FROM fact_vehicle_trace WHERE 1 = 1 AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' @@ -93,8 +93,8 @@ class QueryStrings(Enum): SELECT id, time_step, - prev_speed + accel_without_noise * sim_step AS speed, - accel_without_noise AS acceleration, + prev_speed + target_accel_no_noise_with_failsafe * sim_step AS speed, + target_accel_no_noise_with_failsafe AS acceleration, road_grade, source_id FROM lagged_timestep From 92a745dd3d517135f3bef6b69782c212ffbfd336 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Wed, 20 May 2020 21:47:44 -0700 Subject: [PATCH 07/89] fix flake8 issues --- flow/controllers/base_controller.py | 2 +- flow/data_pipeline/query.py | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/flow/controllers/base_controller.py b/flow/controllers/base_controller.py index c417bb73a..3f6a0f4ae 100755 --- a/flow/controllers/base_controller.py +++ b/flow/controllers/base_controller.py @@ -130,7 +130,7 @@ def get_action(self, env): elif self.fail_safe == 'safe_velocity': accel = self.get_safe_velocity_action(env, accel) env.k.vehicle.update_accel_with_noise_with_failsafe(self.veh_id, accel) - + return accel def get_safe_action_instantaneous(self, env, action): diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index 689818e65..928bb5d47 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -2,7 +2,13 @@ from enum import Enum # tags for different queries -tags = {"energy": ["POWER_DEMAND_MODEL", "POWER_DEMAND_MODEL_DENOISED_ACCEL", "POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL"]} +tags = { + "energy": [ + "POWER_DEMAND_MODEL", + "POWER_DEMAND_MODEL_DENOISED_ACCEL", + "POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL" + ] + } VEHICLE_POWER_DEMAND_FINAL_SELECT = """ SELECT @@ -55,7 +61,8 @@ class QueryStrings(Enum): AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' ) - {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL', 'regular_cte')) + {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL', + 'regular_cte')) POWER_DEMAND_MODEL_DENOISED_ACCEL = """ WITH denoised_accel_cte AS ( @@ -71,7 +78,8 @@ class QueryStrings(Enum): AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' ) - {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL_DENOISED_ACCEL', 'denoised_accel_cte')) + {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL_DENOISED_ACCEL', + 'denoised_accel_cte')) POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL = """ WITH lagged_timestep AS ( @@ -99,4 +107,5 @@ class QueryStrings(Enum): source_id FROM lagged_timestep ) - {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL', 'denoised_speed_cte')) + {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL', + 'denoised_speed_cte')) From 215d4abb938e7b7032f227b9a2e6997092164bc6 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Wed, 20 May 2020 21:51:46 -0700 Subject: [PATCH 08/89] remove trailing whitespaces --- flow/data_pipeline/query.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index 928bb5d47..e8ac34abc 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -4,8 +4,8 @@ # tags for different queries tags = { "energy": [ - "POWER_DEMAND_MODEL", - "POWER_DEMAND_MODEL_DENOISED_ACCEL", + "POWER_DEMAND_MODEL", + "POWER_DEMAND_MODEL_DENOISED_ACCEL", "POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL" ] } @@ -61,7 +61,7 @@ class QueryStrings(Enum): AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' ) - {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL', + {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL', 'regular_cte')) POWER_DEMAND_MODEL_DENOISED_ACCEL = """ @@ -107,5 +107,5 @@ class QueryStrings(Enum): source_id FROM lagged_timestep ) - {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL', + {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL', 'denoised_speed_cte')) From 97f3ccdf34d4fb1323a25d479abc4ccab616c8f1 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Sun, 24 May 2020 23:20:29 -0700 Subject: [PATCH 09/89] fix accel with noise with failsafe output --- flow/controllers/base_controller.py | 1 + flow/core/kernel/vehicle/base.py | 20 ++++++++++++++------ flow/core/kernel/vehicle/traci.py | 10 ++++++++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/flow/controllers/base_controller.py b/flow/controllers/base_controller.py index 3f6a0f4ae..1169ce5b8 100755 --- a/flow/controllers/base_controller.py +++ b/flow/controllers/base_controller.py @@ -113,6 +113,7 @@ def get_action(self, env): # store the acceleration without noise to each vehicle # run fail safe if requested env.k.vehicle.update_accel_no_noise_no_failsafe(self.veh_id, accel) + accel_no_noise_with_failsafe = accel if self.fail_safe == 'instantaneous': accel_no_noise_with_failsafe = self.get_safe_action_instantaneous(env, accel) elif self.fail_safe == 'safe_velocity': diff --git a/flow/core/kernel/vehicle/base.py b/flow/core/kernel/vehicle/base.py index ed53773cb..f6f8ee382 100644 --- a/flow/core/kernel/vehicle/base.py +++ b/flow/core/kernel/vehicle/base.py @@ -694,15 +694,19 @@ def get_accel(self, veh_id): raise NotImplementedError def update_accel_no_noise_no_failsafe(self, veh_id, accel_no_noise_no_failsafe): - """Update stored acceleration without noise of vehicle with veh_id.""" + """Update stored acceleration without noise without failsafe of vehicle with veh_id.""" raise NotImplementedError def update_accel_no_noise_with_failsafe(self, veh_id, accel_no_noise_with_failsafe): - """Update stored acceleration without noise of vehicle with veh_id.""" + """Update stored acceleration without noise with failsafe of vehicle with veh_id.""" raise NotImplementedError def update_accel_with_noise_no_failsafe(self, veh_id, accel_with_noise_no_failsafe): - """Update stored acceleration without noise of vehicle with veh_id.""" + """Update stored acceleration with noise without failsafe of vehicle with veh_id.""" + raise NotImplementedError + + def update_accel_with_noise_with_failsafe(self, veh_id, accel_with_noise_with_failsafe): + """Update stored acceleration with noise with failsafe of vehicle with veh_id.""" raise NotImplementedError def get_2d_position(self, veh_id, error=-1001): @@ -710,15 +714,19 @@ def get_2d_position(self, veh_id, error=-1001): raise NotImplementedError def get_accel_no_noise_no_failsafe(self, veh_id): - """Return the acceleration without noise of vehicle with veh_id.""" + """Return the acceleration without noise without failsafe of vehicle with veh_id.""" raise NotImplementedError def get_accel_no_noise_with_failsafe(self, veh_id): - """Return the acceleration without noise of vehicle with veh_id.""" + """Return the acceleration without noise with failsafe of vehicle with veh_id.""" raise NotImplementedError def get_accel_with_noise_no_failsafe(self, veh_id): - """Return the acceleration without noise of vehicle with veh_id.""" + """Return the acceleration with noise without failsafe of vehicle with veh_id.""" + raise NotImplementedError + + def get_accel_with_noise_with_failsafe(self, veh_id): + """Return the acceleration with noise with failsafe of vehicle with veh_id.""" raise NotImplementedError def get_realized_accel(self, veh_id): diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index 4a1916617..1c0b5f19b 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -1144,6 +1144,10 @@ def update_accel_with_noise_no_failsafe(self, veh_id, accel_with_noise_no_failsa """See parent class.""" self.__vehicles[veh_id]["accel_with_noise_no_failsafe"] = accel_with_noise_no_failsafe + def update_accel_with_noise_with_failsafe(self, veh_id, accel_with_noise_with_failsafe): + """See parent class.""" + self.__vehicles[veh_id]["accel_with_noise_with_failsafe"] = accel_with_noise_with_failsafe + def get_accel_no_noise_no_failsafe(self, veh_id): """See parent class.""" if "accel_no_noise_no_failsafe" not in self.__vehicles[veh_id]: @@ -1162,6 +1166,12 @@ def get_accel_with_noise_no_failsafe(self, veh_id): self.__vehicles[veh_id]["accel_with_noise_no_failsafe"] = None return self.__vehicles[veh_id]["accel_with_noise_no_failsafe"] + def get_accel_with_noise_with_failsafe(self, veh_id): + """See parent class.""" + if "accel_with_noise_with_failsafe" not in self.__vehicles[veh_id]: + self.__vehicles[veh_id]["accel_with_noise_with_failsafe"] = None + return self.__vehicles[veh_id]["accel_with_noise_with_failsafe"] + def get_realized_accel(self, veh_id): """See parent class.""" return (self.get_speed(veh_id) - self.get_previous_speed(veh_id)) / self.sim_step From 36086521fff4c3e3d0728423e36af15bfa242403 Mon Sep 17 00:00:00 2001 From: Yashar Zeinali Farid <34227133+Yasharzf@users.noreply.github.com> Date: Thu, 7 May 2020 23:51:53 -0700 Subject: [PATCH 10/89] get not departed vehicles (#922) * added function to kernel/vehicle to get number of not departed vehiles * fixed over indentation of the docstring * indentation edit * pep8 Co-authored-by: AboudyKreidieh --- flow/core/kernel/vehicle/traci.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index b89e981be..58eddfd1c 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -220,6 +220,10 @@ def update(self, reset): self.num_not_departed += sim_obs[tc.VAR_LOADED_VEHICLES_NUMBER] - \ sim_obs[tc.VAR_DEPARTED_VEHICLES_NUMBER] + # update the number of not departed vehicles + self.num_not_departed += sim_obs[tc.VAR_LOADED_VEHICLES_NUMBER] - \ + sim_obs[tc.VAR_DEPARTED_VEHICLES_NUMBER] + # update the "headway", "leader", and "follower" variables for veh_id in self.__ids: try: @@ -543,6 +547,10 @@ def get_fuel_consumption(self, veh_id, error=-1001): return [self.get_fuel_consumption(vehID, error) for vehID in veh_id] return self.__sumo_obs.get(veh_id, {}).get(tc.VAR_FUELCONSUMPTION, error) * ml_to_gallons + def get_num_not_departed(self): + """See parent class.""" + return self.num_not_departed + def get_previous_speed(self, veh_id, error=-1001): """See parent class.""" if isinstance(veh_id, (list, np.ndarray)): From 0a83576b80c2955ac8e09f88e0b159c836a887c4 Mon Sep 17 00:00:00 2001 From: Eugene Vinitsky Date: Wed, 18 Mar 2020 16:43:22 -0700 Subject: [PATCH 11/89] Add an on ramp option --- examples/exp_configs/non_rl/i210_subnetwork.py | 2 +- flow/envs/base.py | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/examples/exp_configs/non_rl/i210_subnetwork.py b/examples/exp_configs/non_rl/i210_subnetwork.py index 25565bb49..194da1099 100644 --- a/examples/exp_configs/non_rl/i210_subnetwork.py +++ b/examples/exp_configs/non_rl/i210_subnetwork.py @@ -117,7 +117,7 @@ sim=SumoParams( sim_step=0.4, render=False, - color_by_speed=True, + color_by_speed=False, use_ballistic=True ), diff --git a/flow/envs/base.py b/flow/envs/base.py index baf8270b5..f2067d947 100644 --- a/flow/envs/base.py +++ b/flow/envs/base.py @@ -405,8 +405,18 @@ def step(self, rl_actions): # test if the environment should terminate due to a collision or the # time horizon being met done = (self.time_counter >= self.env_params.sims_per_step * - (self.env_params.warmup_steps + self.env_params.horizon) - or crash) + (self.env_params.warmup_steps + self.env_params.horizon)) + if crash: + print( + "**********************************************************\n" + "**********************************************************\n" + "**********************************************************\n" + "WARNING: There was a crash. \n" + "**********************************************************\n" + "**********************************************************\n" + "**********************************************************" + ) + # compute the info for each agent infos = {} From 9b649efbda97a80f3c926ec1fc9838b76f27aa60 Mon Sep 17 00:00:00 2001 From: Eugene Vinitsky Date: Thu, 19 Mar 2020 12:10:07 -0700 Subject: [PATCH 12/89] Upgrade the network to not have keepclear value on the junctions --- .../exp_configs/templates/sumo/test2.net.xml | 78 ++++++++++++++----- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/examples/exp_configs/templates/sumo/test2.net.xml b/examples/exp_configs/templates/sumo/test2.net.xml index 00e3edcd5..16170b917 100644 --- a/examples/exp_configs/templates/sumo/test2.net.xml +++ b/examples/exp_configs/templates/sumo/test2.net.xml @@ -1,5 +1,41 @@ + + @@ -4680,24 +4716,24 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + @@ -4801,10 +4837,10 @@ - + - - + + From c18ec58b8a1e0a036b111649dbd2b0f05bd28c55 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Mon, 6 Apr 2020 15:28:57 -0700 Subject: [PATCH 13/89] datapip pipeline implemented --- examples/data_pipeline.py | 179 ++++++++++++++++++++++++++++ examples/query.py | 8 ++ examples/run_query.py | 34 ++++++ examples/simulate.py | 10 +- flow/controllers/base_controller.py | 12 ++ flow/core/experiment.py | 27 ++++- flow/core/kernel/vehicle/base.py | 16 +++ flow/core/kernel/vehicle/traci.py | 15 +++ 8 files changed, 299 insertions(+), 2 deletions(-) create mode 100644 examples/data_pipeline.py create mode 100644 examples/query.py create mode 100644 examples/run_query.py diff --git a/examples/data_pipeline.py b/examples/data_pipeline.py new file mode 100644 index 000000000..5fdc30cf2 --- /dev/null +++ b/examples/data_pipeline.py @@ -0,0 +1,179 @@ +import pandas as pd +import boto3 +from botocore.exceptions import ClientError +from examples.query import QueryStrings +from time import time + + +def generate_trajectory_table(data_path, extra_info, partition_name): + """ generate desired output for the trajectory_table based on standard SUMO emission + + Parameters + ---------- + data_path : str + path to the standard SUMO emission + extra_info: dict + extra information needed in the trajectory table, collected from flow + partition_name: str + the name of the partition to put this output to + Returns + ------- + output_file_path: str + the local path of the outputted csv file + """ + raw_output = pd.read_csv(data_path, index_col=["time", "id"]) + required_cols = {"time", "id", "speed", "x", "y"} + raw_output = raw_output.drop(set(raw_output.columns) - required_cols, axis=1) + + extra_info = pd.DataFrame.from_dict(extra_info) + extra_info.set_index(["time", "id"]) + raw_output = raw_output.merge(extra_info, how="left", left_on=["time", "id"], right_on=["time", "id"]) + + # add the partition column + raw_output['partition'] = partition_name + + output_file_path = data_path[:-4]+"_trajectory.csv" + raw_output.to_csv(output_file_path, index=False) + return output_file_path + + +def upload_to_s3(bucket_name, bucket_key, file_path): + """ upload a file to S3 bucket + + Parameters + ---------- + bucket_name : str + the bucket to upload to + bucket_key: str + the key within the bucket for the file + file_path: str + the path of the file to be uploaded + """ + s3 = boto3.resource("s3") + s3.Bucket(bucket_name).upload_file(file_path, bucket_key) + return + + +class AthenaQuery: + + def __init__(self): + self.MAX_WAIT = 60 + self.client = boto3.client("athena") + self.existing_partitions = self.get_existing_partitions() + + def get_existing_partitions(self): + """prints the existing partitions in the S3 bucket""" + + response = self.client.start_query_execution( + QueryString='SHOW PARTITIONS trajectory_table', + QueryExecutionContext={ + 'Database': 'simulation' + }, + WorkGroup='primary' + ) + if self.wait_for_execution(response['QueryExecutionId']): + raise RuntimeError("get current partitions timed out") + response = self.client.get_query_results( + QueryExecutionId=response['QueryExecutionId'], + MaxResults=1000 + ) + return [data['Data'][0]['VarCharValue'].split('=')[-1] for data in response['ResultSet']['Rows']] + + def check_status(self, execution_id): + """ Return the status of the execution with given id + + Parameters + ---------- + execution_id : string + id of the execution that is checked for + Returns + ------- + status: str + QUEUED|RUNNING|SUCCEEDED|FAILED|CANCELLED + """ + + response = self.client.get_query_execution( + QueryExecutionId=execution_id + ) + return response['QueryExecution']['Status']['State'] + + def wait_for_execution(self, execution_id): + """ wait for the execution to finish or time-out + + Parameters + ---------- + execution_id : str + id of the execution this is watiing for + Returns + ------- + time_out: bool + True if time-out, False if success + Raises + ------ + RuntimeError: if execution failed or get canceled + """ + start = time() + while time() - start < self.MAX_WAIT: + state = self.check_status(execution_id) + if state == 'FAILED' or state == 'CANCELLED': + raise RuntimeError("update partition failed") + elif state == 'SUCCEEDED': + return False + return True + + def update_partition(self, partition): + """ load the given partition to the trajectory_table on Athena + + Parameters + ---------- + partition : str + the new partition that needs to be loaded + """ + response = self.client.start_query_execution( + QueryString=QueryStrings['UPDATE_PARTITION'].value.format(partition=partition), + QueryExecutionContext={ + 'Database': 'simulation' + }, + WorkGroup='primary' + ) + if self.wait_for_execution(response['QueryExecutionId']): + raise RuntimeError("update partition timed out") + self.existing_partitions.append(partition) + return + + def run_query(self, query_name, result_location="s3://brent.experiments/query-result/", partition="default"): + """ start the execution of a query, does not wait for it to finish + + Parameters + ---------- + query_name : str + name of the query in QueryStrings enum that will be run + result_location: str, optional + location on the S3 bucket where the result will be stored + partition: str, optional + name of the partition to run this query on + Returns + ------- + execution_id: str + the execution id of the execution started by this method + Raises + ------ + ValueError: if tries to run a query not existed in QueryStrings enum + """ + if query_name not in QueryStrings.__members__: + raise ValueError("query not existed: please add it to query.py") + + if partition not in self.existing_partitions: + self.update_partition(partition) + + response = self.client.start_query_execution( + QueryString=QueryStrings[query_name].value.format(partition=partition), + QueryExecutionContext={ + 'Database': 'simulation' + }, + ResultConfiguration={ + 'OutputLocation': result_location, + }, + WorkGroup='primary' + ) + return response['QueryExecutionId'] \ No newline at end of file diff --git a/examples/query.py b/examples/query.py new file mode 100644 index 000000000..3fbbe69e1 --- /dev/null +++ b/examples/query.py @@ -0,0 +1,8 @@ +from enum import Enum + +tags = {} + + +class QueryStrings(Enum): + SAMPLE = "SELECT * FROM trajectory_table WHERE partition_name=\'{partition}\' LIMIT 15;" + UPDATE_PARTITION = "ALTER TABLE trajectory_table ADD IF NOT EXISTS PARTITION (partition_name=\'{partition}\');" \ No newline at end of file diff --git a/examples/run_query.py b/examples/run_query.py new file mode 100644 index 000000000..7b4a5af7d --- /dev/null +++ b/examples/run_query.py @@ -0,0 +1,34 @@ +import argparse +import sys +from examples.data_pipeline import AthenaQuery +from examples.query import QueryStrings + +parser = argparse.ArgumentParser(prog="run_query", description="runs query on AWS Athena and stores the result to" + "a S3 location") +parser.add_argument("--run", type=str, nargs="+") +parser.add_argument("--result_location", type=str, nargs='?', default="s3://brent.experiments/query-result/") +parser.add_argument("--partition", type=str, nargs='?', default="default") +parser.add_argument("--list_partitions", action="store_true") +parser.add_argument("--check_status", type=str, nargs='+') +parser.add_argument("--list_queries", action="store_true") + + +if __name__ == "__main__": + args = parser.parse_args() + queryEngine = AthenaQuery() + + if args.run: + execution_ids = [] + for query_name in args.run: + execution_ids.append(queryEngine.run_query(query_name, args.result_location, args.partition)) + print(execution_ids) + if args.list_partitions: + print(queryEngine.existing_partitions) + if args.check_status: + status = dict() + for execution_id in args.check_status: + status[execution_id] = queryEngine.check_status(execution_id) + print(status) + if args.list_queries: + for q in QueryStrings: + print(q) diff --git a/examples/simulate.py b/examples/simulate.py index 848f030a4..f54bb38d9 100644 --- a/examples/simulate.py +++ b/examples/simulate.py @@ -48,6 +48,12 @@ def parse_args(args): action='store_true', help='Specifies whether to generate an emission file from the ' 'simulation.') + parser.add_argument( + '--to_aws', + type=str, nargs='?', default=None, const="default", + help='Specifies the name of the partition to store the output' + 'file on S3. Putting not None value for this argument' + 'automatically set gen_emission to True.') return parser.parse_known_args(args)[0] @@ -55,6 +61,8 @@ def parse_args(args): if __name__ == "__main__": flags = parse_args(sys.argv[1:]) + flags.gen_emission = flags.gen_emission or flags.to_aws + # Get the flow_params object. module = __import__("exp_configs.non_rl", fromlist=[flags.exp_config]) flow_params = getattr(module, flags.exp_config).flow_params @@ -83,4 +91,4 @@ def parse_args(args): exp = Experiment(flow_params, callables) # Run for the specified number of rollouts. - exp.run(flags.num_runs, convert_to_csv=flags.gen_emission) + exp.run(flags.num_runs, convert_to_csv=flags.gen_emission, partition_name=flags.to_aws) diff --git a/flow/controllers/base_controller.py b/flow/controllers/base_controller.py index 4004b1c4d..6e6734764 100755 --- a/flow/controllers/base_controller.py +++ b/flow/controllers/base_controller.py @@ -88,6 +88,9 @@ def get_action(self, env): float the modified form of the acceleration """ + # clear the current stored accel_without_noise of this vehicle None + env.k.vehicle.update_accel_without_noise(self.veh_id, None) + # this is to avoid abrupt decelerations when a vehicle has just entered # a network and it's data is still not subscribed if len(env.k.vehicle.get_edge(self.veh_id)) == 0: @@ -105,6 +108,15 @@ def get_action(self, env): if accel is None: return None + # store the acceleration without noise to each vehicle + # run fail safe if requested + accel_without_noice = accel + if self.fail_safe == 'instantaneous': + accel_without_noice = self.get_safe_action_instantaneous(env, accel_without_noice) + elif self.fail_safe == 'safe_velocity': + accel_without_noice = self.get_safe_velocity_action(env, accel_without_noice) + env.k.vehicle.update_accel_without_noise(self.veh_id, accel_without_noice) + # add noise to the accelerations, if requested if self.accel_noise > 0: accel += np.sqrt(env.sim_step) * np.random.normal(0, self.accel_noise) diff --git a/flow/core/experiment.py b/flow/core/experiment.py index a0497b595..1f0cce355 100755 --- a/flow/core/experiment.py +++ b/flow/core/experiment.py @@ -1,6 +1,7 @@ """Contains an experiment class for running simulations.""" from flow.core.util import emission_to_csv from flow.utils.registry import make_create_env +from examples.data_pipeline import generate_trajectory_table, upload_to_s3 import datetime import logging import time @@ -85,7 +86,7 @@ def __init__(self, flow_params, custom_callables=None): logging.info("Initializing environment.") - def run(self, num_runs, rl_actions=None, convert_to_csv=False): + def run(self, num_runs, rl_actions=None, convert_to_csv=False, partition_name=None): """Run the given network for a set number of runs. Parameters @@ -98,6 +99,10 @@ def run(self, num_runs, rl_actions=None, convert_to_csv=False): convert_to_csv : bool Specifies whether to convert the emission file created by sumo into a csv file + partition_name: str + Specifies the S3 partition you want to store the output file, + will be used to later for query. If NONE, won't upload output + to S3. Returns ------- @@ -136,6 +141,8 @@ def rl_actions(*_): # time profiling information t = time.time() times = [] + extra_info = {"time": [], "id": [], "headway": [], "acceleration": [], "leader_id": [], "follower_id": [], + "leader_rel_speed": [], "accel_without_noise": [], "road_grade": []} for i in range(num_runs): ret = 0 @@ -153,6 +160,18 @@ def rl_actions(*_): vel.append(np.mean(self.env.k.vehicle.get_speed(veh_ids))) ret += reward + # collect additional information for the data pipeline + for vid in veh_ids: + extra_info["time"].append(self.env.k.vehicle.get_timestep(veh_ids[0]) / 1000) + extra_info["id"].append(vid) + extra_info["headway"].append(self.env.k.vehicle.get_headway(vid)) + extra_info["acceleration"].append(self.env.k.vehicle.get_accel(vid)) + extra_info["leader_id"].append(self.env.k.vehicle.get_leader(vid)) + extra_info["follower_id"].append(self.env.k.vehicle.get_follower(vid)) + extra_info["leader_rel_speed"].append(self.env.k.vehicle.get_speed(self.env.k.vehicle.get_leader(vid)) - self.env.k.vehicle.get_speed(vid)) + extra_info["accel_without_noise"].append(self.env.k.vehicle.get_accel_without_noise(vid)) + extra_info["road_grade"].append(self.env.k.vehicle.get_road_grade(vid)) + # Compute the results for the custom callables. for (key, lambda_func) in self.custom_callables.items(): custom_vals[key].append(lambda_func(self.env)) @@ -195,4 +214,10 @@ def rl_actions(*_): # Delete the .xml version of the emission file. os.remove(emission_path) + output_file = generate_trajectory_table(emission_path[:-4] + ".csv", extra_info, partition_name) + + if partition_name: + upload_to_s3('brent.experiments', 'trajectory-output/' + 'partition_name=' + partition_name + '/' + + output_file.split('/')[-1], output_file) + return info_dict diff --git a/flow/core/kernel/vehicle/base.py b/flow/core/kernel/vehicle/base.py index 706504027..0c992503c 100644 --- a/flow/core/kernel/vehicle/base.py +++ b/flow/core/kernel/vehicle/base.py @@ -684,3 +684,19 @@ def get_max_speed(self, veh_id, error): float """ raise NotImplementedError + + ########################################################################### + # Methods for Datapipeline # + ########################################################################### + + def get_accel(self, veh_id): + """ see traci class """ + raise NotImplementedError + + def update_accel_without_noise(self, veh_id, accel_without_noise): + """ see traci class """ + raise NotImplementedError + + def get_accel_without_noise(self, veh_id): + """ see traci class """ + raise NotImplementedError diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index 58eddfd1c..b06ab112b 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -113,6 +113,7 @@ def initialize(self, vehicles): self.__vehicles[veh_id] = dict() self.__vehicles[veh_id]['type'] = typ['veh_id'] self.__vehicles[veh_id]['initial_speed'] = typ['initial_speed'] + self.__vehicles[veh_id]["accel_without_noise"] = None self.num_vehicles += 1 if typ['acceleration_controller'][0] == RLController: self.num_rl_vehicles += 1 @@ -1128,3 +1129,17 @@ def get_max_speed(self, veh_id, error=-1001): def set_max_speed(self, veh_id, max_speed): """See parent class.""" self.kernel_api.vehicle.setMaxSpeed(veh_id, max_speed) + + # add for data pipeline + def get_accel(self, veh_id): + return (self.get_speed(veh_id) - self.get_previous_speed(veh_id)) / self.sim_step + + def update_accel_without_noise(self, veh_id, accel_without_noise): + self.__vehicles[veh_id]["accel_without_noise"] = accel_without_noise + + def get_accel_without_noise(self, veh_id): + return self.__vehicles[veh_id]["accel_without_noise"] + + def get_road_grade(self, veh_id): + # TODO + return 0 From 5878eae7cc27e766085362b478beb2abe1f51933 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Tue, 19 May 2020 15:56:53 -0700 Subject: [PATCH 14/89] get up to date with i210_dev --- .../exp_configs/non_rl/i210_subnetwork.py | 2 +- .../exp_configs/templates/sumo/test2.net.xml | 78 +++++-------------- 2 files changed, 22 insertions(+), 58 deletions(-) diff --git a/examples/exp_configs/non_rl/i210_subnetwork.py b/examples/exp_configs/non_rl/i210_subnetwork.py index 194da1099..25565bb49 100644 --- a/examples/exp_configs/non_rl/i210_subnetwork.py +++ b/examples/exp_configs/non_rl/i210_subnetwork.py @@ -117,7 +117,7 @@ sim=SumoParams( sim_step=0.4, render=False, - color_by_speed=False, + color_by_speed=True, use_ballistic=True ), diff --git a/examples/exp_configs/templates/sumo/test2.net.xml b/examples/exp_configs/templates/sumo/test2.net.xml index 16170b917..00e3edcd5 100644 --- a/examples/exp_configs/templates/sumo/test2.net.xml +++ b/examples/exp_configs/templates/sumo/test2.net.xml @@ -1,41 +1,5 @@ - - @@ -4716,24 +4680,24 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + @@ -4837,10 +4801,10 @@ - + - - + + From 34cecff822badd955a79ee8c773640875e6bea2b Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Tue, 19 May 2020 20:45:08 -0700 Subject: [PATCH 15/89] remove dupe imports --- examples/train.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/examples/train.py b/examples/train.py index e34b2935c..7cb84d361 100644 --- a/examples/train.py +++ b/examples/train.py @@ -124,8 +124,6 @@ def run_model_stablebaseline(flow_params, stable_baselines.* the trained model """ - from stable_baselines.common.vec_env import DummyVecEnv, SubprocVecEnv - from stable_baselines import PPO2 if num_cpus == 1: constructor = env_constructor(params=flow_params, version=0)() @@ -175,12 +173,7 @@ def setup_exps_rllib(flow_params, dict training configuration parameters """ - from ray import tune from ray.tune.registry import register_env - try: - from ray.rllib.agents.agent import get_agent_class - except ImportError: - from ray.rllib.agents.registry import get_agent_class horizon = flow_params['env'].horizon @@ -263,8 +256,6 @@ def on_episode_end(info): def train_rllib(submodule, flags): """Train policies using the PPO algorithm in RLlib.""" - import ray - from ray.tune import run_experiments flow_params = submodule.flow_params flow_params['sim'].render = flags.render @@ -413,8 +404,6 @@ def train_h_baselines(flow_params, args, multiagent): def train_stable_baselines(submodule, flags): """Train policies using the PPO algorithm in stable-baselines.""" - from stable_baselines.common.vec_env import DummyVecEnv - from stable_baselines import PPO2 flow_params = submodule.flow_params # Path to the saved files From fc9983631ec172b624ae6dfef65eeed1eb8dce4c Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Tue, 19 May 2020 20:51:14 -0700 Subject: [PATCH 16/89] remove blank lines after docstrings --- examples/train.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/train.py b/examples/train.py index 7cb84d361..5a9ab5903 100644 --- a/examples/train.py +++ b/examples/train.py @@ -124,7 +124,6 @@ def run_model_stablebaseline(flow_params, stable_baselines.* the trained model """ - if num_cpus == 1: constructor = env_constructor(params=flow_params, version=0)() # The algorithms require a vectorized environment to run @@ -256,7 +255,6 @@ def on_episode_end(info): def train_rllib(submodule, flags): """Train policies using the PPO algorithm in RLlib.""" - flow_params = submodule.flow_params flow_params['sim'].render = flags.render policy_graphs = getattr(submodule, "POLICY_GRAPHS", None) @@ -404,7 +402,6 @@ def train_h_baselines(flow_params, args, multiagent): def train_stable_baselines(submodule, flags): """Train policies using the PPO algorithm in stable-baselines.""" - flow_params = submodule.flow_params # Path to the saved files exp_tag = flow_params['exp_tag'] From 6c11a70281ba4673edad160ffdcf68fc4372c13a Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Tue, 19 May 2020 20:59:00 -0700 Subject: [PATCH 17/89] add back ray import --- examples/train.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/train.py b/examples/train.py index 5a9ab5903..50720b756 100644 --- a/examples/train.py +++ b/examples/train.py @@ -255,6 +255,8 @@ def on_episode_end(info): def train_rllib(submodule, flags): """Train policies using the PPO algorithm in RLlib.""" + import ray + flow_params = submodule.flow_params flow_params['sim'].render = flags.render policy_graphs = getattr(submodule, "POLICY_GRAPHS", None) From d6ffaa6bb0783fe0aaf0feb09a7b2b1f9591d0b5 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Tue, 19 May 2020 21:04:56 -0700 Subject: [PATCH 18/89] remove whitespace --- examples/train.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/train.py b/examples/train.py index 50720b756..1689d846f 100644 --- a/examples/train.py +++ b/examples/train.py @@ -256,7 +256,7 @@ def on_episode_end(info): def train_rllib(submodule, flags): """Train policies using the PPO algorithm in RLlib.""" import ray - + flow_params = submodule.flow_params flow_params['sim'].render = flags.render policy_graphs = getattr(submodule, "POLICY_GRAPHS", None) From 151e3b2195de3d6f9079593d0acf684489633e81 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Mon, 25 May 2020 16:57:52 -0700 Subject: [PATCH 19/89] fix rebase errors --- examples/data_pipeline.py | 179 ---------------------------- examples/query.py | 8 -- examples/run_query.py | 34 ------ flow/controllers/base_controller.py | 1 + flow/core/kernel/vehicle/traci.py | 8 -- flow/data_pipeline/data_pipeline.py | 10 -- 6 files changed, 1 insertion(+), 239 deletions(-) delete mode 100644 examples/data_pipeline.py delete mode 100644 examples/query.py delete mode 100644 examples/run_query.py diff --git a/examples/data_pipeline.py b/examples/data_pipeline.py deleted file mode 100644 index 5fdc30cf2..000000000 --- a/examples/data_pipeline.py +++ /dev/null @@ -1,179 +0,0 @@ -import pandas as pd -import boto3 -from botocore.exceptions import ClientError -from examples.query import QueryStrings -from time import time - - -def generate_trajectory_table(data_path, extra_info, partition_name): - """ generate desired output for the trajectory_table based on standard SUMO emission - - Parameters - ---------- - data_path : str - path to the standard SUMO emission - extra_info: dict - extra information needed in the trajectory table, collected from flow - partition_name: str - the name of the partition to put this output to - Returns - ------- - output_file_path: str - the local path of the outputted csv file - """ - raw_output = pd.read_csv(data_path, index_col=["time", "id"]) - required_cols = {"time", "id", "speed", "x", "y"} - raw_output = raw_output.drop(set(raw_output.columns) - required_cols, axis=1) - - extra_info = pd.DataFrame.from_dict(extra_info) - extra_info.set_index(["time", "id"]) - raw_output = raw_output.merge(extra_info, how="left", left_on=["time", "id"], right_on=["time", "id"]) - - # add the partition column - raw_output['partition'] = partition_name - - output_file_path = data_path[:-4]+"_trajectory.csv" - raw_output.to_csv(output_file_path, index=False) - return output_file_path - - -def upload_to_s3(bucket_name, bucket_key, file_path): - """ upload a file to S3 bucket - - Parameters - ---------- - bucket_name : str - the bucket to upload to - bucket_key: str - the key within the bucket for the file - file_path: str - the path of the file to be uploaded - """ - s3 = boto3.resource("s3") - s3.Bucket(bucket_name).upload_file(file_path, bucket_key) - return - - -class AthenaQuery: - - def __init__(self): - self.MAX_WAIT = 60 - self.client = boto3.client("athena") - self.existing_partitions = self.get_existing_partitions() - - def get_existing_partitions(self): - """prints the existing partitions in the S3 bucket""" - - response = self.client.start_query_execution( - QueryString='SHOW PARTITIONS trajectory_table', - QueryExecutionContext={ - 'Database': 'simulation' - }, - WorkGroup='primary' - ) - if self.wait_for_execution(response['QueryExecutionId']): - raise RuntimeError("get current partitions timed out") - response = self.client.get_query_results( - QueryExecutionId=response['QueryExecutionId'], - MaxResults=1000 - ) - return [data['Data'][0]['VarCharValue'].split('=')[-1] for data in response['ResultSet']['Rows']] - - def check_status(self, execution_id): - """ Return the status of the execution with given id - - Parameters - ---------- - execution_id : string - id of the execution that is checked for - Returns - ------- - status: str - QUEUED|RUNNING|SUCCEEDED|FAILED|CANCELLED - """ - - response = self.client.get_query_execution( - QueryExecutionId=execution_id - ) - return response['QueryExecution']['Status']['State'] - - def wait_for_execution(self, execution_id): - """ wait for the execution to finish or time-out - - Parameters - ---------- - execution_id : str - id of the execution this is watiing for - Returns - ------- - time_out: bool - True if time-out, False if success - Raises - ------ - RuntimeError: if execution failed or get canceled - """ - start = time() - while time() - start < self.MAX_WAIT: - state = self.check_status(execution_id) - if state == 'FAILED' or state == 'CANCELLED': - raise RuntimeError("update partition failed") - elif state == 'SUCCEEDED': - return False - return True - - def update_partition(self, partition): - """ load the given partition to the trajectory_table on Athena - - Parameters - ---------- - partition : str - the new partition that needs to be loaded - """ - response = self.client.start_query_execution( - QueryString=QueryStrings['UPDATE_PARTITION'].value.format(partition=partition), - QueryExecutionContext={ - 'Database': 'simulation' - }, - WorkGroup='primary' - ) - if self.wait_for_execution(response['QueryExecutionId']): - raise RuntimeError("update partition timed out") - self.existing_partitions.append(partition) - return - - def run_query(self, query_name, result_location="s3://brent.experiments/query-result/", partition="default"): - """ start the execution of a query, does not wait for it to finish - - Parameters - ---------- - query_name : str - name of the query in QueryStrings enum that will be run - result_location: str, optional - location on the S3 bucket where the result will be stored - partition: str, optional - name of the partition to run this query on - Returns - ------- - execution_id: str - the execution id of the execution started by this method - Raises - ------ - ValueError: if tries to run a query not existed in QueryStrings enum - """ - if query_name not in QueryStrings.__members__: - raise ValueError("query not existed: please add it to query.py") - - if partition not in self.existing_partitions: - self.update_partition(partition) - - response = self.client.start_query_execution( - QueryString=QueryStrings[query_name].value.format(partition=partition), - QueryExecutionContext={ - 'Database': 'simulation' - }, - ResultConfiguration={ - 'OutputLocation': result_location, - }, - WorkGroup='primary' - ) - return response['QueryExecutionId'] \ No newline at end of file diff --git a/examples/query.py b/examples/query.py deleted file mode 100644 index 3fbbe69e1..000000000 --- a/examples/query.py +++ /dev/null @@ -1,8 +0,0 @@ -from enum import Enum - -tags = {} - - -class QueryStrings(Enum): - SAMPLE = "SELECT * FROM trajectory_table WHERE partition_name=\'{partition}\' LIMIT 15;" - UPDATE_PARTITION = "ALTER TABLE trajectory_table ADD IF NOT EXISTS PARTITION (partition_name=\'{partition}\');" \ No newline at end of file diff --git a/examples/run_query.py b/examples/run_query.py deleted file mode 100644 index 7b4a5af7d..000000000 --- a/examples/run_query.py +++ /dev/null @@ -1,34 +0,0 @@ -import argparse -import sys -from examples.data_pipeline import AthenaQuery -from examples.query import QueryStrings - -parser = argparse.ArgumentParser(prog="run_query", description="runs query on AWS Athena and stores the result to" - "a S3 location") -parser.add_argument("--run", type=str, nargs="+") -parser.add_argument("--result_location", type=str, nargs='?', default="s3://brent.experiments/query-result/") -parser.add_argument("--partition", type=str, nargs='?', default="default") -parser.add_argument("--list_partitions", action="store_true") -parser.add_argument("--check_status", type=str, nargs='+') -parser.add_argument("--list_queries", action="store_true") - - -if __name__ == "__main__": - args = parser.parse_args() - queryEngine = AthenaQuery() - - if args.run: - execution_ids = [] - for query_name in args.run: - execution_ids.append(queryEngine.run_query(query_name, args.result_location, args.partition)) - print(execution_ids) - if args.list_partitions: - print(queryEngine.existing_partitions) - if args.check_status: - status = dict() - for execution_id in args.check_status: - status[execution_id] = queryEngine.check_status(execution_id) - print(status) - if args.list_queries: - for q in QueryStrings: - print(q) diff --git a/flow/controllers/base_controller.py b/flow/controllers/base_controller.py index 1169ce5b8..ac29bca2e 100755 --- a/flow/controllers/base_controller.py +++ b/flow/controllers/base_controller.py @@ -92,6 +92,7 @@ def get_action(self, env): env.k.vehicle.update_accel_no_noise_no_failsafe(self.veh_id, None) env.k.vehicle.update_accel_no_noise_with_failsafe(self.veh_id, None) env.k.vehicle.update_accel_with_noise_no_failsafe(self.veh_id, None) + env.k.vehicle.update_accel_with_noise_with_failsafe(self.veh_id, None) # this is to avoid abrupt decelerations when a vehicle has just entered # a network and it's data is still not subscribed diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index 5f3821a01..c1e614fe5 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -224,10 +224,6 @@ def update(self, reset): self.num_not_departed += sim_obs[tc.VAR_LOADED_VEHICLES_NUMBER] - \ sim_obs[tc.VAR_DEPARTED_VEHICLES_NUMBER] - # update the number of not departed vehicles - self.num_not_departed += sim_obs[tc.VAR_LOADED_VEHICLES_NUMBER] - \ - sim_obs[tc.VAR_DEPARTED_VEHICLES_NUMBER] - # update the "headway", "leader", and "follower" variables for veh_id in self.__ids: try: @@ -551,10 +547,6 @@ def get_fuel_consumption(self, veh_id, error=-1001): return [self.get_fuel_consumption(vehID, error) for vehID in veh_id] return self.__sumo_obs.get(veh_id, {}).get(tc.VAR_FUELCONSUMPTION, error) * ml_to_gallons - def get_num_not_departed(self): - """See parent class.""" - return self.num_not_departed - def get_previous_speed(self, veh_id, error=-1001): """See parent class.""" if isinstance(veh_id, (list, np.ndarray)): diff --git a/flow/data_pipeline/data_pipeline.py b/flow/data_pipeline/data_pipeline.py index 11d85cb0d..aea9b349c 100644 --- a/flow/data_pipeline/data_pipeline.py +++ b/flow/data_pipeline/data_pipeline.py @@ -88,16 +88,6 @@ def upload_to_s3(bucket_name, bucket_key, file_path, only_query): return -def extra_init(): - """Return the dictionary with all the field pre-populated with empty list.""" - extra_info = {"time_step": [], "id": [], "x": [], "y": [], "speed": [], "headway": [], - "target_accel_with_noise_with_failsafe": [], "target_accel_no_noise_no_failsafe": [], - "target_accel_with_noise_no_failsafe": [], "target_accel_no_noise_with_failsafe": [], - "realized_accel": [], "leader_id": [], "follower_id": [], - "leader_rel_speed": [], "road_grade": [], "source_id": []} - return extra_info - - def get_extra_info(veh_kernel, extra_info, veh_ids): """Get all the necessary information for the trajectory output from flow.""" for vid in veh_ids: From 9d2026e6a3635f756417f632abd30bb8891310a9 Mon Sep 17 00:00:00 2001 From: Yashar Zeinali Farid <34227133+Yasharzf@users.noreply.github.com> Date: Thu, 7 May 2020 23:51:53 -0700 Subject: [PATCH 20/89] get not departed vehicles (#922) * added function to kernel/vehicle to get number of not departed vehiles * fixed over indentation of the docstring * indentation edit * pep8 Co-authored-by: AboudyKreidieh --- flow/core/kernel/vehicle/traci.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index b89e981be..58eddfd1c 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -220,6 +220,10 @@ def update(self, reset): self.num_not_departed += sim_obs[tc.VAR_LOADED_VEHICLES_NUMBER] - \ sim_obs[tc.VAR_DEPARTED_VEHICLES_NUMBER] + # update the number of not departed vehicles + self.num_not_departed += sim_obs[tc.VAR_LOADED_VEHICLES_NUMBER] - \ + sim_obs[tc.VAR_DEPARTED_VEHICLES_NUMBER] + # update the "headway", "leader", and "follower" variables for veh_id in self.__ids: try: @@ -543,6 +547,10 @@ def get_fuel_consumption(self, veh_id, error=-1001): return [self.get_fuel_consumption(vehID, error) for vehID in veh_id] return self.__sumo_obs.get(veh_id, {}).get(tc.VAR_FUELCONSUMPTION, error) * ml_to_gallons + def get_num_not_departed(self): + """See parent class.""" + return self.num_not_departed + def get_previous_speed(self, veh_id, error=-1001): """See parent class.""" if isinstance(veh_id, (list, np.ndarray)): From 43eeee0193d92e10ef76c9436dac903a52060157 Mon Sep 17 00:00:00 2001 From: Eugene Vinitsky Date: Wed, 18 Mar 2020 16:43:22 -0700 Subject: [PATCH 21/89] Add an on ramp option --- examples/exp_configs/non_rl/i210_subnetwork.py | 2 +- flow/envs/base.py | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/examples/exp_configs/non_rl/i210_subnetwork.py b/examples/exp_configs/non_rl/i210_subnetwork.py index 25565bb49..194da1099 100644 --- a/examples/exp_configs/non_rl/i210_subnetwork.py +++ b/examples/exp_configs/non_rl/i210_subnetwork.py @@ -117,7 +117,7 @@ sim=SumoParams( sim_step=0.4, render=False, - color_by_speed=True, + color_by_speed=False, use_ballistic=True ), diff --git a/flow/envs/base.py b/flow/envs/base.py index baf8270b5..f2067d947 100644 --- a/flow/envs/base.py +++ b/flow/envs/base.py @@ -405,8 +405,18 @@ def step(self, rl_actions): # test if the environment should terminate due to a collision or the # time horizon being met done = (self.time_counter >= self.env_params.sims_per_step * - (self.env_params.warmup_steps + self.env_params.horizon) - or crash) + (self.env_params.warmup_steps + self.env_params.horizon)) + if crash: + print( + "**********************************************************\n" + "**********************************************************\n" + "**********************************************************\n" + "WARNING: There was a crash. \n" + "**********************************************************\n" + "**********************************************************\n" + "**********************************************************" + ) + # compute the info for each agent infos = {} From 8eed7e16ef8793914761a48cc6c0af30756b89d0 Mon Sep 17 00:00:00 2001 From: Eugene Vinitsky Date: Thu, 19 Mar 2020 12:10:07 -0700 Subject: [PATCH 22/89] Upgrade the network to not have keepclear value on the junctions --- .../exp_configs/templates/sumo/test2.net.xml | 78 ++++++++++++++----- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/examples/exp_configs/templates/sumo/test2.net.xml b/examples/exp_configs/templates/sumo/test2.net.xml index 00e3edcd5..16170b917 100644 --- a/examples/exp_configs/templates/sumo/test2.net.xml +++ b/examples/exp_configs/templates/sumo/test2.net.xml @@ -1,5 +1,41 @@ + + @@ -4680,24 +4716,24 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + @@ -4801,10 +4837,10 @@ - + - - + + From c373e94388e8fa4399a95e377c1ba95bbdb282c3 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Mon, 6 Apr 2020 15:28:57 -0700 Subject: [PATCH 23/89] datapip pipeline implemented --- examples/data_pipeline.py | 179 ++++++++++++++++++++++++++++ examples/query.py | 8 ++ examples/run_query.py | 34 ++++++ examples/simulate.py | 10 +- flow/controllers/base_controller.py | 12 ++ flow/core/experiment.py | 27 ++++- flow/core/kernel/vehicle/base.py | 16 +++ flow/core/kernel/vehicle/traci.py | 15 +++ 8 files changed, 299 insertions(+), 2 deletions(-) create mode 100644 examples/data_pipeline.py create mode 100644 examples/query.py create mode 100644 examples/run_query.py diff --git a/examples/data_pipeline.py b/examples/data_pipeline.py new file mode 100644 index 000000000..5fdc30cf2 --- /dev/null +++ b/examples/data_pipeline.py @@ -0,0 +1,179 @@ +import pandas as pd +import boto3 +from botocore.exceptions import ClientError +from examples.query import QueryStrings +from time import time + + +def generate_trajectory_table(data_path, extra_info, partition_name): + """ generate desired output for the trajectory_table based on standard SUMO emission + + Parameters + ---------- + data_path : str + path to the standard SUMO emission + extra_info: dict + extra information needed in the trajectory table, collected from flow + partition_name: str + the name of the partition to put this output to + Returns + ------- + output_file_path: str + the local path of the outputted csv file + """ + raw_output = pd.read_csv(data_path, index_col=["time", "id"]) + required_cols = {"time", "id", "speed", "x", "y"} + raw_output = raw_output.drop(set(raw_output.columns) - required_cols, axis=1) + + extra_info = pd.DataFrame.from_dict(extra_info) + extra_info.set_index(["time", "id"]) + raw_output = raw_output.merge(extra_info, how="left", left_on=["time", "id"], right_on=["time", "id"]) + + # add the partition column + raw_output['partition'] = partition_name + + output_file_path = data_path[:-4]+"_trajectory.csv" + raw_output.to_csv(output_file_path, index=False) + return output_file_path + + +def upload_to_s3(bucket_name, bucket_key, file_path): + """ upload a file to S3 bucket + + Parameters + ---------- + bucket_name : str + the bucket to upload to + bucket_key: str + the key within the bucket for the file + file_path: str + the path of the file to be uploaded + """ + s3 = boto3.resource("s3") + s3.Bucket(bucket_name).upload_file(file_path, bucket_key) + return + + +class AthenaQuery: + + def __init__(self): + self.MAX_WAIT = 60 + self.client = boto3.client("athena") + self.existing_partitions = self.get_existing_partitions() + + def get_existing_partitions(self): + """prints the existing partitions in the S3 bucket""" + + response = self.client.start_query_execution( + QueryString='SHOW PARTITIONS trajectory_table', + QueryExecutionContext={ + 'Database': 'simulation' + }, + WorkGroup='primary' + ) + if self.wait_for_execution(response['QueryExecutionId']): + raise RuntimeError("get current partitions timed out") + response = self.client.get_query_results( + QueryExecutionId=response['QueryExecutionId'], + MaxResults=1000 + ) + return [data['Data'][0]['VarCharValue'].split('=')[-1] for data in response['ResultSet']['Rows']] + + def check_status(self, execution_id): + """ Return the status of the execution with given id + + Parameters + ---------- + execution_id : string + id of the execution that is checked for + Returns + ------- + status: str + QUEUED|RUNNING|SUCCEEDED|FAILED|CANCELLED + """ + + response = self.client.get_query_execution( + QueryExecutionId=execution_id + ) + return response['QueryExecution']['Status']['State'] + + def wait_for_execution(self, execution_id): + """ wait for the execution to finish or time-out + + Parameters + ---------- + execution_id : str + id of the execution this is watiing for + Returns + ------- + time_out: bool + True if time-out, False if success + Raises + ------ + RuntimeError: if execution failed or get canceled + """ + start = time() + while time() - start < self.MAX_WAIT: + state = self.check_status(execution_id) + if state == 'FAILED' or state == 'CANCELLED': + raise RuntimeError("update partition failed") + elif state == 'SUCCEEDED': + return False + return True + + def update_partition(self, partition): + """ load the given partition to the trajectory_table on Athena + + Parameters + ---------- + partition : str + the new partition that needs to be loaded + """ + response = self.client.start_query_execution( + QueryString=QueryStrings['UPDATE_PARTITION'].value.format(partition=partition), + QueryExecutionContext={ + 'Database': 'simulation' + }, + WorkGroup='primary' + ) + if self.wait_for_execution(response['QueryExecutionId']): + raise RuntimeError("update partition timed out") + self.existing_partitions.append(partition) + return + + def run_query(self, query_name, result_location="s3://brent.experiments/query-result/", partition="default"): + """ start the execution of a query, does not wait for it to finish + + Parameters + ---------- + query_name : str + name of the query in QueryStrings enum that will be run + result_location: str, optional + location on the S3 bucket where the result will be stored + partition: str, optional + name of the partition to run this query on + Returns + ------- + execution_id: str + the execution id of the execution started by this method + Raises + ------ + ValueError: if tries to run a query not existed in QueryStrings enum + """ + if query_name not in QueryStrings.__members__: + raise ValueError("query not existed: please add it to query.py") + + if partition not in self.existing_partitions: + self.update_partition(partition) + + response = self.client.start_query_execution( + QueryString=QueryStrings[query_name].value.format(partition=partition), + QueryExecutionContext={ + 'Database': 'simulation' + }, + ResultConfiguration={ + 'OutputLocation': result_location, + }, + WorkGroup='primary' + ) + return response['QueryExecutionId'] \ No newline at end of file diff --git a/examples/query.py b/examples/query.py new file mode 100644 index 000000000..3fbbe69e1 --- /dev/null +++ b/examples/query.py @@ -0,0 +1,8 @@ +from enum import Enum + +tags = {} + + +class QueryStrings(Enum): + SAMPLE = "SELECT * FROM trajectory_table WHERE partition_name=\'{partition}\' LIMIT 15;" + UPDATE_PARTITION = "ALTER TABLE trajectory_table ADD IF NOT EXISTS PARTITION (partition_name=\'{partition}\');" \ No newline at end of file diff --git a/examples/run_query.py b/examples/run_query.py new file mode 100644 index 000000000..7b4a5af7d --- /dev/null +++ b/examples/run_query.py @@ -0,0 +1,34 @@ +import argparse +import sys +from examples.data_pipeline import AthenaQuery +from examples.query import QueryStrings + +parser = argparse.ArgumentParser(prog="run_query", description="runs query on AWS Athena and stores the result to" + "a S3 location") +parser.add_argument("--run", type=str, nargs="+") +parser.add_argument("--result_location", type=str, nargs='?', default="s3://brent.experiments/query-result/") +parser.add_argument("--partition", type=str, nargs='?', default="default") +parser.add_argument("--list_partitions", action="store_true") +parser.add_argument("--check_status", type=str, nargs='+') +parser.add_argument("--list_queries", action="store_true") + + +if __name__ == "__main__": + args = parser.parse_args() + queryEngine = AthenaQuery() + + if args.run: + execution_ids = [] + for query_name in args.run: + execution_ids.append(queryEngine.run_query(query_name, args.result_location, args.partition)) + print(execution_ids) + if args.list_partitions: + print(queryEngine.existing_partitions) + if args.check_status: + status = dict() + for execution_id in args.check_status: + status[execution_id] = queryEngine.check_status(execution_id) + print(status) + if args.list_queries: + for q in QueryStrings: + print(q) diff --git a/examples/simulate.py b/examples/simulate.py index 848f030a4..f54bb38d9 100644 --- a/examples/simulate.py +++ b/examples/simulate.py @@ -48,6 +48,12 @@ def parse_args(args): action='store_true', help='Specifies whether to generate an emission file from the ' 'simulation.') + parser.add_argument( + '--to_aws', + type=str, nargs='?', default=None, const="default", + help='Specifies the name of the partition to store the output' + 'file on S3. Putting not None value for this argument' + 'automatically set gen_emission to True.') return parser.parse_known_args(args)[0] @@ -55,6 +61,8 @@ def parse_args(args): if __name__ == "__main__": flags = parse_args(sys.argv[1:]) + flags.gen_emission = flags.gen_emission or flags.to_aws + # Get the flow_params object. module = __import__("exp_configs.non_rl", fromlist=[flags.exp_config]) flow_params = getattr(module, flags.exp_config).flow_params @@ -83,4 +91,4 @@ def parse_args(args): exp = Experiment(flow_params, callables) # Run for the specified number of rollouts. - exp.run(flags.num_runs, convert_to_csv=flags.gen_emission) + exp.run(flags.num_runs, convert_to_csv=flags.gen_emission, partition_name=flags.to_aws) diff --git a/flow/controllers/base_controller.py b/flow/controllers/base_controller.py index 4004b1c4d..6e6734764 100755 --- a/flow/controllers/base_controller.py +++ b/flow/controllers/base_controller.py @@ -88,6 +88,9 @@ def get_action(self, env): float the modified form of the acceleration """ + # clear the current stored accel_without_noise of this vehicle None + env.k.vehicle.update_accel_without_noise(self.veh_id, None) + # this is to avoid abrupt decelerations when a vehicle has just entered # a network and it's data is still not subscribed if len(env.k.vehicle.get_edge(self.veh_id)) == 0: @@ -105,6 +108,15 @@ def get_action(self, env): if accel is None: return None + # store the acceleration without noise to each vehicle + # run fail safe if requested + accel_without_noice = accel + if self.fail_safe == 'instantaneous': + accel_without_noice = self.get_safe_action_instantaneous(env, accel_without_noice) + elif self.fail_safe == 'safe_velocity': + accel_without_noice = self.get_safe_velocity_action(env, accel_without_noice) + env.k.vehicle.update_accel_without_noise(self.veh_id, accel_without_noice) + # add noise to the accelerations, if requested if self.accel_noise > 0: accel += np.sqrt(env.sim_step) * np.random.normal(0, self.accel_noise) diff --git a/flow/core/experiment.py b/flow/core/experiment.py index a0497b595..1f0cce355 100755 --- a/flow/core/experiment.py +++ b/flow/core/experiment.py @@ -1,6 +1,7 @@ """Contains an experiment class for running simulations.""" from flow.core.util import emission_to_csv from flow.utils.registry import make_create_env +from examples.data_pipeline import generate_trajectory_table, upload_to_s3 import datetime import logging import time @@ -85,7 +86,7 @@ def __init__(self, flow_params, custom_callables=None): logging.info("Initializing environment.") - def run(self, num_runs, rl_actions=None, convert_to_csv=False): + def run(self, num_runs, rl_actions=None, convert_to_csv=False, partition_name=None): """Run the given network for a set number of runs. Parameters @@ -98,6 +99,10 @@ def run(self, num_runs, rl_actions=None, convert_to_csv=False): convert_to_csv : bool Specifies whether to convert the emission file created by sumo into a csv file + partition_name: str + Specifies the S3 partition you want to store the output file, + will be used to later for query. If NONE, won't upload output + to S3. Returns ------- @@ -136,6 +141,8 @@ def rl_actions(*_): # time profiling information t = time.time() times = [] + extra_info = {"time": [], "id": [], "headway": [], "acceleration": [], "leader_id": [], "follower_id": [], + "leader_rel_speed": [], "accel_without_noise": [], "road_grade": []} for i in range(num_runs): ret = 0 @@ -153,6 +160,18 @@ def rl_actions(*_): vel.append(np.mean(self.env.k.vehicle.get_speed(veh_ids))) ret += reward + # collect additional information for the data pipeline + for vid in veh_ids: + extra_info["time"].append(self.env.k.vehicle.get_timestep(veh_ids[0]) / 1000) + extra_info["id"].append(vid) + extra_info["headway"].append(self.env.k.vehicle.get_headway(vid)) + extra_info["acceleration"].append(self.env.k.vehicle.get_accel(vid)) + extra_info["leader_id"].append(self.env.k.vehicle.get_leader(vid)) + extra_info["follower_id"].append(self.env.k.vehicle.get_follower(vid)) + extra_info["leader_rel_speed"].append(self.env.k.vehicle.get_speed(self.env.k.vehicle.get_leader(vid)) - self.env.k.vehicle.get_speed(vid)) + extra_info["accel_without_noise"].append(self.env.k.vehicle.get_accel_without_noise(vid)) + extra_info["road_grade"].append(self.env.k.vehicle.get_road_grade(vid)) + # Compute the results for the custom callables. for (key, lambda_func) in self.custom_callables.items(): custom_vals[key].append(lambda_func(self.env)) @@ -195,4 +214,10 @@ def rl_actions(*_): # Delete the .xml version of the emission file. os.remove(emission_path) + output_file = generate_trajectory_table(emission_path[:-4] + ".csv", extra_info, partition_name) + + if partition_name: + upload_to_s3('brent.experiments', 'trajectory-output/' + 'partition_name=' + partition_name + '/' + + output_file.split('/')[-1], output_file) + return info_dict diff --git a/flow/core/kernel/vehicle/base.py b/flow/core/kernel/vehicle/base.py index 706504027..0c992503c 100644 --- a/flow/core/kernel/vehicle/base.py +++ b/flow/core/kernel/vehicle/base.py @@ -684,3 +684,19 @@ def get_max_speed(self, veh_id, error): float """ raise NotImplementedError + + ########################################################################### + # Methods for Datapipeline # + ########################################################################### + + def get_accel(self, veh_id): + """ see traci class """ + raise NotImplementedError + + def update_accel_without_noise(self, veh_id, accel_without_noise): + """ see traci class """ + raise NotImplementedError + + def get_accel_without_noise(self, veh_id): + """ see traci class """ + raise NotImplementedError diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index 58eddfd1c..b06ab112b 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -113,6 +113,7 @@ def initialize(self, vehicles): self.__vehicles[veh_id] = dict() self.__vehicles[veh_id]['type'] = typ['veh_id'] self.__vehicles[veh_id]['initial_speed'] = typ['initial_speed'] + self.__vehicles[veh_id]["accel_without_noise"] = None self.num_vehicles += 1 if typ['acceleration_controller'][0] == RLController: self.num_rl_vehicles += 1 @@ -1128,3 +1129,17 @@ def get_max_speed(self, veh_id, error=-1001): def set_max_speed(self, veh_id, max_speed): """See parent class.""" self.kernel_api.vehicle.setMaxSpeed(veh_id, max_speed) + + # add for data pipeline + def get_accel(self, veh_id): + return (self.get_speed(veh_id) - self.get_previous_speed(veh_id)) / self.sim_step + + def update_accel_without_noise(self, veh_id, accel_without_noise): + self.__vehicles[veh_id]["accel_without_noise"] = accel_without_noise + + def get_accel_without_noise(self, veh_id): + return self.__vehicles[veh_id]["accel_without_noise"] + + def get_road_grade(self, veh_id): + # TODO + return 0 From a88c209f5fa6eb057c978c6583ab040cd11a8aa0 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Tue, 19 May 2020 15:56:53 -0700 Subject: [PATCH 24/89] get up to date with i210_dev --- .../exp_configs/non_rl/i210_subnetwork.py | 2 +- .../exp_configs/templates/sumo/test2.net.xml | 78 +++++-------------- 2 files changed, 22 insertions(+), 58 deletions(-) diff --git a/examples/exp_configs/non_rl/i210_subnetwork.py b/examples/exp_configs/non_rl/i210_subnetwork.py index 194da1099..25565bb49 100644 --- a/examples/exp_configs/non_rl/i210_subnetwork.py +++ b/examples/exp_configs/non_rl/i210_subnetwork.py @@ -117,7 +117,7 @@ sim=SumoParams( sim_step=0.4, render=False, - color_by_speed=False, + color_by_speed=True, use_ballistic=True ), diff --git a/examples/exp_configs/templates/sumo/test2.net.xml b/examples/exp_configs/templates/sumo/test2.net.xml index 16170b917..00e3edcd5 100644 --- a/examples/exp_configs/templates/sumo/test2.net.xml +++ b/examples/exp_configs/templates/sumo/test2.net.xml @@ -1,41 +1,5 @@ - - @@ -4716,24 +4680,24 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + @@ -4837,10 +4801,10 @@ - + - - + + From 89f8d1d504a4e4c98bc564967c1490f0718774cd Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Tue, 19 May 2020 20:45:08 -0700 Subject: [PATCH 25/89] remove dupe imports --- examples/train.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/examples/train.py b/examples/train.py index e34b2935c..7cb84d361 100644 --- a/examples/train.py +++ b/examples/train.py @@ -124,8 +124,6 @@ def run_model_stablebaseline(flow_params, stable_baselines.* the trained model """ - from stable_baselines.common.vec_env import DummyVecEnv, SubprocVecEnv - from stable_baselines import PPO2 if num_cpus == 1: constructor = env_constructor(params=flow_params, version=0)() @@ -175,12 +173,7 @@ def setup_exps_rllib(flow_params, dict training configuration parameters """ - from ray import tune from ray.tune.registry import register_env - try: - from ray.rllib.agents.agent import get_agent_class - except ImportError: - from ray.rllib.agents.registry import get_agent_class horizon = flow_params['env'].horizon @@ -263,8 +256,6 @@ def on_episode_end(info): def train_rllib(submodule, flags): """Train policies using the PPO algorithm in RLlib.""" - import ray - from ray.tune import run_experiments flow_params = submodule.flow_params flow_params['sim'].render = flags.render @@ -413,8 +404,6 @@ def train_h_baselines(flow_params, args, multiagent): def train_stable_baselines(submodule, flags): """Train policies using the PPO algorithm in stable-baselines.""" - from stable_baselines.common.vec_env import DummyVecEnv - from stable_baselines import PPO2 flow_params = submodule.flow_params # Path to the saved files From 306a01fe55f3e756931098e306d03872602b88b2 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Tue, 19 May 2020 20:51:14 -0700 Subject: [PATCH 26/89] remove blank lines after docstrings --- examples/train.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/train.py b/examples/train.py index 7cb84d361..5a9ab5903 100644 --- a/examples/train.py +++ b/examples/train.py @@ -124,7 +124,6 @@ def run_model_stablebaseline(flow_params, stable_baselines.* the trained model """ - if num_cpus == 1: constructor = env_constructor(params=flow_params, version=0)() # The algorithms require a vectorized environment to run @@ -256,7 +255,6 @@ def on_episode_end(info): def train_rllib(submodule, flags): """Train policies using the PPO algorithm in RLlib.""" - flow_params = submodule.flow_params flow_params['sim'].render = flags.render policy_graphs = getattr(submodule, "POLICY_GRAPHS", None) @@ -404,7 +402,6 @@ def train_h_baselines(flow_params, args, multiagent): def train_stable_baselines(submodule, flags): """Train policies using the PPO algorithm in stable-baselines.""" - flow_params = submodule.flow_params # Path to the saved files exp_tag = flow_params['exp_tag'] From 0d5fa6bda67aca96014b8be335cde547b47d7f7b Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Tue, 19 May 2020 20:59:00 -0700 Subject: [PATCH 27/89] add back ray import --- examples/train.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/train.py b/examples/train.py index 5a9ab5903..50720b756 100644 --- a/examples/train.py +++ b/examples/train.py @@ -255,6 +255,8 @@ def on_episode_end(info): def train_rllib(submodule, flags): """Train policies using the PPO algorithm in RLlib.""" + import ray + flow_params = submodule.flow_params flow_params['sim'].render = flags.render policy_graphs = getattr(submodule, "POLICY_GRAPHS", None) From 0ade197b74f7ec0a5a4890e419d605ff3933f824 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Tue, 19 May 2020 21:04:56 -0700 Subject: [PATCH 28/89] remove whitespace --- examples/train.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/train.py b/examples/train.py index 50720b756..1689d846f 100644 --- a/examples/train.py +++ b/examples/train.py @@ -256,7 +256,7 @@ def on_episode_end(info): def train_rllib(submodule, flags): """Train policies using the PPO algorithm in RLlib.""" import ray - + flow_params = submodule.flow_params flow_params['sim'].render = flags.render policy_graphs = getattr(submodule, "POLICY_GRAPHS", None) From 1111e9aa34a4ce46058ec282255c43d03b117123 Mon Sep 17 00:00:00 2001 From: chendiw <31671291+chendiw@users.noreply.github.com> Date: Tue, 21 Apr 2020 15:14:31 -0700 Subject: [PATCH 29/89] moved imports under functions in train.py (#903) * deleting unworking params from SumoChangeLaneParams * deleted unworking params, sublane working in highway : * moved imports inside functions * Apply suggestions from code review * bug fixes * bug fix Co-authored-by: Aboudy Kreidieh --- examples/train.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/examples/train.py b/examples/train.py index 1689d846f..d9e7dde07 100644 --- a/examples/train.py +++ b/examples/train.py @@ -124,6 +124,9 @@ def run_model_stablebaseline(flow_params, stable_baselines.* the trained model """ + from stable_baselines.common.vec_env import DummyVecEnv, SubprocVecEnv + from stable_baselines import PPO2 + if num_cpus == 1: constructor = env_constructor(params=flow_params, version=0)() # The algorithms require a vectorized environment to run @@ -172,7 +175,12 @@ def setup_exps_rllib(flow_params, dict training configuration parameters """ + from ray import tune from ray.tune.registry import register_env + try: + from ray.rllib.agents.agent import get_agent_class + except ImportError: + from ray.rllib.agents.registry import get_agent_class horizon = flow_params['env'].horizon @@ -404,6 +412,9 @@ def train_h_baselines(flow_params, args, multiagent): def train_stable_baselines(submodule, flags): """Train policies using the PPO algorithm in stable-baselines.""" + from stable_baselines.common.vec_env import DummyVecEnv + from stable_baselines import PPO2 + flow_params = submodule.flow_params # Path to the saved files exp_tag = flow_params['exp_tag'] From a4c7d67758bd4187f176e1b5f1f63bc12a10af81 Mon Sep 17 00:00:00 2001 From: Yashar Zeinali Farid <34227133+Yasharzf@users.noreply.github.com> Date: Thu, 7 May 2020 23:51:53 -0700 Subject: [PATCH 30/89] get not departed vehicles (#922) * added function to kernel/vehicle to get number of not departed vehiles * fixed over indentation of the docstring * indentation edit * pep8 Co-authored-by: AboudyKreidieh --- flow/core/kernel/vehicle/traci.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index b06ab112b..1c814b7b4 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -225,6 +225,10 @@ def update(self, reset): self.num_not_departed += sim_obs[tc.VAR_LOADED_VEHICLES_NUMBER] - \ sim_obs[tc.VAR_DEPARTED_VEHICLES_NUMBER] + # update the number of not departed vehicles + self.num_not_departed += sim_obs[tc.VAR_LOADED_VEHICLES_NUMBER] - \ + sim_obs[tc.VAR_DEPARTED_VEHICLES_NUMBER] + # update the "headway", "leader", and "follower" variables for veh_id in self.__ids: try: @@ -552,6 +556,10 @@ def get_num_not_departed(self): """See parent class.""" return self.num_not_departed + def get_num_not_departed(self): + """See parent class.""" + return self.num_not_departed + def get_previous_speed(self, veh_id, error=-1001): """See parent class.""" if isinstance(veh_id, (list, np.ndarray)): From 36e8851f7f7ae71a25b2d5ca5a927396b9e1e41a Mon Sep 17 00:00:00 2001 From: Yashar Zeinali Farid <34227133+Yasharzf@users.noreply.github.com> Date: Sat, 9 May 2020 15:31:44 -0700 Subject: [PATCH 31/89] changed _departed_ids, and _arrived_ids in the update function (#926) * changed _departed_ids, and _arrived_ids in the update function * fixed bug in get_departed_ids and get_arrived_ids --- flow/core/kernel/vehicle/traci.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index 1c814b7b4..bdf94579a 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -552,14 +552,6 @@ def get_fuel_consumption(self, veh_id, error=-1001): return [self.get_fuel_consumption(vehID, error) for vehID in veh_id] return self.__sumo_obs.get(veh_id, {}).get(tc.VAR_FUELCONSUMPTION, error) * ml_to_gallons - def get_num_not_departed(self): - """See parent class.""" - return self.num_not_departed - - def get_num_not_departed(self): - """See parent class.""" - return self.num_not_departed - def get_previous_speed(self, veh_id, error=-1001): """See parent class.""" if isinstance(veh_id, (list, np.ndarray)): From ebb29215ad82c0b2a6b89625ea1b899b5587420a Mon Sep 17 00:00:00 2001 From: Eugene Vinitsky Date: Wed, 18 Mar 2020 16:43:22 -0700 Subject: [PATCH 32/89] Add an on ramp option --- .../exp_configs/non_rl/i210_subnetwork.py | 64 ++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/examples/exp_configs/non_rl/i210_subnetwork.py b/examples/exp_configs/non_rl/i210_subnetwork.py index 25565bb49..474d7335e 100644 --- a/examples/exp_configs/non_rl/i210_subnetwork.py +++ b/examples/exp_configs/non_rl/i210_subnetwork.py @@ -2,8 +2,13 @@ import os import numpy as np +<<<<<<< HEAD from flow.controllers import IDMController from flow.controllers import I210Router +======= +from flow.controllers.car_following_models import IDMController +from flow.controllers.routing_controllers import I210Router +>>>>>>> Add an on ramp option from flow.core.params import SumoParams from flow.core.params import EnvParams from flow.core.params import NetParams @@ -15,6 +20,7 @@ from flow.envs import TestEnv from flow.networks.i210_subnetwork import I210SubNetwork, EDGES_DISTRIBUTION +<<<<<<< HEAD # =========================================================================== # # Specify some configurable constants. # # =========================================================================== # @@ -72,6 +78,37 @@ }), routing_controller=(I210Router, {}) if ON_RAMP else None, ) +======= +ON_RAMP = True + +if ON_RAMP: + vehicles = VehicleParams() + vehicles.add( + "human", + num_vehicles=0, + lane_change_params=SumoLaneChangeParams( + lane_change_mode="strategic", + ), + acceleration_controller=(IDMController, { + "a": 0.3, "b": 2.0, "noise": 0.5 + }), + routing_controller=(I210Router, {}) + ) + +else: + # create the base vehicle type that will be used for inflows + vehicles = VehicleParams() + vehicles.add( + "human", + num_vehicles=0, + lane_change_params=SumoLaneChangeParams( + lane_change_mode="strategic", + ), + acceleration_controller=(IDMController, { + "a": 0.3, "b": 2.0, "noise": 0.5 + }), + ) +>>>>>>> Add an on ramp option inflow = InFlows() # main highway @@ -86,6 +123,7 @@ inflow.add( veh_type="human", edge="27414345", +<<<<<<< HEAD vehs_per_hour=500, departLane="random", departSpeed=10) @@ -99,6 +137,21 @@ # =========================================================================== # # Generate the flow_params dict with all relevant simulation information. # # =========================================================================== # +======= + vehs_per_hour=321, + departLane="random", + departSpeed=20) + inflow.add( + veh_type="human", + edge="27414342#0", + vehs_per_hour=421, + departLane="random", + departSpeed=20) + +NET_TEMPLATE = os.path.join( + config.PROJECT_PATH, + "examples/exp_configs/templates/sumo/test2.net.xml") +>>>>>>> Add an on ramp option flow_params = dict( # name of the experiment @@ -117,24 +170,33 @@ sim=SumoParams( sim_step=0.4, render=False, - color_by_speed=True, + color_by_speed=False, use_ballistic=True ), # environment related parameters (see flow.core.params.EnvParams) env=EnvParams( +<<<<<<< HEAD horizon=10000, +======= + horizon=7200, +>>>>>>> Add an on ramp option ), # network-related parameters (see flow.core.params.NetParams and the # network's documentation or ADDITIONAL_NET_PARAMS component) net=NetParams( inflows=inflow, +<<<<<<< HEAD template=net_template, additional_params={ "on_ramp": ON_RAMP, "ghost_edge": WANT_GHOST_CELL, } +======= + template=NET_TEMPLATE, + additional_params={"use_on_ramp": ON_RAMP} +>>>>>>> Add an on ramp option ), # vehicles to be placed in the network at the start of a rollout (see From e4c02bb1f5513e905f2ea0c5e635d3946fe4d38a Mon Sep 17 00:00:00 2001 From: Eugene Vinitsky Date: Thu, 19 Mar 2020 11:32:12 -0700 Subject: [PATCH 33/89] Increased inflows to 10800 to match density in Bennis ring --- .../exp_configs/non_rl/i210_subnetwork.py | 66 +------------------ 1 file changed, 2 insertions(+), 64 deletions(-) diff --git a/examples/exp_configs/non_rl/i210_subnetwork.py b/examples/exp_configs/non_rl/i210_subnetwork.py index 474d7335e..3704a7a1c 100644 --- a/examples/exp_configs/non_rl/i210_subnetwork.py +++ b/examples/exp_configs/non_rl/i210_subnetwork.py @@ -2,13 +2,8 @@ import os import numpy as np -<<<<<<< HEAD from flow.controllers import IDMController from flow.controllers import I210Router -======= -from flow.controllers.car_following_models import IDMController -from flow.controllers.routing_controllers import I210Router ->>>>>>> Add an on ramp option from flow.core.params import SumoParams from flow.core.params import EnvParams from flow.core.params import NetParams @@ -20,7 +15,6 @@ from flow.envs import TestEnv from flow.networks.i210_subnetwork import I210SubNetwork, EDGES_DISTRIBUTION -<<<<<<< HEAD # =========================================================================== # # Specify some configurable constants. # # =========================================================================== # @@ -78,37 +72,6 @@ }), routing_controller=(I210Router, {}) if ON_RAMP else None, ) -======= -ON_RAMP = True - -if ON_RAMP: - vehicles = VehicleParams() - vehicles.add( - "human", - num_vehicles=0, - lane_change_params=SumoLaneChangeParams( - lane_change_mode="strategic", - ), - acceleration_controller=(IDMController, { - "a": 0.3, "b": 2.0, "noise": 0.5 - }), - routing_controller=(I210Router, {}) - ) - -else: - # create the base vehicle type that will be used for inflows - vehicles = VehicleParams() - vehicles.add( - "human", - num_vehicles=0, - lane_change_params=SumoLaneChangeParams( - lane_change_mode="strategic", - ), - acceleration_controller=(IDMController, { - "a": 0.3, "b": 2.0, "noise": 0.5 - }), - ) ->>>>>>> Add an on ramp option inflow = InFlows() # main highway @@ -123,7 +86,6 @@ inflow.add( veh_type="human", edge="27414345", -<<<<<<< HEAD vehs_per_hour=500, departLane="random", departSpeed=10) @@ -137,21 +99,6 @@ # =========================================================================== # # Generate the flow_params dict with all relevant simulation information. # # =========================================================================== # -======= - vehs_per_hour=321, - departLane="random", - departSpeed=20) - inflow.add( - veh_type="human", - edge="27414342#0", - vehs_per_hour=421, - departLane="random", - departSpeed=20) - -NET_TEMPLATE = os.path.join( - config.PROJECT_PATH, - "examples/exp_configs/templates/sumo/test2.net.xml") ->>>>>>> Add an on ramp option flow_params = dict( # name of the experiment @@ -170,33 +117,24 @@ sim=SumoParams( sim_step=0.4, render=False, - color_by_speed=False, + color_by_speed=True, use_ballistic=True ), # environment related parameters (see flow.core.params.EnvParams) env=EnvParams( -<<<<<<< HEAD horizon=10000, -======= - horizon=7200, ->>>>>>> Add an on ramp option ), # network-related parameters (see flow.core.params.NetParams and the # network's documentation or ADDITIONAL_NET_PARAMS component) net=NetParams( inflows=inflow, -<<<<<<< HEAD template=net_template, additional_params={ "on_ramp": ON_RAMP, "ghost_edge": WANT_GHOST_CELL, } -======= - template=NET_TEMPLATE, - additional_params={"use_on_ramp": ON_RAMP} ->>>>>>> Add an on ramp option ), # vehicles to be placed in the network at the start of a rollout (see @@ -225,4 +163,4 @@ "avg_density": lambda env: 5 * 1000 * len(env.k.vehicle.get_ids_by_edge( edge_id)) / (env.k.network.edge_length(edge_id) * env.k.network.num_lanes(edge_id)), -} +} \ No newline at end of file From 505d646beb9814daaa527f417740f8309a9f1c85 Mon Sep 17 00:00:00 2001 From: Eugene Vinitsky Date: Thu, 19 Mar 2020 12:10:07 -0700 Subject: [PATCH 34/89] Upgrade the network to not have keepclear value on the junctions --- .../exp_configs/templates/sumo/test2.net.xml | 78 ++++++++++++++----- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/examples/exp_configs/templates/sumo/test2.net.xml b/examples/exp_configs/templates/sumo/test2.net.xml index 00e3edcd5..16170b917 100644 --- a/examples/exp_configs/templates/sumo/test2.net.xml +++ b/examples/exp_configs/templates/sumo/test2.net.xml @@ -1,5 +1,41 @@ + + @@ -4680,24 +4716,24 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + @@ -4801,10 +4837,10 @@ - + - - + + From 7d52445fdaa2f6ef358bad6cd58f6b26775a4f36 Mon Sep 17 00:00:00 2001 From: Eugene Vinitsky Date: Tue, 24 Mar 2020 22:49:17 -0700 Subject: [PATCH 35/89] Add 1 lane highway network for Benni --- examples/exp_configs/non_rl/highway.py | 40 +++++++++----------------- flow/networks/highway.py | 2 +- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/examples/exp_configs/non_rl/highway.py b/examples/exp_configs/non_rl/highway.py index e7505f2d7..1905e2f7f 100644 --- a/examples/exp_configs/non_rl/highway.py +++ b/examples/exp_configs/non_rl/highway.py @@ -5,25 +5,19 @@ from flow.core.params import VehicleParams, InFlows from flow.envs.ring.lane_change_accel import ADDITIONAL_ENV_PARAMS from flow.networks.highway import HighwayNetwork, ADDITIONAL_NET_PARAMS -from flow.envs import LaneChangeAccelEnv +from flow.envs import TestEnv vehicles = VehicleParams() vehicles.add( - veh_id="human", - acceleration_controller=(IDMController, {}), - lane_change_params=SumoLaneChangeParams( - model="SL2015", - lc_sublane=2.0, - ), - num_vehicles=20) -vehicles.add( - veh_id="human2", - acceleration_controller=(IDMController, {}), - lane_change_params=SumoLaneChangeParams( - model="SL2015", - lc_sublane=2.0, - ), - num_vehicles=20) + "human", + num_vehicles=0, + lane_change_params=SumoLaneChangeParams( + lane_change_mode="strategic", + ), + acceleration_controller=(IDMController, { + "a": 0.3, "b": 2.0, "noise": 0.5 + }), + ) env_params = EnvParams(additional_params=ADDITIONAL_ENV_PARAMS) @@ -31,13 +25,7 @@ inflow.add( veh_type="human", edge="highway_0", - probability=0.25, - departLane="free", - departSpeed=20) -inflow.add( - veh_type="human2", - edge="highway_0", - probability=0.25, + vehs_per_hour=10800 / 5.0, departLane="free", departSpeed=20) @@ -47,7 +35,7 @@ exp_tag='highway', # name of the flow environment the experiment is running on - env_name=LaneChangeAccelEnv, + env_name=TestEnv, # name of the network class the experiment is running on network=HighwayNetwork, @@ -58,12 +46,12 @@ # sumo-related parameters (see flow.core.params.SumoParams) sim=SumoParams( render=True, - lateral_resolution=1.0, + sim_step=0.5 ), # environment related parameters (see flow.core.params.EnvParams) env=EnvParams( - horizon=1500, + horizon=4000, additional_params=ADDITIONAL_ENV_PARAMS.copy(), ), diff --git a/flow/networks/highway.py b/flow/networks/highway.py index 6f10d3279..871e7f415 100644 --- a/flow/networks/highway.py +++ b/flow/networks/highway.py @@ -9,7 +9,7 @@ # length of the highway "length": 1000, # number of lanes - "lanes": 4, + "lanes": 1, # speed limit for all edges "speed_limit": 30, # number of edges to divide the highway into From c3b2a51aa3fcf2c60c0678e7e3c385febf11d867 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Fri, 10 Apr 2020 19:54:30 -0700 Subject: [PATCH 36/89] multiple runs issue solved, testing added --- examples/data_pipeline.py | 55 +++++++- examples/datapipeline_test.py | 33 +++++ examples/query.py | 13 +- examples/run_query.py | 6 +- flow/core/experiment.py | 224 +----------------------------- flow/core/kernel/vehicle/base.py | 4 + flow/core/kernel/vehicle/traci.py | 3 + 7 files changed, 107 insertions(+), 231 deletions(-) create mode 100644 examples/datapipeline_test.py diff --git a/examples/data_pipeline.py b/examples/data_pipeline.py index 5fdc30cf2..9d56548c2 100644 --- a/examples/data_pipeline.py +++ b/examples/data_pipeline.py @@ -1,7 +1,8 @@ import pandas as pd +import numpy as np import boto3 from botocore.exceptions import ClientError -from examples.query import QueryStrings +from examples.query import QueryStrings, testing_functions from time import time @@ -30,13 +31,22 @@ def generate_trajectory_table(data_path, extra_info, partition_name): raw_output = raw_output.merge(extra_info, how="left", left_on=["time", "id"], right_on=["time", "id"]) # add the partition column - raw_output['partition'] = partition_name - + # raw_output['partition'] = partition_name + raw_output = raw_output.sort_values(by=["time", "id"]) output_file_path = data_path[:-4]+"_trajectory.csv" raw_output.to_csv(output_file_path, index=False) return output_file_path +def generate_trajectory_from_flow(data_path, extra_info, partition_name): + extra_info = pd.DataFrame.from_dict(extra_info) + # extra_info["partition"] = partition_name + extra_info.to_csv(data_path, index=False) + upload_only_file_path = data_path[:-4] + "_upload" + ".csv" + extra_info.to_csv(upload_only_file_path, index=False, header=False) + return upload_only_file_path + + def upload_to_s3(bucket_name, bucket_key, file_path): """ upload a file to S3 bucket @@ -176,4 +186,41 @@ def run_query(self, query_name, result_location="s3://brent.experiments/query-re }, WorkGroup='primary' ) - return response['QueryExecutionId'] \ No newline at end of file + return response['QueryExecutionId'] + +########################################################################### +# Helpers for testing the SQL Queries # +########################################################################### + + +def test_sql_query(query_name): + if query_name not in testing_functions: + raise ValueError("no tests supported for this query") + + # Run the respective sql query + queryEngine = AthenaQuery() + execution_id = queryEngine.run_query(query_name, result_location="s3://brent.experiments/query-result/query-test", + partition="test") + if queryEngine.wait_for_execution(execution_id): + raise RuntimeError("execution timed out") + + # get the Athena query result from S3 + s3 = boto3.resource("s3") + s3.Bucket("brent.experiments").download_file("query-result/query-test/"+execution_id+".csv", + "data/athena_result.csv") + athena_result = pd.read_csv("data/athena_result.csv") + athena_result = athena_result.sort_values(by=["time", "id"]) + + # get the python expected result + expected_result = pd.read_csv("data/test_data.csv") + expected_result = expected_result.apply(testing_functions[query_name], axis=1, result_type="expand") + expected_result.columns = ["time", "id", "power"] + expected_result = expected_result.sort_values(by=["time", "id"]) + + difference = athena_result["power"] - expected_result["power"] + print("average difference is: " + str(np.mean(difference))) + print("std of difference is: " + str(np.std(difference))) + print("average ratio of difference to expected is: " + + str(np.mean(np.divide(difference, expected_result["power"])))) + difference = pd.DataFrame(difference) + difference.to_csv("./difference.csv") diff --git a/examples/datapipeline_test.py b/examples/datapipeline_test.py new file mode 100644 index 000000000..564060d3b --- /dev/null +++ b/examples/datapipeline_test.py @@ -0,0 +1,33 @@ +import math + +# Vehicle Mass +M = 1200 +# Gravity +g = 9.81 +# Density of Air +ro_air = 1.225 +# Rolling resistance coefficient +C_r = .005 +# Aerodynamic drag coefficient +C_a = 0.3 +# Vehicle Cross sectional Area +A = 2.6 +# Road grade +theta = 0 + + +def heavyside(inp): + return 0 if inp <= 0 else 1 + + +def calculate_power(mu, acceleration, M=M, g=g, theta=theta, C_r=C_r, ro_air=ro_air, A=A, C_a=C_a): + acceleration = (0.8 + ((1 - 0.8) * heavyside(acceleration)) * acceleration) + accel_and_slope = M * mu * (acceleration + g * math.sin(theta)) + rolling_friction = M * g * C_r * mu + air_drag = .5 * ro_air * A * C_a * mu**3 + power = accel_and_slope + rolling_friction + air_drag + return power + + +def apply_energy_one(row): + return [row[0], row[1], calculate_power(row[4], row[6])] \ No newline at end of file diff --git a/examples/query.py b/examples/query.py index 3fbbe69e1..6354cec3b 100644 --- a/examples/query.py +++ b/examples/query.py @@ -1,8 +1,17 @@ from enum import Enum +from examples.datapipeline_test import apply_energy_one -tags = {} +tags = {"energy": ["ENERGY_ONE"]} + +testing_functions = {"ENERGY_ONE": apply_energy_one} class QueryStrings(Enum): SAMPLE = "SELECT * FROM trajectory_table WHERE partition_name=\'{partition}\' LIMIT 15;" - UPDATE_PARTITION = "ALTER TABLE trajectory_table ADD IF NOT EXISTS PARTITION (partition_name=\'{partition}\');" \ No newline at end of file + UPDATE_PARTITION = "ALTER TABLE trajectory_table ADD IF NOT EXISTS PARTITION (partition_name=\'{partition}\');" + ENERGY_ONE = "SELECT id, time, 1200 * speed * " \ + "((CASE WHEN acceleration > 0 THEN 1 ELSE 0 END * (1-0.8) * acceleration) + 0.8 " \ + "+ 9.81 * SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 " \ + "* POW(speed,3) AS power, 1 AS energy_model_id, source_id " \ + "FROM trajectory_table " \ + "WHERE partition_name=\'{partition}\'" \ No newline at end of file diff --git a/examples/run_query.py b/examples/run_query.py index 7b4a5af7d..ea8839b09 100644 --- a/examples/run_query.py +++ b/examples/run_query.py @@ -1,6 +1,5 @@ import argparse -import sys -from examples.data_pipeline import AthenaQuery +from examples.data_pipeline import AthenaQuery, test_sql_query from examples.query import QueryStrings parser = argparse.ArgumentParser(prog="run_query", description="runs query on AWS Athena and stores the result to" @@ -11,6 +10,7 @@ parser.add_argument("--list_partitions", action="store_true") parser.add_argument("--check_status", type=str, nargs='+') parser.add_argument("--list_queries", action="store_true") +parser.add_argument("--test_query", nargs=1) if __name__ == "__main__": @@ -32,3 +32,5 @@ if args.list_queries: for q in QueryStrings: print(q) + if args.test_query: + test_sql_query(args.test_query[0]) \ No newline at end of file diff --git a/flow/core/experiment.py b/flow/core/experiment.py index 1f0cce355..63c92e798 100755 --- a/flow/core/experiment.py +++ b/flow/core/experiment.py @@ -1,223 +1 @@ -"""Contains an experiment class for running simulations.""" -from flow.core.util import emission_to_csv -from flow.utils.registry import make_create_env -from examples.data_pipeline import generate_trajectory_table, upload_to_s3 -import datetime -import logging -import time -import os -import numpy as np - - -class Experiment: - """ - Class for systematically running simulations in any supported simulator. - - This class acts as a runner for a network and environment. In order to use - it to run an network and environment in the absence of a method specifying - the actions of RL agents in the network, type the following: - - >>> from flow.envs import Env - >>> flow_params = dict(...) # see the examples in exp_config - >>> exp = Experiment(flow_params) # for some experiment configuration - >>> exp.run(num_runs=1) - - If you wish to specify the actions of RL agents in the network, this may be - done as follows: - - >>> rl_actions = lambda state: 0 # replace with something appropriate - >>> exp.run(num_runs=1, rl_actions=rl_actions) - - Finally, if you would like to like to plot and visualize your results, this - class can generate csv files from emission files produced by sumo. These - files will contain the speeds, positions, edges, etc... of every vehicle - in the network at every time step. - - In order to ensure that the simulator constructs an emission file, set the - ``emission_path`` attribute in ``SimParams`` to some path. - - >>> from flow.core.params import SimParams - >>> flow_params['sim'] = SimParams(emission_path="./data") - - Once you have included this in your environment, run your Experiment object - as follows: - - >>> exp.run(num_runs=1, convert_to_csv=True) - - After the experiment is complete, look at the "./data" directory. There - will be two files, one with the suffix .xml and another with the suffix - .csv. The latter should be easily interpretable from any csv reader (e.g. - Excel), and can be parsed using tools such as numpy and pandas. - - Attributes - ---------- - custom_callables : dict < str, lambda > - strings and lambda functions corresponding to some information we want - to extract from the environment. The lambda will be called at each step - to extract information from the env and it will be stored in a dict - keyed by the str. - env : flow.envs.Env - the environment object the simulator will run - """ - - def __init__(self, flow_params, custom_callables=None): - """Instantiate the Experiment class. - - Parameters - ---------- - flow_params : dict - flow-specific parameters - custom_callables : dict < str, lambda > - strings and lambda functions corresponding to some information we - want to extract from the environment. The lambda will be called at - each step to extract information from the env and it will be stored - in a dict keyed by the str. - """ - self.custom_callables = custom_callables or {} - - # Get the env name and a creator for the environment. - create_env, _ = make_create_env(flow_params) - - # Create the environment. - self.env = create_env() - - logging.info(" Starting experiment {} at {}".format( - self.env.network.name, str(datetime.datetime.utcnow()))) - - logging.info("Initializing environment.") - - def run(self, num_runs, rl_actions=None, convert_to_csv=False, partition_name=None): - """Run the given network for a set number of runs. - - Parameters - ---------- - num_runs : int - number of runs the experiment should perform - rl_actions : method, optional - maps states to actions to be performed by the RL agents (if - there are any) - convert_to_csv : bool - Specifies whether to convert the emission file created by sumo - into a csv file - partition_name: str - Specifies the S3 partition you want to store the output file, - will be used to later for query. If NONE, won't upload output - to S3. - - Returns - ------- - info_dict : dict < str, Any > - contains returns, average speed per step - """ - num_steps = self.env.env_params.horizon - - # raise an error if convert_to_csv is set to True but no emission - # file will be generated, to avoid getting an error at the end of the - # simulation - if convert_to_csv and self.env.sim_params.emission_path is None: - raise ValueError( - 'The experiment was run with convert_to_csv set ' - 'to True, but no emission file will be generated. If you wish ' - 'to generate an emission file, you should set the parameter ' - 'emission_path in the simulation parameters (SumoParams or ' - 'AimsunParams) to the path of the folder where emissions ' - 'output should be generated. If you do not wish to generate ' - 'emissions, set the convert_to_csv parameter to False.') - - # used to store - info_dict = { - "returns": [], - "velocities": [], - "outflows": [], - } - info_dict.update({ - key: [] for key in self.custom_callables.keys() - }) - - if rl_actions is None: - def rl_actions(*_): - return None - - # time profiling information - t = time.time() - times = [] - extra_info = {"time": [], "id": [], "headway": [], "acceleration": [], "leader_id": [], "follower_id": [], - "leader_rel_speed": [], "accel_without_noise": [], "road_grade": []} - - for i in range(num_runs): - ret = 0 - vel = [] - custom_vals = {key: [] for key in self.custom_callables.keys()} - state = self.env.reset() - for j in range(num_steps): - t0 = time.time() - state, reward, done, _ = self.env.step(rl_actions(state)) - t1 = time.time() - times.append(1 / (t1 - t0)) - - # Compute the velocity speeds and cumulative returns. - veh_ids = self.env.k.vehicle.get_ids() - vel.append(np.mean(self.env.k.vehicle.get_speed(veh_ids))) - ret += reward - - # collect additional information for the data pipeline - for vid in veh_ids: - extra_info["time"].append(self.env.k.vehicle.get_timestep(veh_ids[0]) / 1000) - extra_info["id"].append(vid) - extra_info["headway"].append(self.env.k.vehicle.get_headway(vid)) - extra_info["acceleration"].append(self.env.k.vehicle.get_accel(vid)) - extra_info["leader_id"].append(self.env.k.vehicle.get_leader(vid)) - extra_info["follower_id"].append(self.env.k.vehicle.get_follower(vid)) - extra_info["leader_rel_speed"].append(self.env.k.vehicle.get_speed(self.env.k.vehicle.get_leader(vid)) - self.env.k.vehicle.get_speed(vid)) - extra_info["accel_without_noise"].append(self.env.k.vehicle.get_accel_without_noise(vid)) - extra_info["road_grade"].append(self.env.k.vehicle.get_road_grade(vid)) - - # Compute the results for the custom callables. - for (key, lambda_func) in self.custom_callables.items(): - custom_vals[key].append(lambda_func(self.env)) - - if type(done) is dict and done['__all__'] or type(done) is not dict and done: - break - - # Store the information from the run in info_dict. - outflow = self.env.k.vehicle.get_outflow_rate(int(500)) - info_dict["returns"].append(ret) - info_dict["velocities"].append(np.mean(vel)) - info_dict["outflows"].append(outflow) - for key in custom_vals.keys(): - info_dict[key].append(np.mean(custom_vals[key])) - - print("Round {0}, return: {1}".format(i, ret)) - - # Print the averages/std for all variables in the info_dict. - for key in info_dict.keys(): - print("Average, std {}: {}, {}".format( - key, np.mean(info_dict[key]), np.std(info_dict[key]))) - - print("Total time:", time.time() - t) - print("steps/second:", np.mean(times)) - self.env.terminate() - - if convert_to_csv and self.env.simulator == "traci": - # wait a short period of time to ensure the xml file is readable - time.sleep(0.1) - - # collect the location of the emission file - dir_path = self.env.sim_params.emission_path - emission_filename = \ - "{0}-emission.xml".format(self.env.network.name) - emission_path = os.path.join(dir_path, emission_filename) - - # convert the emission file into a csv - emission_to_csv(emission_path) - - # Delete the .xml version of the emission file. - os.remove(emission_path) - - output_file = generate_trajectory_table(emission_path[:-4] + ".csv", extra_info, partition_name) - - if partition_name: - upload_to_s3('brent.experiments', 'trajectory-output/' + 'partition_name=' + partition_name + '/' - + output_file.split('/')[-1], output_file) - - return info_dict +"""Contains an experiment class for running simulations.""" from flow.core.util import emission_to_csv from flow.utils.registry import make_create_env from examples.data_pipeline import generate_trajectory_from_flow, upload_to_s3 import datetime import logging import time import os import numpy as np class Experiment: """ Class for systematically running simulations in any supported simulator. This class acts as a runner for a network and environment. In order to use it to run an network and environment in the absence of a method specifying the actions of RL agents in the network, type the following: >>> from flow.envs import Env >>> flow_params = dict(...) # see the examples in exp_config >>> exp = Experiment(flow_params) # for some experiment configuration >>> exp.run(num_runs=1) If you wish to specify the actions of RL agents in the network, this may be done as follows: >>> rl_actions = lambda state: 0 # replace with something appropriate >>> exp.run(num_runs=1, rl_actions=rl_actions) Finally, if you would like to like to plot and visualize your results, this class can generate csv files from emission files produced by sumo. These files will contain the speeds, positions, edges, etc... of every vehicle in the network at every time step. In order to ensure that the simulator constructs an emission file, set the ``emission_path`` attribute in ``SimParams`` to some path. >>> from flow.core.params import SimParams >>> flow_params['sim'] = SimParams(emission_path="./data") Once you have included this in your environment, run your Experiment object as follows: >>> exp.run(num_runs=1, convert_to_csv=True) After the experiment is complete, look at the "./data" directory. There will be two files, one with the suffix .xml and another with the suffix .csv. The latter should be easily interpretable from any csv reader (e.g. Excel), and can be parsed using tools such as numpy and pandas. Attributes ---------- custom_callables : dict < str, lambda > strings and lambda functions corresponding to some information we want to extract from the environment. The lambda will be called at each step to extract information from the env and it will be stored in a dict keyed by the str. env : flow.envs.Env the environment object the simulator will run """ def __init__(self, flow_params, custom_callables=None): """Instantiate the Experiment class. Parameters ---------- flow_params : dict flow-specific parameters custom_callables : dict < str, lambda > strings and lambda functions corresponding to some information we want to extract from the environment. The lambda will be called at each step to extract information from the env and it will be stored in a dict keyed by the str. """ self.custom_callables = custom_callables or {} # Get the env name and a creator for the environment. create_env, _ = make_create_env(flow_params) # Create the environment. self.env = create_env() logging.info(" Starting experiment {} at {}".format( self.env.network.name, str(datetime.datetime.utcnow()))) logging.info("Initializing environment.") def run(self, num_runs, rl_actions=None, convert_to_csv=False, partition_name=None): """Run the given network for a set number of runs. Parameters ---------- num_runs : int number of runs the experiment should perform rl_actions : method, optional maps states to actions to be performed by the RL agents (if there are any) convert_to_csv : bool Specifies whether to convert the emission file created by sumo into a csv file partition_name: str Specifies the S3 partition you want to store the output file, will be used to later for query. If NONE, won't upload output to S3. Returns ------- info_dict : dict < str, Any > contains returns, average speed per step """ num_steps = self.env.env_params.horizon # raise an error if convert_to_csv is set to True but no emission # file will be generated, to avoid getting an error at the end of the # simulation if convert_to_csv and self.env.sim_params.emission_path is None: raise ValueError( 'The experiment was run with convert_to_csv set ' 'to True, but no emission file will be generated. If you wish ' 'to generate an emission file, you should set the parameter ' 'emission_path in the simulation parameters (SumoParams or ' 'AimsunParams) to the path of the folder where emissions ' 'output should be generated. If you do not wish to generate ' 'emissions, set the convert_to_csv parameter to False.') # used to store info_dict = { "returns": [], "velocities": [], "outflows": [], } info_dict.update({ key: [] for key in self.custom_callables.keys() }) if rl_actions is None: def rl_actions(*_): return None # time profiling information t = time.time() times = [] extra_info = {"time": [], "id": [], "x": [], "y": [], "speed": [], "headway": [], "acceleration": [], "leader_id": [], "follower_id": [], "leader_rel_speed": [], "accel_without_noise": [], "road_grade": [], "source_id": []} source_id = self.env.network.name for i in range(num_runs): ret = 0 vel = [] custom_vals = {key: [] for key in self.custom_callables.keys()} state = self.env.reset() for j in range(num_steps): t0 = time.time() state, reward, done, _ = self.env.step(rl_actions(state)) t1 = time.time() times.append(1 / (t1 - t0)) # Compute the velocity speeds and cumulative returns. veh_ids = self.env.k.vehicle.get_ids() vel.append(np.mean(self.env.k.vehicle.get_speed(veh_ids))) ret += reward # collect additional information for the data pipeline for vid in veh_ids: extra_info["time"].append(self.env.k.vehicle.get_timestep(vid) / 1000) extra_info["id"].append(vid) extra_info["headway"].append(self.env.k.vehicle.get_headway(vid)) extra_info["acceleration"].append(self.env.k.vehicle.get_accel(vid)) extra_info["leader_id"].append(self.env.k.vehicle.get_leader(vid)) extra_info["follower_id"].append(self.env.k.vehicle.get_follower(vid)) extra_info["leader_rel_speed"].append(self.env.k.vehicle.get_speed( self.env.k.vehicle.get_leader(vid)) - self.env.k.vehicle.get_speed(vid)) extra_info["accel_without_noise"].append(self.env.k.vehicle.get_accel_without_noise(vid)) extra_info["road_grade"].append(self.env.k.vehicle.get_road_grade(vid)) position = self.env.k.vehicle.get_2D_position(vid) extra_info["x"].append(position[0]) extra_info["y"].append(position[1]) extra_info["speed"].append(self.env.k.vehicle.get_speed(vid)) extra_info["source_id"].extend([source_id+"run" + str(i)] * len(veh_ids)) # Compute the results for the custom callables. for (key, lambda_func) in self.custom_callables.items(): custom_vals[key].append(lambda_func(self.env)) if done: break # Store the information from the run in info_dict. outflow = self.env.k.vehicle.get_outflow_rate(int(500)) info_dict["returns"].append(ret) info_dict["velocities"].append(np.mean(vel)) info_dict["outflows"].append(outflow) for key in custom_vals.keys(): info_dict[key].append(np.mean(custom_vals[key])) print("Round {0}, return: {1}".format(i, ret)) # Print the averages/std for all variables in the info_dict. for key in info_dict.keys(): print("Average, std {}: {}, {}".format( key, np.mean(info_dict[key]), np.std(info_dict[key]))) print("Total time:", time.time() - t) print("steps/second:", np.mean(times)) self.env.terminate() if convert_to_csv and self.env.simulator == "traci": # wait a short period of time to ensure the xml file is readable time.sleep(0.1) # collect the location of the emission file dir_path = self.env.sim_params.emission_path emission_filename = \ "{0}-emission.xml".format(self.env.network.name) emission_path = os.path.join(dir_path, emission_filename) # convert the emission file into a csv emission_to_csv(emission_path) # Delete the .xml version of the emission file. os.remove(emission_path) trajectory_table_path = emission_path[:-4] + "_trajectory.csv" upload_file_path = generate_trajectory_from_flow(trajectory_table_path, extra_info, partition_name) if partition_name: upload_to_s3('brent.experiments', 'trajectory-output/' + 'partition_name=' + partition_name + '/' + upload_file_path.split('/')[-1], upload_file_path) # delete the S3-only version of the trajectory file os.remove(upload_file_path) return info_dict \ No newline at end of file diff --git a/flow/core/kernel/vehicle/base.py b/flow/core/kernel/vehicle/base.py index 0c992503c..3c285697f 100644 --- a/flow/core/kernel/vehicle/base.py +++ b/flow/core/kernel/vehicle/base.py @@ -697,6 +697,10 @@ def update_accel_without_noise(self, veh_id, accel_without_noise): """ see traci class """ raise NotImplementedError + def get_2D_position(self, veh_id, error=-1001): + """ see traci class """ + raise NotImplementedError + def get_accel_without_noise(self, veh_id): """ see traci class """ raise NotImplementedError diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index bdf94579a..889528b36 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -1140,6 +1140,9 @@ def update_accel_without_noise(self, veh_id, accel_without_noise): def get_accel_without_noise(self, veh_id): return self.__vehicles[veh_id]["accel_without_noise"] + def get_2D_position(self, veh_id, error=-1001): + return self.__sumo_obs.get(veh_id, {}).get(tc.VAR_POSITION, error) + def get_road_grade(self, veh_id): # TODO return 0 From dc881e06442f642538320c1792dec529abad6086 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Wed, 22 Apr 2020 05:22:01 -0700 Subject: [PATCH 37/89] added more support for lambda function --- examples/data_pipeline.py | 28 ++++++++++++++++++++++++++-- examples/lambda_function.py | 26 ++++++++++++++++++++++++++ examples/simulate.py | 8 +++++++- flow/core/experiment.py | 2 +- 4 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 examples/lambda_function.py diff --git a/examples/data_pipeline.py b/examples/data_pipeline.py index 9d56548c2..28d3b5e73 100644 --- a/examples/data_pipeline.py +++ b/examples/data_pipeline.py @@ -39,6 +39,24 @@ def generate_trajectory_table(data_path, extra_info, partition_name): def generate_trajectory_from_flow(data_path, extra_info, partition_name): + """ generate desired output for the trajectory_table based only on flow output + + Parameters + ---------- + data_path : str + output file path + extra_info: dict + extra information needed in the trajectory table, collected from flow + partition_name: str + the name of the partition to put this output to + Returns + ------- + output_file_path: str + the local path of the outputted csv file that should be used for + upload to s3 only, it does not the human readable column names and + will be deleted after uploading to s3. A copy of this file with all + the column name will remain in the ./data folder + """ extra_info = pd.DataFrame.from_dict(extra_info) # extra_info["partition"] = partition_name extra_info.to_csv(data_path, index=False) @@ -47,7 +65,7 @@ def generate_trajectory_from_flow(data_path, extra_info, partition_name): return upload_only_file_path -def upload_to_s3(bucket_name, bucket_key, file_path): +def upload_to_s3(bucket_name, bucket_key, file_path, only_query): """ upload a file to S3 bucket Parameters @@ -58,9 +76,15 @@ def upload_to_s3(bucket_name, bucket_key, file_path): the key within the bucket for the file file_path: str the path of the file to be uploaded + only_query: str + specify which query should be run on this file by lambda: + if empty: run none of them + if "all": run all available analysis query + if a string of list of queries: run only those mentioned in the list """ s3 = boto3.resource("s3") - s3.Bucket(bucket_name).upload_file(file_path, bucket_key) + s3.Bucket(bucket_name).upload_file(file_path, bucket_key, + ExtraArgs={"Metadata": {"run-query": only_query}}) return diff --git a/examples/lambda_function.py b/examples/lambda_function.py new file mode 100644 index 000000000..01ce1512a --- /dev/null +++ b/examples/lambda_function.py @@ -0,0 +1,26 @@ +import boto3 +from urllib.parse import unquote_plus +from examples.data_pipeline import AthenaQuery +from examples.query import tags + +s3 = boto3.client('s3') +queryEngine = AthenaQuery() + + +def lambda_handler(event, context): + for record in event['Records']: + bucket = record['s3']['bucket']['name'] + key = unquote_plus(record['s3']['object']['key']) + partition = key.split('/')[-2].split('=')[-1] + response = s3.head_object(Bucket=bucket, Key=key) + run_query = response["Metadata"]["run-query"] + + if bucket == 'brent.experiments' and 'trajectory-output/' in key: + if run_query == "all": + query_list = tags["analysis"] + elif not run_query: + break + else: + query_list = run_query.split("\', \'") + for query_name in query_list: + queryEngine.run_query(query_name, 's3://brent.experiments/query-result/auto/', partition) \ No newline at end of file diff --git a/examples/simulate.py b/examples/simulate.py index f54bb38d9..69e11b2fb 100644 --- a/examples/simulate.py +++ b/examples/simulate.py @@ -54,6 +54,12 @@ def parse_args(args): help='Specifies the name of the partition to store the output' 'file on S3. Putting not None value for this argument' 'automatically set gen_emission to True.') + parser.add_argument( + '--only_query', + nargs='*', default="[\'all\']", + help='specify which query should be run by lambda' + 'for detail, see upload_to_s3 in data_pipeline.py' + ) return parser.parse_known_args(args)[0] @@ -91,4 +97,4 @@ def parse_args(args): exp = Experiment(flow_params, callables) # Run for the specified number of rollouts. - exp.run(flags.num_runs, convert_to_csv=flags.gen_emission, partition_name=flags.to_aws) + exp.run(flags.num_runs, convert_to_csv=flags.gen_emission, partition_name=flags.to_aws, only_query=flags.only_query) diff --git a/flow/core/experiment.py b/flow/core/experiment.py index 63c92e798..12391f9ae 100755 --- a/flow/core/experiment.py +++ b/flow/core/experiment.py @@ -1 +1 @@ -"""Contains an experiment class for running simulations.""" from flow.core.util import emission_to_csv from flow.utils.registry import make_create_env from examples.data_pipeline import generate_trajectory_from_flow, upload_to_s3 import datetime import logging import time import os import numpy as np class Experiment: """ Class for systematically running simulations in any supported simulator. This class acts as a runner for a network and environment. In order to use it to run an network and environment in the absence of a method specifying the actions of RL agents in the network, type the following: >>> from flow.envs import Env >>> flow_params = dict(...) # see the examples in exp_config >>> exp = Experiment(flow_params) # for some experiment configuration >>> exp.run(num_runs=1) If you wish to specify the actions of RL agents in the network, this may be done as follows: >>> rl_actions = lambda state: 0 # replace with something appropriate >>> exp.run(num_runs=1, rl_actions=rl_actions) Finally, if you would like to like to plot and visualize your results, this class can generate csv files from emission files produced by sumo. These files will contain the speeds, positions, edges, etc... of every vehicle in the network at every time step. In order to ensure that the simulator constructs an emission file, set the ``emission_path`` attribute in ``SimParams`` to some path. >>> from flow.core.params import SimParams >>> flow_params['sim'] = SimParams(emission_path="./data") Once you have included this in your environment, run your Experiment object as follows: >>> exp.run(num_runs=1, convert_to_csv=True) After the experiment is complete, look at the "./data" directory. There will be two files, one with the suffix .xml and another with the suffix .csv. The latter should be easily interpretable from any csv reader (e.g. Excel), and can be parsed using tools such as numpy and pandas. Attributes ---------- custom_callables : dict < str, lambda > strings and lambda functions corresponding to some information we want to extract from the environment. The lambda will be called at each step to extract information from the env and it will be stored in a dict keyed by the str. env : flow.envs.Env the environment object the simulator will run """ def __init__(self, flow_params, custom_callables=None): """Instantiate the Experiment class. Parameters ---------- flow_params : dict flow-specific parameters custom_callables : dict < str, lambda > strings and lambda functions corresponding to some information we want to extract from the environment. The lambda will be called at each step to extract information from the env and it will be stored in a dict keyed by the str. """ self.custom_callables = custom_callables or {} # Get the env name and a creator for the environment. create_env, _ = make_create_env(flow_params) # Create the environment. self.env = create_env() logging.info(" Starting experiment {} at {}".format( self.env.network.name, str(datetime.datetime.utcnow()))) logging.info("Initializing environment.") def run(self, num_runs, rl_actions=None, convert_to_csv=False, partition_name=None): """Run the given network for a set number of runs. Parameters ---------- num_runs : int number of runs the experiment should perform rl_actions : method, optional maps states to actions to be performed by the RL agents (if there are any) convert_to_csv : bool Specifies whether to convert the emission file created by sumo into a csv file partition_name: str Specifies the S3 partition you want to store the output file, will be used to later for query. If NONE, won't upload output to S3. Returns ------- info_dict : dict < str, Any > contains returns, average speed per step """ num_steps = self.env.env_params.horizon # raise an error if convert_to_csv is set to True but no emission # file will be generated, to avoid getting an error at the end of the # simulation if convert_to_csv and self.env.sim_params.emission_path is None: raise ValueError( 'The experiment was run with convert_to_csv set ' 'to True, but no emission file will be generated. If you wish ' 'to generate an emission file, you should set the parameter ' 'emission_path in the simulation parameters (SumoParams or ' 'AimsunParams) to the path of the folder where emissions ' 'output should be generated. If you do not wish to generate ' 'emissions, set the convert_to_csv parameter to False.') # used to store info_dict = { "returns": [], "velocities": [], "outflows": [], } info_dict.update({ key: [] for key in self.custom_callables.keys() }) if rl_actions is None: def rl_actions(*_): return None # time profiling information t = time.time() times = [] extra_info = {"time": [], "id": [], "x": [], "y": [], "speed": [], "headway": [], "acceleration": [], "leader_id": [], "follower_id": [], "leader_rel_speed": [], "accel_without_noise": [], "road_grade": [], "source_id": []} source_id = self.env.network.name for i in range(num_runs): ret = 0 vel = [] custom_vals = {key: [] for key in self.custom_callables.keys()} state = self.env.reset() for j in range(num_steps): t0 = time.time() state, reward, done, _ = self.env.step(rl_actions(state)) t1 = time.time() times.append(1 / (t1 - t0)) # Compute the velocity speeds and cumulative returns. veh_ids = self.env.k.vehicle.get_ids() vel.append(np.mean(self.env.k.vehicle.get_speed(veh_ids))) ret += reward # collect additional information for the data pipeline for vid in veh_ids: extra_info["time"].append(self.env.k.vehicle.get_timestep(vid) / 1000) extra_info["id"].append(vid) extra_info["headway"].append(self.env.k.vehicle.get_headway(vid)) extra_info["acceleration"].append(self.env.k.vehicle.get_accel(vid)) extra_info["leader_id"].append(self.env.k.vehicle.get_leader(vid)) extra_info["follower_id"].append(self.env.k.vehicle.get_follower(vid)) extra_info["leader_rel_speed"].append(self.env.k.vehicle.get_speed( self.env.k.vehicle.get_leader(vid)) - self.env.k.vehicle.get_speed(vid)) extra_info["accel_without_noise"].append(self.env.k.vehicle.get_accel_without_noise(vid)) extra_info["road_grade"].append(self.env.k.vehicle.get_road_grade(vid)) position = self.env.k.vehicle.get_2D_position(vid) extra_info["x"].append(position[0]) extra_info["y"].append(position[1]) extra_info["speed"].append(self.env.k.vehicle.get_speed(vid)) extra_info["source_id"].extend([source_id+"run" + str(i)] * len(veh_ids)) # Compute the results for the custom callables. for (key, lambda_func) in self.custom_callables.items(): custom_vals[key].append(lambda_func(self.env)) if done: break # Store the information from the run in info_dict. outflow = self.env.k.vehicle.get_outflow_rate(int(500)) info_dict["returns"].append(ret) info_dict["velocities"].append(np.mean(vel)) info_dict["outflows"].append(outflow) for key in custom_vals.keys(): info_dict[key].append(np.mean(custom_vals[key])) print("Round {0}, return: {1}".format(i, ret)) # Print the averages/std for all variables in the info_dict. for key in info_dict.keys(): print("Average, std {}: {}, {}".format( key, np.mean(info_dict[key]), np.std(info_dict[key]))) print("Total time:", time.time() - t) print("steps/second:", np.mean(times)) self.env.terminate() if convert_to_csv and self.env.simulator == "traci": # wait a short period of time to ensure the xml file is readable time.sleep(0.1) # collect the location of the emission file dir_path = self.env.sim_params.emission_path emission_filename = \ "{0}-emission.xml".format(self.env.network.name) emission_path = os.path.join(dir_path, emission_filename) # convert the emission file into a csv emission_to_csv(emission_path) # Delete the .xml version of the emission file. os.remove(emission_path) trajectory_table_path = emission_path[:-4] + "_trajectory.csv" upload_file_path = generate_trajectory_from_flow(trajectory_table_path, extra_info, partition_name) if partition_name: upload_to_s3('brent.experiments', 'trajectory-output/' + 'partition_name=' + partition_name + '/' + upload_file_path.split('/')[-1], upload_file_path) # delete the S3-only version of the trajectory file os.remove(upload_file_path) return info_dict \ No newline at end of file +"""Contains an experiment class for running simulations.""" from flow.core.util import emission_to_csv from flow.utils.registry import make_create_env from examples.data_pipeline import generate_trajectory_from_flow, upload_to_s3 import datetime import logging import time import os import numpy as np import uuid class Experiment: """ Class for systematically running simulations in any supported simulator. This class acts as a runner for a network and environment. In order to use it to run an network and environment in the absence of a method specifying the actions of RL agents in the network, type the following: >>> from flow.envs import Env >>> flow_params = dict(...) # see the examples in exp_config >>> exp = Experiment(flow_params) # for some experiment configuration >>> exp.run(num_runs=1) If you wish to specify the actions of RL agents in the network, this may be done as follows: >>> rl_actions = lambda state: 0 # replace with something appropriate >>> exp.run(num_runs=1, rl_actions=rl_actions) Finally, if you would like to like to plot and visualize your results, this class can generate csv files from emission files produced by sumo. These files will contain the speeds, positions, edges, etc... of every vehicle in the network at every time step. In order to ensure that the simulator constructs an emission file, set the ``emission_path`` attribute in ``SimParams`` to some path. >>> from flow.core.params import SimParams >>> flow_params['sim'] = SimParams(emission_path="./data") Once you have included this in your environment, run your Experiment object as follows: >>> exp.run(num_runs=1, convert_to_csv=True) After the experiment is complete, look at the "./data" directory. There will be two files, one with the suffix .xml and another with the suffix .csv. The latter should be easily interpretable from any csv reader (e.g. Excel), and can be parsed using tools such as numpy and pandas. Attributes ---------- custom_callables : dict < str, lambda > strings and lambda functions corresponding to some information we want to extract from the environment. The lambda will be called at each step to extract information from the env and it will be stored in a dict keyed by the str. env : flow.envs.Env the environment object the simulator will run """ def __init__(self, flow_params, custom_callables=None): """Instantiate the Experiment class. Parameters ---------- flow_params : dict flow-specific parameters custom_callables : dict < str, lambda > strings and lambda functions corresponding to some information we want to extract from the environment. The lambda will be called at each step to extract information from the env and it will be stored in a dict keyed by the str. """ self.custom_callables = custom_callables or {} # Get the env name and a creator for the environment. create_env, _ = make_create_env(flow_params) # Create the environment. self.env = create_env() logging.info(" Starting experiment {} at {}".format( self.env.network.name, str(datetime.datetime.utcnow()))) logging.info("Initializing environment.") def run(self, num_runs, rl_actions=None, convert_to_csv=False, partition_name=None, only_query=None): """Run the given network for a set number of runs. Parameters ---------- num_runs : int number of runs the experiment should perform rl_actions : method, optional maps states to actions to be performed by the RL agents (if there are any) convert_to_csv : bool Specifies whether to convert the emission file created by sumo into a csv file partition_name: str Specifies the S3 partition you want to store the output file, will be used to later for query. If NONE, won't upload output to S3. only_query: str Specifies whether queries should be automatically run the simulation data when it gets uploaded to s3 Returns ------- info_dict : dict < str, Any > contains returns, average speed per step """ num_steps = self.env.env_params.horizon # raise an error if convert_to_csv is set to True but no emission # file will be generated, to avoid getting an error at the end of the # simulation if convert_to_csv and self.env.sim_params.emission_path is None: raise ValueError( 'The experiment was run with convert_to_csv set ' 'to True, but no emission file will be generated. If you wish ' 'to generate an emission file, you should set the parameter ' 'emission_path in the simulation parameters (SumoParams or ' 'AimsunParams) to the path of the folder where emissions ' 'output should be generated. If you do not wish to generate ' 'emissions, set the convert_to_csv parameter to False.') # used to store info_dict = { "returns": [], "velocities": [], "outflows": [], } info_dict.update({ key: [] for key in self.custom_callables.keys() }) if rl_actions is None: def rl_actions(*_): return None # time profiling information t = time.time() times = [] extra_info = {"time": [], "id": [], "x": [], "y": [], "speed": [], "headway": [], "acceleration": [], "leader_id": [], "follower_id": [], "leader_rel_speed": [], "accel_without_noise": [], "road_grade": [], "source_id": []} source_id = uuid.uuid4().hex for i in range(num_runs): ret = 0 vel = [] custom_vals = {key: [] for key in self.custom_callables.keys()} state = self.env.reset() for j in range(num_steps): t0 = time.time() state, reward, done, _ = self.env.step(rl_actions(state)) t1 = time.time() times.append(1 / (t1 - t0)) # Compute the velocity speeds and cumulative returns. veh_ids = self.env.k.vehicle.get_ids() vel.append(np.mean(self.env.k.vehicle.get_speed(veh_ids))) ret += reward # collect additional information for the data pipeline for vid in veh_ids: extra_info["time"].append(self.env.k.vehicle.get_timestep(vid) / 1000) extra_info["id"].append(vid) extra_info["headway"].append(self.env.k.vehicle.get_headway(vid)) extra_info["acceleration"].append(self.env.k.vehicle.get_accel(vid)) extra_info["leader_id"].append(self.env.k.vehicle.get_leader(vid)) extra_info["follower_id"].append(self.env.k.vehicle.get_follower(vid)) extra_info["leader_rel_speed"].append(self.env.k.vehicle.get_speed( self.env.k.vehicle.get_leader(vid)) - self.env.k.vehicle.get_speed(vid)) extra_info["accel_without_noise"].append(self.env.k.vehicle.get_accel_without_noise(vid)) extra_info["road_grade"].append(self.env.k.vehicle.get_road_grade(vid)) position = self.env.k.vehicle.get_2D_position(vid) extra_info["x"].append(position[0]) extra_info["y"].append(position[1]) extra_info["speed"].append(self.env.k.vehicle.get_speed(vid)) extra_info["source_id"].extend([source_id+"run" + str(i)] * len(veh_ids)) # Compute the results for the custom callables. for (key, lambda_func) in self.custom_callables.items(): custom_vals[key].append(lambda_func(self.env)) if done: break # Store the information from the run in info_dict. outflow = self.env.k.vehicle.get_outflow_rate(int(500)) info_dict["returns"].append(ret) info_dict["velocities"].append(np.mean(vel)) info_dict["outflows"].append(outflow) for key in custom_vals.keys(): info_dict[key].append(np.mean(custom_vals[key])) print("Round {0}, return: {1}".format(i, ret)) # Print the averages/std for all variables in the info_dict. for key in info_dict.keys(): print("Average, std {}: {}, {}".format( key, np.mean(info_dict[key]), np.std(info_dict[key]))) print("Total time:", time.time() - t) print("steps/second:", np.mean(times)) self.env.terminate() if convert_to_csv and self.env.simulator == "traci": # wait a short period of time to ensure the xml file is readable time.sleep(0.1) # collect the location of the emission file dir_path = self.env.sim_params.emission_path emission_filename = \ "{0}-emission.xml".format(self.env.network.name) emission_path = os.path.join(dir_path, emission_filename) # convert the emission file into a csv emission_to_csv(emission_path) # Delete the .xml version of the emission file. os.remove(emission_path) trajectory_table_path = './data/' + source_id + ".csv" upload_file_path = generate_trajectory_from_flow(trajectory_table_path, extra_info, partition_name) if partition_name: upload_to_s3('brent.experiments', 'trajectory-output/' + 'partition_name=' + partition_name + '/' + upload_file_path.split('/')[-1].split('_')[0] + '.csv', upload_file_path, str(only_query)[2:-2]) # delete the S3-only version of the trajectory file os.remove(upload_file_path) return info_dict \ No newline at end of file From ee1188ec7b5796aeb96bc7de89c5d9bfd10168de Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Thu, 23 Apr 2020 02:54:33 -0700 Subject: [PATCH 38/89] fix windoes line ending issue with experiment.py --- flow/core/experiment.py | 240 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 239 insertions(+), 1 deletion(-) diff --git a/flow/core/experiment.py b/flow/core/experiment.py index 12391f9ae..80d607e7d 100755 --- a/flow/core/experiment.py +++ b/flow/core/experiment.py @@ -1 +1,239 @@ -"""Contains an experiment class for running simulations.""" from flow.core.util import emission_to_csv from flow.utils.registry import make_create_env from examples.data_pipeline import generate_trajectory_from_flow, upload_to_s3 import datetime import logging import time import os import numpy as np import uuid class Experiment: """ Class for systematically running simulations in any supported simulator. This class acts as a runner for a network and environment. In order to use it to run an network and environment in the absence of a method specifying the actions of RL agents in the network, type the following: >>> from flow.envs import Env >>> flow_params = dict(...) # see the examples in exp_config >>> exp = Experiment(flow_params) # for some experiment configuration >>> exp.run(num_runs=1) If you wish to specify the actions of RL agents in the network, this may be done as follows: >>> rl_actions = lambda state: 0 # replace with something appropriate >>> exp.run(num_runs=1, rl_actions=rl_actions) Finally, if you would like to like to plot and visualize your results, this class can generate csv files from emission files produced by sumo. These files will contain the speeds, positions, edges, etc... of every vehicle in the network at every time step. In order to ensure that the simulator constructs an emission file, set the ``emission_path`` attribute in ``SimParams`` to some path. >>> from flow.core.params import SimParams >>> flow_params['sim'] = SimParams(emission_path="./data") Once you have included this in your environment, run your Experiment object as follows: >>> exp.run(num_runs=1, convert_to_csv=True) After the experiment is complete, look at the "./data" directory. There will be two files, one with the suffix .xml and another with the suffix .csv. The latter should be easily interpretable from any csv reader (e.g. Excel), and can be parsed using tools such as numpy and pandas. Attributes ---------- custom_callables : dict < str, lambda > strings and lambda functions corresponding to some information we want to extract from the environment. The lambda will be called at each step to extract information from the env and it will be stored in a dict keyed by the str. env : flow.envs.Env the environment object the simulator will run """ def __init__(self, flow_params, custom_callables=None): """Instantiate the Experiment class. Parameters ---------- flow_params : dict flow-specific parameters custom_callables : dict < str, lambda > strings and lambda functions corresponding to some information we want to extract from the environment. The lambda will be called at each step to extract information from the env and it will be stored in a dict keyed by the str. """ self.custom_callables = custom_callables or {} # Get the env name and a creator for the environment. create_env, _ = make_create_env(flow_params) # Create the environment. self.env = create_env() logging.info(" Starting experiment {} at {}".format( self.env.network.name, str(datetime.datetime.utcnow()))) logging.info("Initializing environment.") def run(self, num_runs, rl_actions=None, convert_to_csv=False, partition_name=None, only_query=None): """Run the given network for a set number of runs. Parameters ---------- num_runs : int number of runs the experiment should perform rl_actions : method, optional maps states to actions to be performed by the RL agents (if there are any) convert_to_csv : bool Specifies whether to convert the emission file created by sumo into a csv file partition_name: str Specifies the S3 partition you want to store the output file, will be used to later for query. If NONE, won't upload output to S3. only_query: str Specifies whether queries should be automatically run the simulation data when it gets uploaded to s3 Returns ------- info_dict : dict < str, Any > contains returns, average speed per step """ num_steps = self.env.env_params.horizon # raise an error if convert_to_csv is set to True but no emission # file will be generated, to avoid getting an error at the end of the # simulation if convert_to_csv and self.env.sim_params.emission_path is None: raise ValueError( 'The experiment was run with convert_to_csv set ' 'to True, but no emission file will be generated. If you wish ' 'to generate an emission file, you should set the parameter ' 'emission_path in the simulation parameters (SumoParams or ' 'AimsunParams) to the path of the folder where emissions ' 'output should be generated. If you do not wish to generate ' 'emissions, set the convert_to_csv parameter to False.') # used to store info_dict = { "returns": [], "velocities": [], "outflows": [], } info_dict.update({ key: [] for key in self.custom_callables.keys() }) if rl_actions is None: def rl_actions(*_): return None # time profiling information t = time.time() times = [] extra_info = {"time": [], "id": [], "x": [], "y": [], "speed": [], "headway": [], "acceleration": [], "leader_id": [], "follower_id": [], "leader_rel_speed": [], "accel_without_noise": [], "road_grade": [], "source_id": []} source_id = uuid.uuid4().hex for i in range(num_runs): ret = 0 vel = [] custom_vals = {key: [] for key in self.custom_callables.keys()} state = self.env.reset() for j in range(num_steps): t0 = time.time() state, reward, done, _ = self.env.step(rl_actions(state)) t1 = time.time() times.append(1 / (t1 - t0)) # Compute the velocity speeds and cumulative returns. veh_ids = self.env.k.vehicle.get_ids() vel.append(np.mean(self.env.k.vehicle.get_speed(veh_ids))) ret += reward # collect additional information for the data pipeline for vid in veh_ids: extra_info["time"].append(self.env.k.vehicle.get_timestep(vid) / 1000) extra_info["id"].append(vid) extra_info["headway"].append(self.env.k.vehicle.get_headway(vid)) extra_info["acceleration"].append(self.env.k.vehicle.get_accel(vid)) extra_info["leader_id"].append(self.env.k.vehicle.get_leader(vid)) extra_info["follower_id"].append(self.env.k.vehicle.get_follower(vid)) extra_info["leader_rel_speed"].append(self.env.k.vehicle.get_speed( self.env.k.vehicle.get_leader(vid)) - self.env.k.vehicle.get_speed(vid)) extra_info["accel_without_noise"].append(self.env.k.vehicle.get_accel_without_noise(vid)) extra_info["road_grade"].append(self.env.k.vehicle.get_road_grade(vid)) position = self.env.k.vehicle.get_2D_position(vid) extra_info["x"].append(position[0]) extra_info["y"].append(position[1]) extra_info["speed"].append(self.env.k.vehicle.get_speed(vid)) extra_info["source_id"].extend([source_id+"run" + str(i)] * len(veh_ids)) # Compute the results for the custom callables. for (key, lambda_func) in self.custom_callables.items(): custom_vals[key].append(lambda_func(self.env)) if done: break # Store the information from the run in info_dict. outflow = self.env.k.vehicle.get_outflow_rate(int(500)) info_dict["returns"].append(ret) info_dict["velocities"].append(np.mean(vel)) info_dict["outflows"].append(outflow) for key in custom_vals.keys(): info_dict[key].append(np.mean(custom_vals[key])) print("Round {0}, return: {1}".format(i, ret)) # Print the averages/std for all variables in the info_dict. for key in info_dict.keys(): print("Average, std {}: {}, {}".format( key, np.mean(info_dict[key]), np.std(info_dict[key]))) print("Total time:", time.time() - t) print("steps/second:", np.mean(times)) self.env.terminate() if convert_to_csv and self.env.simulator == "traci": # wait a short period of time to ensure the xml file is readable time.sleep(0.1) # collect the location of the emission file dir_path = self.env.sim_params.emission_path emission_filename = \ "{0}-emission.xml".format(self.env.network.name) emission_path = os.path.join(dir_path, emission_filename) # convert the emission file into a csv emission_to_csv(emission_path) # Delete the .xml version of the emission file. os.remove(emission_path) trajectory_table_path = './data/' + source_id + ".csv" upload_file_path = generate_trajectory_from_flow(trajectory_table_path, extra_info, partition_name) if partition_name: upload_to_s3('brent.experiments', 'trajectory-output/' + 'partition_name=' + partition_name + '/' + upload_file_path.split('/')[-1].split('_')[0] + '.csv', upload_file_path, str(only_query)[2:-2]) # delete the S3-only version of the trajectory file os.remove(upload_file_path) return info_dict \ No newline at end of file +"""Contains an experiment class for running simulations.""" +from flow.core.util import emission_to_csv +from flow.utils.registry import make_create_env +from examples.data_pipeline import generate_trajectory_from_flow, upload_to_s3 +import datetime +import logging +import time +import os +import numpy as np +import uuid + + +class Experiment: + """ + Class for systematically running simulations in any supported simulator. + + This class acts as a runner for a network and environment. In order to use + it to run an network and environment in the absence of a method specifying + the actions of RL agents in the network, type the following: + + >>> from flow.envs import Env + >>> flow_params = dict(...) # see the examples in exp_config + >>> exp = Experiment(flow_params) # for some experiment configuration + >>> exp.run(num_runs=1) + + If you wish to specify the actions of RL agents in the network, this may be + done as follows: + + >>> rl_actions = lambda state: 0 # replace with something appropriate + >>> exp.run(num_runs=1, rl_actions=rl_actions) + + Finally, if you would like to like to plot and visualize your results, this + class can generate csv files from emission files produced by sumo. These + files will contain the speeds, positions, edges, etc... of every vehicle + in the network at every time step. + + In order to ensure that the simulator constructs an emission file, set the + ``emission_path`` attribute in ``SimParams`` to some path. + + >>> from flow.core.params import SimParams + >>> flow_params['sim'] = SimParams(emission_path="./data") + + Once you have included this in your environment, run your Experiment object + as follows: + + >>> exp.run(num_runs=1, convert_to_csv=True) + + After the experiment is complete, look at the "./data" directory. There + will be two files, one with the suffix .xml and another with the suffix + .csv. The latter should be easily interpretable from any csv reader (e.g. + Excel), and can be parsed using tools such as numpy and pandas. + + Attributes + ---------- + custom_callables : dict < str, lambda > + strings and lambda functions corresponding to some information we want + to extract from the environment. The lambda will be called at each step + to extract information from the env and it will be stored in a dict + keyed by the str. + env : flow.envs.Env + the environment object the simulator will run + """ + + def __init__(self, flow_params, custom_callables=None): + """Instantiate the Experiment class. + + Parameters + ---------- + flow_params : dict + flow-specific parameters + custom_callables : dict < str, lambda > + strings and lambda functions corresponding to some information we + want to extract from the environment. The lambda will be called at + each step to extract information from the env and it will be stored + in a dict keyed by the str. + """ + self.custom_callables = custom_callables or {} + + # Get the env name and a creator for the environment. + create_env, _ = make_create_env(flow_params) + + # Create the environment. + self.env = create_env() + + logging.info(" Starting experiment {} at {}".format( + self.env.network.name, str(datetime.datetime.utcnow()))) + + logging.info("Initializing environment.") + + def run(self, num_runs, rl_actions=None, convert_to_csv=False, partition_name=None, only_query=None): + """Run the given network for a set number of runs. + + Parameters + ---------- + num_runs : int + number of runs the experiment should perform + rl_actions : method, optional + maps states to actions to be performed by the RL agents (if + there are any) + convert_to_csv : bool + Specifies whether to convert the emission file created by sumo + into a csv file + partition_name: str + Specifies the S3 partition you want to store the output file, + will be used to later for query. If NONE, won't upload output + to S3. + only_query: str + Specifies whether queries should be automatically run the + simulation data when it gets uploaded to s3 + + Returns + ------- + info_dict : dict < str, Any > + contains returns, average speed per step + """ + num_steps = self.env.env_params.horizon + + # raise an error if convert_to_csv is set to True but no emission + # file will be generated, to avoid getting an error at the end of the + # simulation + if convert_to_csv and self.env.sim_params.emission_path is None: + raise ValueError( + 'The experiment was run with convert_to_csv set ' + 'to True, but no emission file will be generated. If you wish ' + 'to generate an emission file, you should set the parameter ' + 'emission_path in the simulation parameters (SumoParams or ' + 'AimsunParams) to the path of the folder where emissions ' + 'output should be generated. If you do not wish to generate ' + 'emissions, set the convert_to_csv parameter to False.') + + # used to store + info_dict = { + "returns": [], + "velocities": [], + "outflows": [], + } + info_dict.update({ + key: [] for key in self.custom_callables.keys() + }) + + if rl_actions is None: + def rl_actions(*_): + return None + + # time profiling information + t = time.time() + times = [] + extra_info = {"time": [], "id": [], "x": [], "y": [], "speed": [], "headway": [], "acceleration": [], + "leader_id": [], "follower_id": [], "leader_rel_speed": [], "accel_without_noise": [], + "road_grade": [], "source_id": []} + source_id = uuid.uuid4().hex + + for i in range(num_runs): + ret = 0 + vel = [] + custom_vals = {key: [] for key in self.custom_callables.keys()} + state = self.env.reset() + for j in range(num_steps): + t0 = time.time() + state, reward, done, _ = self.env.step(rl_actions(state)) + t1 = time.time() + times.append(1 / (t1 - t0)) + + # Compute the velocity speeds and cumulative returns. + veh_ids = self.env.k.vehicle.get_ids() + vel.append(np.mean(self.env.k.vehicle.get_speed(veh_ids))) + ret += reward + + # collect additional information for the data pipeline + for vid in veh_ids: + extra_info["time"].append(self.env.k.vehicle.get_timestep(vid) / 1000) + extra_info["id"].append(vid) + extra_info["headway"].append(self.env.k.vehicle.get_headway(vid)) + extra_info["acceleration"].append(self.env.k.vehicle.get_accel(vid)) + extra_info["leader_id"].append(self.env.k.vehicle.get_leader(vid)) + extra_info["follower_id"].append(self.env.k.vehicle.get_follower(vid)) + extra_info["leader_rel_speed"].append(self.env.k.vehicle.get_speed( + self.env.k.vehicle.get_leader(vid)) - self.env.k.vehicle.get_speed(vid)) + extra_info["accel_without_noise"].append(self.env.k.vehicle.get_accel_without_noise(vid)) + extra_info["road_grade"].append(self.env.k.vehicle.get_road_grade(vid)) + position = self.env.k.vehicle.get_2D_position(vid) + extra_info["x"].append(position[0]) + extra_info["y"].append(position[1]) + extra_info["speed"].append(self.env.k.vehicle.get_speed(vid)) + extra_info["source_id"].extend([source_id+"run" + str(i)] * len(veh_ids)) + + # Compute the results for the custom callables. + for (key, lambda_func) in self.custom_callables.items(): + custom_vals[key].append(lambda_func(self.env)) + + if done: + break + + # Store the information from the run in info_dict. + outflow = self.env.k.vehicle.get_outflow_rate(int(500)) + info_dict["returns"].append(ret) + info_dict["velocities"].append(np.mean(vel)) + info_dict["outflows"].append(outflow) + for key in custom_vals.keys(): + info_dict[key].append(np.mean(custom_vals[key])) + + print("Round {0}, return: {1}".format(i, ret)) + + # Print the averages/std for all variables in the info_dict. + for key in info_dict.keys(): + print("Average, std {}: {}, {}".format( + key, np.mean(info_dict[key]), np.std(info_dict[key]))) + + print("Total time:", time.time() - t) + print("steps/second:", np.mean(times)) + self.env.terminate() + + if convert_to_csv and self.env.simulator == "traci": + # wait a short period of time to ensure the xml file is readable + time.sleep(0.1) + + # collect the location of the emission file + dir_path = self.env.sim_params.emission_path + emission_filename = \ + "{0}-emission.xml".format(self.env.network.name) + emission_path = os.path.join(dir_path, emission_filename) + + # convert the emission file into a csv + emission_to_csv(emission_path) + + # Delete the .xml version of the emission file. + os.remove(emission_path) + + trajectory_table_path = './data/' + source_id + ".csv" + upload_file_path = generate_trajectory_from_flow(trajectory_table_path, extra_info, partition_name) + + if partition_name: + upload_to_s3('brent.experiments', 'trajectory-output/' + 'partition_name=' + partition_name + '/' + + upload_file_path.split('/')[-1].split('_')[0] + '.csv', upload_file_path, str(only_query)[2:-2]) + + # delete the S3-only version of the trajectory file + os.remove(upload_file_path) + + return info_dict From 65c9ee061541b4e9660bf54d241a603dabf77e95 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Thu, 23 Apr 2020 12:35:54 -0700 Subject: [PATCH 39/89] fix style issue --- examples/data_pipeline.py | 113 +++++++++++++++++++----------- examples/datapipeline_test.py | 4 ++ examples/lambda_function.py | 10 +++ examples/query.py | 11 ++- examples/run_query.py | 1 + flow/core/kernel/vehicle/base.py | 12 ++-- flow/core/kernel/vehicle/traci.py | 5 ++ 7 files changed, 110 insertions(+), 46 deletions(-) diff --git a/examples/data_pipeline.py b/examples/data_pipeline.py index 28d3b5e73..03b0f87e5 100644 --- a/examples/data_pipeline.py +++ b/examples/data_pipeline.py @@ -1,3 +1,4 @@ +"""contains class and helper functions for the data pipeline.""" import pandas as pd import numpy as np import boto3 @@ -7,21 +8,21 @@ def generate_trajectory_table(data_path, extra_info, partition_name): - """ generate desired output for the trajectory_table based on standard SUMO emission + """Generate desired output for the trajectory_table based on standard SUMO emission. - Parameters - ---------- - data_path : str - path to the standard SUMO emission - extra_info: dict - extra information needed in the trajectory table, collected from flow - partition_name: str - the name of the partition to put this output to - Returns - ------- - output_file_path: str - the local path of the outputted csv file - """ + Parameters + ---------- + data_path : str + path to the standard SUMO emission + extra_info: dict + extra information needed in the trajectory table, collected from flow + partition_name: str + the name of the partition to put this output to + Returns + ------- + output_file_path: str + the local path of the outputted csv file + """ raw_output = pd.read_csv(data_path, index_col=["time", "id"]) required_cols = {"time", "id", "speed", "x", "y"} raw_output = raw_output.drop(set(raw_output.columns) - required_cols, axis=1) @@ -39,24 +40,24 @@ def generate_trajectory_table(data_path, extra_info, partition_name): def generate_trajectory_from_flow(data_path, extra_info, partition_name): - """ generate desired output for the trajectory_table based only on flow output - - Parameters - ---------- - data_path : str - output file path - extra_info: dict - extra information needed in the trajectory table, collected from flow - partition_name: str - the name of the partition to put this output to - Returns - ------- - output_file_path: str - the local path of the outputted csv file that should be used for - upload to s3 only, it does not the human readable column names and - will be deleted after uploading to s3. A copy of this file with all - the column name will remain in the ./data folder - """ + """Generate desired output for the trajectory_table based only on flow output. + + Parameters + ---------- + data_path : str + output file path + extra_info: dict + extra information needed in the trajectory table, collected from flow + partition_name: str + the name of the partition to put this output to + Returns + ------- + output_file_path: str + the local path of the outputted csv file that should be used for + upload to s3 only, it does not the human readable column names and + will be deleted after uploading to s3. A copy of this file with all + the column name will remain in the ./data folder + """ extra_info = pd.DataFrame.from_dict(extra_info) # extra_info["partition"] = partition_name extra_info.to_csv(data_path, index=False) @@ -66,7 +67,7 @@ def generate_trajectory_from_flow(data_path, extra_info, partition_name): def upload_to_s3(bucket_name, bucket_key, file_path, only_query): - """ upload a file to S3 bucket + """Upload a file to S3 bucket. Parameters ---------- @@ -89,15 +90,40 @@ def upload_to_s3(bucket_name, bucket_key, file_path, only_query): class AthenaQuery: + """ + Class used to run query. + + Act as a query engine, maintains an open session with AWS Athena. + + Attributes + ---------- + MAX_WAIT: int + maximum number of seconds to wait before declares time-out + client: boto3.client + the athena client that is used to run the query + existing_partitions: list + a list of partitions that is already recorded in Athena's datalog, + this is obtained through query at the initialization of this class + instance. + """ def __init__(self): + """Initialize AthenaQuery instance. + + initialize a client session with AWS Athena, + query Athena to obtain extisting_partition. + """ self.MAX_WAIT = 60 self.client = boto3.client("athena") self.existing_partitions = self.get_existing_partitions() def get_existing_partitions(self): - """prints the existing partitions in the S3 bucket""" + """Return the existing partitions in the S3 bucket. + Returns + ------- + partitions: a list of existing partitions on S3 bucket + """ response = self.client.start_query_execution( QueryString='SHOW PARTITIONS trajectory_table', QueryExecutionContext={ @@ -114,7 +140,7 @@ def get_existing_partitions(self): return [data['Data'][0]['VarCharValue'].split('=')[-1] for data in response['ResultSet']['Rows']] def check_status(self, execution_id): - """ Return the status of the execution with given id + """Return the status of the execution with given id. Parameters ---------- @@ -125,14 +151,13 @@ def check_status(self, execution_id): status: str QUEUED|RUNNING|SUCCEEDED|FAILED|CANCELLED """ - response = self.client.get_query_execution( QueryExecutionId=execution_id ) return response['QueryExecution']['Status']['State'] def wait_for_execution(self, execution_id): - """ wait for the execution to finish or time-out + """Wait for the execution to finish or time-out. Parameters ---------- @@ -156,7 +181,7 @@ def wait_for_execution(self, execution_id): return True def update_partition(self, partition): - """ load the given partition to the trajectory_table on Athena + """Load the given partition to the trajectory_table on Athena. Parameters ---------- @@ -176,7 +201,7 @@ def update_partition(self, partition): return def run_query(self, query_name, result_location="s3://brent.experiments/query-result/", partition="default"): - """ start the execution of a query, does not wait for it to finish + """Start the execution of a query, does not wait for it to finish. Parameters ---------- @@ -218,6 +243,16 @@ def run_query(self, query_name, result_location="s3://brent.experiments/query-re def test_sql_query(query_name): + """Start the execution of a query, does not wait for it to finish. + + Parameters + ---------- + query_name : str + name of the query in QueryStrings enum that will be tested + Raises + ------ + RuntimeError: if timeout + """ if query_name not in testing_functions: raise ValueError("no tests supported for this query") diff --git a/examples/datapipeline_test.py b/examples/datapipeline_test.py index 564060d3b..ae0ea382f 100644 --- a/examples/datapipeline_test.py +++ b/examples/datapipeline_test.py @@ -1,3 +1,4 @@ +"""functions that calculates the expected result for testing.""" import math # Vehicle Mass @@ -17,10 +18,12 @@ def heavyside(inp): + """Return 1 if input is positive.""" return 0 if inp <= 0 else 1 def calculate_power(mu, acceleration, M=M, g=g, theta=theta, C_r=C_r, ro_air=ro_air, A=A, C_a=C_a): + """Calculate the expected power for POWER_DEMAND_MODEL query.""" acceleration = (0.8 + ((1 - 0.8) * heavyside(acceleration)) * acceleration) accel_and_slope = M * mu * (acceleration + g * math.sin(theta)) rolling_friction = M * g * C_r * mu @@ -30,4 +33,5 @@ def calculate_power(mu, acceleration, M=M, g=g, theta=theta, C_r=C_r, ro_air=ro_ def apply_energy_one(row): + """Apply the power calculation to a row of the dataframe.""" return [row[0], row[1], calculate_power(row[4], row[6])] \ No newline at end of file diff --git a/examples/lambda_function.py b/examples/lambda_function.py index 01ce1512a..4f7937c85 100644 --- a/examples/lambda_function.py +++ b/examples/lambda_function.py @@ -1,3 +1,4 @@ +"""lambda function on AWS Lambda.""" import boto3 from urllib.parse import unquote_plus from examples.data_pipeline import AthenaQuery @@ -8,6 +9,15 @@ def lambda_handler(event, context): + """Invoke by AWS Lambda upon triggered by an event. + + Parameters + ---------- + event : dic < str: dic > + an S3 event + context: + not used + """ for record in event['Records']: bucket = record['s3']['bucket']['name'] key = unquote_plus(record['s3']['object']['key']) diff --git a/examples/query.py b/examples/query.py index 6354cec3b..0f0ee13b4 100644 --- a/examples/query.py +++ b/examples/query.py @@ -1,15 +1,20 @@ +"""stores all the pre-defined query strings.""" from enum import Enum from examples.datapipeline_test import apply_energy_one -tags = {"energy": ["ENERGY_ONE"]} +# tags for different queries +tags = {"energy": ["POWER_DEMAND_MODEL"], "analysis": ["POWER_DEMAND_MODEL"]} -testing_functions = {"ENERGY_ONE": apply_energy_one} +# specify the function to calculate the expected result of each query +testing_functions = {"POWER_DEMAND_MODEL": apply_energy_one} class QueryStrings(Enum): + """An enumeration of all the pre-defined query strings.""" + SAMPLE = "SELECT * FROM trajectory_table WHERE partition_name=\'{partition}\' LIMIT 15;" UPDATE_PARTITION = "ALTER TABLE trajectory_table ADD IF NOT EXISTS PARTITION (partition_name=\'{partition}\');" - ENERGY_ONE = "SELECT id, time, 1200 * speed * " \ + POWER_DEMAND_MODEL = "SELECT id, time, 1200 * speed * " \ "((CASE WHEN acceleration > 0 THEN 1 ELSE 0 END * (1-0.8) * acceleration) + 0.8 " \ "+ 9.81 * SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 " \ "* POW(speed,3) AS power, 1 AS energy_model_id, source_id " \ diff --git a/examples/run_query.py b/examples/run_query.py index ea8839b09..64baa6656 100644 --- a/examples/run_query.py +++ b/examples/run_query.py @@ -1,3 +1,4 @@ +"""runner script for invoking query manually.""" import argparse from examples.data_pipeline import AthenaQuery, test_sql_query from examples.query import QueryStrings diff --git a/flow/core/kernel/vehicle/base.py b/flow/core/kernel/vehicle/base.py index 3c285697f..080162c7b 100644 --- a/flow/core/kernel/vehicle/base.py +++ b/flow/core/kernel/vehicle/base.py @@ -690,17 +690,21 @@ def get_max_speed(self, veh_id, error): ########################################################################### def get_accel(self, veh_id): - """ see traci class """ + """Return the acceleration of vehicle with veh_id.""" raise NotImplementedError def update_accel_without_noise(self, veh_id, accel_without_noise): - """ see traci class """ + """Update stored acceleration without noise of vehicle with veh_id.""" raise NotImplementedError def get_2D_position(self, veh_id, error=-1001): - """ see traci class """ + """Return (x, y) position of vehicle with veh_id.""" raise NotImplementedError def get_accel_without_noise(self, veh_id): - """ see traci class """ + """Return the acceleration without noise of vehicle with veh_id.""" + raise NotImplementedError + + def get_road_grade(self, veh_id): + """Return the road-grade of the vehicle with veh_id.""" raise NotImplementedError diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index 889528b36..b473a1fa7 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -1132,17 +1132,22 @@ def set_max_speed(self, veh_id, max_speed): # add for data pipeline def get_accel(self, veh_id): + """See parent class.""" return (self.get_speed(veh_id) - self.get_previous_speed(veh_id)) / self.sim_step def update_accel_without_noise(self, veh_id, accel_without_noise): + """See parent class.""" self.__vehicles[veh_id]["accel_without_noise"] = accel_without_noise def get_accel_without_noise(self, veh_id): + """See parent class.""" return self.__vehicles[veh_id]["accel_without_noise"] def get_2D_position(self, veh_id, error=-1001): + """See parent class.""" return self.__sumo_obs.get(veh_id, {}).get(tc.VAR_POSITION, error) def get_road_grade(self, veh_id): + """See parent class.""" # TODO return 0 From 5a3ff57fb2d70f2736a9f1ba091aa5730d7006d4 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Thu, 23 Apr 2020 12:38:47 -0700 Subject: [PATCH 40/89] reorganized file locations --- {examples => flow/data_pipeline}/data_pipeline.py | 0 {examples => flow/data_pipeline}/datapipeline_test.py | 0 {examples => flow/data_pipeline}/lambda_function.py | 0 {examples => flow/data_pipeline}/query.py | 0 {examples => flow/data_pipeline}/run_query.py | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename {examples => flow/data_pipeline}/data_pipeline.py (100%) rename {examples => flow/data_pipeline}/datapipeline_test.py (100%) rename {examples => flow/data_pipeline}/lambda_function.py (100%) rename {examples => flow/data_pipeline}/query.py (100%) rename {examples => flow/data_pipeline}/run_query.py (100%) diff --git a/examples/data_pipeline.py b/flow/data_pipeline/data_pipeline.py similarity index 100% rename from examples/data_pipeline.py rename to flow/data_pipeline/data_pipeline.py diff --git a/examples/datapipeline_test.py b/flow/data_pipeline/datapipeline_test.py similarity index 100% rename from examples/datapipeline_test.py rename to flow/data_pipeline/datapipeline_test.py diff --git a/examples/lambda_function.py b/flow/data_pipeline/lambda_function.py similarity index 100% rename from examples/lambda_function.py rename to flow/data_pipeline/lambda_function.py diff --git a/examples/query.py b/flow/data_pipeline/query.py similarity index 100% rename from examples/query.py rename to flow/data_pipeline/query.py diff --git a/examples/run_query.py b/flow/data_pipeline/run_query.py similarity index 100% rename from examples/run_query.py rename to flow/data_pipeline/run_query.py From ddc53fb03ae5474c6c2faf2627feb11a6bdac7da Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Thu, 23 Apr 2020 12:58:44 -0700 Subject: [PATCH 41/89] fix some more style issues --- examples/simulate.py | 3 ++- flow/controllers/base_controller.py | 10 +++++----- flow/core/experiment.py | 3 ++- flow/core/kernel/vehicle/base.py | 2 +- flow/core/kernel/vehicle/traci.py | 2 +- flow/data_pipeline/__init__.py | 1 + flow/data_pipeline/data_pipeline.py | 3 +-- flow/data_pipeline/datapipeline_test.py | 2 +- flow/data_pipeline/lambda_function.py | 2 +- flow/data_pipeline/query.py | 12 ++++++------ flow/data_pipeline/run_query.py | 8 ++++---- flow/envs/base.py | 1 - 12 files changed, 25 insertions(+), 24 deletions(-) create mode 100644 flow/data_pipeline/__init__.py diff --git a/examples/simulate.py b/examples/simulate.py index 69e11b2fb..86d14aa14 100644 --- a/examples/simulate.py +++ b/examples/simulate.py @@ -97,4 +97,5 @@ def parse_args(args): exp = Experiment(flow_params, callables) # Run for the specified number of rollouts. - exp.run(flags.num_runs, convert_to_csv=flags.gen_emission, partition_name=flags.to_aws, only_query=flags.only_query) + exp.run(flags.num_runs, convert_to_csv=flags.gen_emission, partition_name=flags.to_aws, + only_query=flags.only_query) diff --git a/flow/controllers/base_controller.py b/flow/controllers/base_controller.py index 6e6734764..7adcdf310 100755 --- a/flow/controllers/base_controller.py +++ b/flow/controllers/base_controller.py @@ -110,18 +110,18 @@ def get_action(self, env): # store the acceleration without noise to each vehicle # run fail safe if requested - accel_without_noice = accel + accel_without_noise = accel if self.fail_safe == 'instantaneous': - accel_without_noice = self.get_safe_action_instantaneous(env, accel_without_noice) + accel_without_noise = self.get_safe_action_instantaneous(env, accel_without_noise) elif self.fail_safe == 'safe_velocity': - accel_without_noice = self.get_safe_velocity_action(env, accel_without_noice) - env.k.vehicle.update_accel_without_noise(self.veh_id, accel_without_noice) + accel_without_noise = self.get_safe_velocity_action(env, accel_without_noise) + env.k.vehicle.update_accel_without_noise(self.veh_id, accel_without_noise) # add noise to the accelerations, if requested if self.accel_noise > 0: accel += np.sqrt(env.sim_step) * np.random.normal(0, self.accel_noise) - # run the failsafes, if requested + # run the fail-safes, if requested if self.fail_safe == 'instantaneous': accel = self.get_safe_action_instantaneous(env, accel) elif self.fail_safe == 'safe_velocity': diff --git a/flow/core/experiment.py b/flow/core/experiment.py index 80d607e7d..aa5028836 100755 --- a/flow/core/experiment.py +++ b/flow/core/experiment.py @@ -231,7 +231,8 @@ def rl_actions(*_): if partition_name: upload_to_s3('brent.experiments', 'trajectory-output/' + 'partition_name=' + partition_name + '/' - + upload_file_path.split('/')[-1].split('_')[0] + '.csv', upload_file_path, str(only_query)[2:-2]) + + upload_file_path.split('/')[-1].split('_')[0] + '.csv', + upload_file_path, str(only_query)[2:-2]) # delete the S3-only version of the trajectory file os.remove(upload_file_path) diff --git a/flow/core/kernel/vehicle/base.py b/flow/core/kernel/vehicle/base.py index 080162c7b..1b729d159 100644 --- a/flow/core/kernel/vehicle/base.py +++ b/flow/core/kernel/vehicle/base.py @@ -697,7 +697,7 @@ def update_accel_without_noise(self, veh_id, accel_without_noise): """Update stored acceleration without noise of vehicle with veh_id.""" raise NotImplementedError - def get_2D_position(self, veh_id, error=-1001): + def get_2d_position(self, veh_id, error=-1001): """Return (x, y) position of vehicle with veh_id.""" raise NotImplementedError diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index b473a1fa7..81d759988 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -1143,7 +1143,7 @@ def get_accel_without_noise(self, veh_id): """See parent class.""" return self.__vehicles[veh_id]["accel_without_noise"] - def get_2D_position(self, veh_id, error=-1001): + def get_2d_position(self, veh_id, error=-1001): """See parent class.""" return self.__sumo_obs.get(veh_id, {}).get(tc.VAR_POSITION, error) diff --git a/flow/data_pipeline/__init__.py b/flow/data_pipeline/__init__.py new file mode 100644 index 000000000..622e09b06 --- /dev/null +++ b/flow/data_pipeline/__init__.py @@ -0,0 +1 @@ +"""Empty init file to ensure that data_pipeline is recognized as a package""" diff --git a/flow/data_pipeline/data_pipeline.py b/flow/data_pipeline/data_pipeline.py index 03b0f87e5..afbc09f92 100644 --- a/flow/data_pipeline/data_pipeline.py +++ b/flow/data_pipeline/data_pipeline.py @@ -2,8 +2,7 @@ import pandas as pd import numpy as np import boto3 -from botocore.exceptions import ClientError -from examples.query import QueryStrings, testing_functions +from flow.data_pipeline.query import QueryStrings, testing_functions from time import time diff --git a/flow/data_pipeline/datapipeline_test.py b/flow/data_pipeline/datapipeline_test.py index ae0ea382f..0e1a50518 100644 --- a/flow/data_pipeline/datapipeline_test.py +++ b/flow/data_pipeline/datapipeline_test.py @@ -34,4 +34,4 @@ def calculate_power(mu, acceleration, M=M, g=g, theta=theta, C_r=C_r, ro_air=ro_ def apply_energy_one(row): """Apply the power calculation to a row of the dataframe.""" - return [row[0], row[1], calculate_power(row[4], row[6])] \ No newline at end of file + return [row[0], row[1], calculate_power(row[4], row[6])] diff --git a/flow/data_pipeline/lambda_function.py b/flow/data_pipeline/lambda_function.py index 4f7937c85..afef55a4b 100644 --- a/flow/data_pipeline/lambda_function.py +++ b/flow/data_pipeline/lambda_function.py @@ -33,4 +33,4 @@ def lambda_handler(event, context): else: query_list = run_query.split("\', \'") for query_name in query_list: - queryEngine.run_query(query_name, 's3://brent.experiments/query-result/auto/', partition) \ No newline at end of file + queryEngine.run_query(query_name, 's3://brent.experiments/query-result/auto/', partition) diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index 0f0ee13b4..af1b51ce7 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -1,6 +1,6 @@ """stores all the pre-defined query strings.""" from enum import Enum -from examples.datapipeline_test import apply_energy_one +from flow.data_pipeline.datapipeline_test import apply_energy_one # tags for different queries tags = {"energy": ["POWER_DEMAND_MODEL"], "analysis": ["POWER_DEMAND_MODEL"]} @@ -15,8 +15,8 @@ class QueryStrings(Enum): SAMPLE = "SELECT * FROM trajectory_table WHERE partition_name=\'{partition}\' LIMIT 15;" UPDATE_PARTITION = "ALTER TABLE trajectory_table ADD IF NOT EXISTS PARTITION (partition_name=\'{partition}\');" POWER_DEMAND_MODEL = "SELECT id, time, 1200 * speed * " \ - "((CASE WHEN acceleration > 0 THEN 1 ELSE 0 END * (1-0.8) * acceleration) + 0.8 " \ - "+ 9.81 * SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 " \ - "* POW(speed,3) AS power, 1 AS energy_model_id, source_id " \ - "FROM trajectory_table " \ - "WHERE partition_name=\'{partition}\'" \ No newline at end of file + "((CASE WHEN acceleration > 0 THEN 1 ELSE 0 END * (1-0.8) * acceleration) + 0.8 " \ + "+ 9.81 * SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 " \ + "* POW(speed,3) AS power, 1 AS energy_model_id, source_id " \ + "FROM trajectory_table " \ + "WHERE partition_name=\'{partition}\'" diff --git a/flow/data_pipeline/run_query.py b/flow/data_pipeline/run_query.py index 64baa6656..f065a726e 100644 --- a/flow/data_pipeline/run_query.py +++ b/flow/data_pipeline/run_query.py @@ -1,10 +1,10 @@ """runner script for invoking query manually.""" import argparse -from examples.data_pipeline import AthenaQuery, test_sql_query -from examples.query import QueryStrings +from flow.data_pipeline.data_pipeline import AthenaQuery, test_sql_query +from flow.data_pipeline.query import QueryStrings parser = argparse.ArgumentParser(prog="run_query", description="runs query on AWS Athena and stores the result to" - "a S3 location") + "a S3 location") parser.add_argument("--run", type=str, nargs="+") parser.add_argument("--result_location", type=str, nargs='?', default="s3://brent.experiments/query-result/") parser.add_argument("--partition", type=str, nargs='?', default="default") @@ -34,4 +34,4 @@ for q in QueryStrings: print(q) if args.test_query: - test_sql_query(args.test_query[0]) \ No newline at end of file + test_sql_query(args.test_query[0]) diff --git a/flow/envs/base.py b/flow/envs/base.py index f2067d947..cf1674355 100644 --- a/flow/envs/base.py +++ b/flow/envs/base.py @@ -417,7 +417,6 @@ def step(self, rl_actions): "**********************************************************" ) - # compute the info for each agent infos = {} From e7ac1a9afa6513f0cb425a2e37c3db26b259f6f0 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Thu, 23 Apr 2020 13:02:33 -0700 Subject: [PATCH 42/89] fix one more style issue --- flow/data_pipeline/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/data_pipeline/__init__.py b/flow/data_pipeline/__init__.py index 622e09b06..d9d6a6573 100644 --- a/flow/data_pipeline/__init__.py +++ b/flow/data_pipeline/__init__.py @@ -1 +1 @@ -"""Empty init file to ensure that data_pipeline is recognized as a package""" +"""Empty init file to ensure that data_pipeline is recognized as a package.""" From c97021992460a6d628ad769c289975f83bdf9628 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Sat, 9 May 2020 22:06:30 -0700 Subject: [PATCH 43/89] added new two new quries --- flow/core/experiment.py | 4 ++-- flow/core/kernel/vehicle/base.py | 4 ++++ flow/core/kernel/vehicle/traci.py | 4 ++++ flow/data_pipeline/query.py | 38 ++++++++++++++++++++++++++++++- 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/flow/core/experiment.py b/flow/core/experiment.py index aa5028836..37fcb03af 100755 --- a/flow/core/experiment.py +++ b/flow/core/experiment.py @@ -1,7 +1,7 @@ """Contains an experiment class for running simulations.""" from flow.core.util import emission_to_csv from flow.utils.registry import make_create_env -from examples.data_pipeline import generate_trajectory_from_flow, upload_to_s3 +from flow.data_pipeline.data_pipeline import generate_trajectory_from_flow, upload_to_s3 import datetime import logging import time @@ -178,7 +178,7 @@ def rl_actions(*_): self.env.k.vehicle.get_leader(vid)) - self.env.k.vehicle.get_speed(vid)) extra_info["accel_without_noise"].append(self.env.k.vehicle.get_accel_without_noise(vid)) extra_info["road_grade"].append(self.env.k.vehicle.get_road_grade(vid)) - position = self.env.k.vehicle.get_2D_position(vid) + position = self.env.k.vehicle.get_2d_position(vid) extra_info["x"].append(position[0]) extra_info["y"].append(position[1]) extra_info["speed"].append(self.env.k.vehicle.get_speed(vid)) diff --git a/flow/core/kernel/vehicle/base.py b/flow/core/kernel/vehicle/base.py index 1b729d159..7609cf252 100644 --- a/flow/core/kernel/vehicle/base.py +++ b/flow/core/kernel/vehicle/base.py @@ -705,6 +705,10 @@ def get_accel_without_noise(self, veh_id): """Return the acceleration without noise of vehicle with veh_id.""" raise NotImplementedError + def get_velocity_without_noise(self, veh_id): + """Return the velocity without noise of vehicle with veh_id.""" + raise NotImplementedError + def get_road_grade(self, veh_id): """Return the road-grade of the vehicle with veh_id.""" raise NotImplementedError diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index 81d759988..1f697f046 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -1143,6 +1143,10 @@ def get_accel_without_noise(self, veh_id): """See parent class.""" return self.__vehicles[veh_id]["accel_without_noise"] + def get_velocity_without_noise(self, veh_id): + """See parent class.""" + return max([self.get_speed(veh_id) + self.get_accel_without_noise(veh_id) * self.sim_step, 0]) + def get_2d_position(self, veh_id, error=-1001): """See parent class.""" return self.__sumo_obs.get(veh_id, {}).get(tc.VAR_POSITION, error) diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index af1b51ce7..0c87b3dcc 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -3,7 +3,8 @@ from flow.data_pipeline.datapipeline_test import apply_energy_one # tags for different queries -tags = {"energy": ["POWER_DEMAND_MODEL"], "analysis": ["POWER_DEMAND_MODEL"]} +tags = {"energy": ["POWER_DEMAND_MODEL", "POWER_DEMAND_MODEL_DENOISED_ACCEL", "POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL"], + "analysis": ["POWER_DEMAND_MODEL"]} # specify the function to calculate the expected result of each query testing_functions = {"POWER_DEMAND_MODEL": apply_energy_one} @@ -20,3 +21,38 @@ class QueryStrings(Enum): "* POW(speed,3) AS power, 1 AS energy_model_id, source_id " \ "FROM trajectory_table " \ "WHERE partition_name=\'{partition}\'" + POWER_DEMAND_MODEL_DENOISED_ACCEL = \ + "SELECT id, time, 1200 * speed * " \ + "((CASE WHEN accel_without_noise > 0 THEN 1 ELSE 0 END * (1-0.8) * accel_without_noise)+0.8 " \ + "+ 9.81 * SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 " \ + "* POW(speed,3) AS power, 1 AS energy_model_id, source_id " \ + "FROM trajectory_table " \ + "WHERE partition_name=\'{partition}\'" + POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL = \ + "WITH sub1 AS ( " \ + "SELECT" \ + "time, id, speed, acceleration, accel_without_noise, road_grade, source_id," \ + "time - LAG(time, 1) " \ + "OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS sim_step," \ + "LAG(speed, 1) " \ + "OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_speed," \ + "LAG(acceleration, 1) " \ + "OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_accel," \ + "LAG(accel_without_noise, 1) " \ + "OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_accel_denoised" \ + "FROM trajectory_table" \ + "WHERE partition_name=\'{partition}\'" \ + ")," \ + "sub2 AS (" \ + "SELECT time, id, speed, acceleration, accel_without_noise, " \ + "road_grade, source_id, " \ + "speed-prev_accel*sim_step+prev_accel_denoised*sim_step AS speed_denoised" \ + "FROM sub1" \ + ") " \ + "SELECT id, time, speed_denoised, accel_without_noise," \ + "1200 * speed_denoised * ((CASE WHEN accel_without_noise > 0 " \ + "THEN 1 ELSE 0 END * (1-0.8) * accel_without_noise) + 0.8 + 9.81 " \ + "* SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed_denoised + 0.5 * 1.225 " \ + "* 2.6 * 0.3 * POW(speed_denoised,3) AS power, " \ + "'POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL' AS energy_model, source_id" \ + "FROM sub2 " From 3b10524a6830986f3ec446907a9655a08c3f85dd Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Sun, 10 May 2020 23:03:35 -0700 Subject: [PATCH 44/89] including next_V for testing only --- flow/core/experiment.py | 1 + flow/core/kernel/vehicle/traci.py | 15 ++++++++++- flow/data_pipeline/query.py | 41 ++++++++++++++++--------------- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/flow/core/experiment.py b/flow/core/experiment.py index 37fcb03af..8b5cbac02 100755 --- a/flow/core/experiment.py +++ b/flow/core/experiment.py @@ -182,6 +182,7 @@ def rl_actions(*_): extra_info["x"].append(position[0]) extra_info["y"].append(position[1]) extra_info["speed"].append(self.env.k.vehicle.get_speed(vid)) + #extra_info["next_v"].append(self.env.k.vehicle.get_next_v(vid)) extra_info["source_id"].extend([source_id+"run" + str(i)] * len(veh_ids)) # Compute the results for the custom callables. diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index 1f697f046..13ca8efa6 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -967,8 +967,10 @@ def apply_acceleration(self, veh_ids, acc): for i, vid in enumerate(veh_ids): if acc[i] is not None and vid in self.get_ids(): + self.__vehicles[vid]["accel"] = acc[i] this_vel = self.get_speed(vid) next_vel = max([this_vel + acc[i] * self.sim_step, 0]) + #self.__vehicles[vid]["next_v"] = next_vel self.kernel_api.vehicle.slowDown(vid, next_vel, 1e-3) def apply_lane_change(self, veh_ids, direction): @@ -1131,9 +1133,18 @@ def set_max_speed(self, veh_id, max_speed): self.kernel_api.vehicle.setMaxSpeed(veh_id, max_speed) # add for data pipeline + def get_next_v(self, veh_id): + """See parent class.""" + if not "next_v" in self.__vehicles[veh_id]: + self.__vehicles[veh_id]["next_v"] = None + return self.__vehicles[veh_id]["next_v"] + #return (self.get_speed(veh_id) - self.get_previous_speed(veh_id)) / self.sim_step + def get_accel(self, veh_id): """See parent class.""" - return (self.get_speed(veh_id) - self.get_previous_speed(veh_id)) / self.sim_step + if not "accel" in self.__vehicles[veh_id]: + self.__vehicles[veh_id]["accel"] = None + return self.__vehicles[veh_id]["accel"] def update_accel_without_noise(self, veh_id, accel_without_noise): """See parent class.""" @@ -1141,6 +1152,8 @@ def update_accel_without_noise(self, veh_id, accel_without_noise): def get_accel_without_noise(self, veh_id): """See parent class.""" + if not "accel_without_noise" in self.__vehicles[veh_id]: + self.__vehicles[veh_id]["accel_without_noise"] = None return self.__vehicles[veh_id]["accel_without_noise"] def get_velocity_without_noise(self, veh_id): diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index 0c87b3dcc..9054364e6 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -15,44 +15,45 @@ class QueryStrings(Enum): SAMPLE = "SELECT * FROM trajectory_table WHERE partition_name=\'{partition}\' LIMIT 15;" UPDATE_PARTITION = "ALTER TABLE trajectory_table ADD IF NOT EXISTS PARTITION (partition_name=\'{partition}\');" - POWER_DEMAND_MODEL = "SELECT id, time, 1200 * speed * " \ + POWER_DEMAND_MODEL = "SELECT id, time, speed, acceleration, 1200 * speed * " \ "((CASE WHEN acceleration > 0 THEN 1 ELSE 0 END * (1-0.8) * acceleration) + 0.8 " \ "+ 9.81 * SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 " \ - "* POW(speed,3) AS power, 1 AS energy_model_id, source_id " \ + "* POW(speed,3) AS power, 'POWER_DEMAND_MODEL' AS energy_model_id, source_id " \ "FROM trajectory_table " \ - "WHERE partition_name=\'{partition}\'" + "WHERE partition_name=\'{partition}\' " \ + "ORDER BY id, time " POWER_DEMAND_MODEL_DENOISED_ACCEL = \ - "SELECT id, time, 1200 * speed * " \ + "SELECT id, time, speed, accel_without_noise, 1200 * speed * " \ "((CASE WHEN accel_without_noise > 0 THEN 1 ELSE 0 END * (1-0.8) * accel_without_noise)+0.8 " \ "+ 9.81 * SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 " \ - "* POW(speed,3) AS power, 1 AS energy_model_id, source_id " \ + "* POW(speed,3) AS power, 'POWER_DEMAND_MODEL_DENOISED_ACCEL' AS energy_model_id, source_id " \ "FROM trajectory_table " \ - "WHERE partition_name=\'{partition}\'" + "WHERE partition_name=\'{partition}\' " \ + "ORDER BY id, time " POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL = \ "WITH sub1 AS ( " \ - "SELECT" \ - "time, id, speed, acceleration, accel_without_noise, road_grade, source_id," \ + "SELECT time, id, speed, acceleration, accel_without_noise, road_grade, source_id, " \ "time - LAG(time, 1) " \ - "OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS sim_step," \ + "OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS sim_step, " \ "LAG(speed, 1) " \ - "OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_speed," \ + "OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_speed, " \ "LAG(acceleration, 1) " \ - "OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_accel," \ + "OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_accel, " \ "LAG(accel_without_noise, 1) " \ - "OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_accel_denoised" \ - "FROM trajectory_table" \ + "OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_accel_denoised "\ + "FROM trajectory_table " \ "WHERE partition_name=\'{partition}\'" \ ")," \ "sub2 AS (" \ - "SELECT time, id, speed, acceleration, accel_without_noise, " \ - "road_grade, source_id, " \ - "speed-prev_accel*sim_step+prev_accel_denoised*sim_step AS speed_denoised" \ + "SELECT time, id, speed, acceleration, accel_without_noise, road_grade, source_id, " \ + "prev_speed+accel_without_noise*sim_step AS speed_denoised " \ "FROM sub1" \ ") " \ - "SELECT id, time, speed_denoised, accel_without_noise," \ - "1200 * speed_denoised * ((CASE WHEN accel_without_noise > 0 " \ + "SELECT id, time, speed_denoised, accel_without_noise, " \ + "1200 * speed_denoised * ((CASE WHEN accel_without_noise > 0 " \ "THEN 1 ELSE 0 END * (1-0.8) * accel_without_noise) + 0.8 + 9.81 " \ "* SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed_denoised + 0.5 * 1.225 " \ "* 2.6 * 0.3 * POW(speed_denoised,3) AS power, " \ - "'POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL' AS energy_model, source_id" \ - "FROM sub2 " + "'POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL' AS energy_model, source_id " \ + "FROM sub2 " \ + "ORDER BY id, time " From 638f9b4ff1a7baec698264f2f2cdbb35d507b669 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Mon, 18 May 2020 12:25:00 -0700 Subject: [PATCH 45/89] change the bucket to a common bucket --- flow/core/experiment.py | 29 +++++++---------------- flow/core/kernel/vehicle/base.py | 4 ++-- flow/core/kernel/vehicle/traci.py | 13 +++------- flow/data_pipeline/data_pipeline.py | 34 ++++++++++++++++++++++++--- flow/data_pipeline/lambda_function.py | 4 ++-- flow/visualize/i210_replay.py | 14 ++++++++--- 6 files changed, 57 insertions(+), 41 deletions(-) diff --git a/flow/core/experiment.py b/flow/core/experiment.py index 8b5cbac02..2296ef635 100755 --- a/flow/core/experiment.py +++ b/flow/core/experiment.py @@ -1,10 +1,11 @@ """Contains an experiment class for running simulations.""" from flow.core.util import emission_to_csv from flow.utils.registry import make_create_env -from flow.data_pipeline.data_pipeline import generate_trajectory_from_flow, upload_to_s3 +from flow.data_pipeline.data_pipeline import generate_trajectory_from_flow, upload_to_s3, extra_init, get_extra_info import datetime import logging import time +from datetime import date import os import numpy as np import uuid @@ -145,9 +146,7 @@ def rl_actions(*_): # time profiling information t = time.time() times = [] - extra_info = {"time": [], "id": [], "x": [], "y": [], "speed": [], "headway": [], "acceleration": [], - "leader_id": [], "follower_id": [], "leader_rel_speed": [], "accel_without_noise": [], - "road_grade": [], "source_id": []} + extra_info = extra_init() source_id = uuid.uuid4().hex for i in range(num_runs): @@ -167,22 +166,7 @@ def rl_actions(*_): ret += reward # collect additional information for the data pipeline - for vid in veh_ids: - extra_info["time"].append(self.env.k.vehicle.get_timestep(vid) / 1000) - extra_info["id"].append(vid) - extra_info["headway"].append(self.env.k.vehicle.get_headway(vid)) - extra_info["acceleration"].append(self.env.k.vehicle.get_accel(vid)) - extra_info["leader_id"].append(self.env.k.vehicle.get_leader(vid)) - extra_info["follower_id"].append(self.env.k.vehicle.get_follower(vid)) - extra_info["leader_rel_speed"].append(self.env.k.vehicle.get_speed( - self.env.k.vehicle.get_leader(vid)) - self.env.k.vehicle.get_speed(vid)) - extra_info["accel_without_noise"].append(self.env.k.vehicle.get_accel_without_noise(vid)) - extra_info["road_grade"].append(self.env.k.vehicle.get_road_grade(vid)) - position = self.env.k.vehicle.get_2d_position(vid) - extra_info["x"].append(position[0]) - extra_info["y"].append(position[1]) - extra_info["speed"].append(self.env.k.vehicle.get_speed(vid)) - #extra_info["next_v"].append(self.env.k.vehicle.get_next_v(vid)) + get_extra_info(self.env.k.vehicle, extra_info, veh_ids) extra_info["source_id"].extend([source_id+"run" + str(i)] * len(veh_ids)) # Compute the results for the custom callables. @@ -231,7 +215,10 @@ def rl_actions(*_): upload_file_path = generate_trajectory_from_flow(trajectory_table_path, extra_info, partition_name) if partition_name: - upload_to_s3('brent.experiments', 'trajectory-output/' + 'partition_name=' + partition_name + '/' + if partition_name == "default": + partition_name = source_id[0:3] + partition_name = date.today().isoformat() + " " + partition_name + upload_to_s3('circles.data', 'trajectory-output/' + 'partition_name=' + partition_name + '/' + upload_file_path.split('/')[-1].split('_')[0] + '.csv', upload_file_path, str(only_query)[2:-2]) diff --git a/flow/core/kernel/vehicle/base.py b/flow/core/kernel/vehicle/base.py index 7609cf252..647ef37fe 100644 --- a/flow/core/kernel/vehicle/base.py +++ b/flow/core/kernel/vehicle/base.py @@ -705,8 +705,8 @@ def get_accel_without_noise(self, veh_id): """Return the acceleration without noise of vehicle with veh_id.""" raise NotImplementedError - def get_velocity_without_noise(self, veh_id): - """Return the velocity without noise of vehicle with veh_id.""" + def get_realized_accel(self, veh_id): + """Return the acceleration that the vehicle actually make.""" raise NotImplementedError def get_road_grade(self, veh_id): diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index 13ca8efa6..f40eed99c 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -1133,13 +1133,6 @@ def set_max_speed(self, veh_id, max_speed): self.kernel_api.vehicle.setMaxSpeed(veh_id, max_speed) # add for data pipeline - def get_next_v(self, veh_id): - """See parent class.""" - if not "next_v" in self.__vehicles[veh_id]: - self.__vehicles[veh_id]["next_v"] = None - return self.__vehicles[veh_id]["next_v"] - #return (self.get_speed(veh_id) - self.get_previous_speed(veh_id)) / self.sim_step - def get_accel(self, veh_id): """See parent class.""" if not "accel" in self.__vehicles[veh_id]: @@ -1156,9 +1149,9 @@ def get_accel_without_noise(self, veh_id): self.__vehicles[veh_id]["accel_without_noise"] = None return self.__vehicles[veh_id]["accel_without_noise"] - def get_velocity_without_noise(self, veh_id): + def get_realized_accel(self, veh_id): """See parent class.""" - return max([self.get_speed(veh_id) + self.get_accel_without_noise(veh_id) * self.sim_step, 0]) + return (self.get_speed(veh_id) - self.get_previous_speed(veh_id)) / self.sim_step def get_2d_position(self, veh_id, error=-1001): """See parent class.""" @@ -1166,5 +1159,5 @@ def get_2d_position(self, veh_id, error=-1001): def get_road_grade(self, veh_id): """See parent class.""" - # TODO + # TODO : Brent return 0 diff --git a/flow/data_pipeline/data_pipeline.py b/flow/data_pipeline/data_pipeline.py index afbc09f92..0cd0cbc79 100644 --- a/flow/data_pipeline/data_pipeline.py +++ b/flow/data_pipeline/data_pipeline.py @@ -88,6 +88,34 @@ def upload_to_s3(bucket_name, bucket_key, file_path, only_query): return +def extra_init(): + """Return the dictionary with all the feild pre-populated with empty list.""" + extra_info = {"time": [], "id": [], "x": [], "y": [], "speed": [], "headway": [], "acceleration": [], + "accel_without_noise": [], "realilzed_accel": [], "leader_id": [], "follower_id": [], + "leader_rel_speed": [], "road_grade": [], "source_id": []} + return extra_info + + +def get_extra_info(veh_kernel, extra_info, veh_ids): + """Get all the necessary information for the trajectory output from flow.""" + for vid in veh_ids: + extra_info["time"].append(veh_kernel.get_timestep(vid) / 1000) + extra_info["id"].append(vid) + extra_info["headway"].append(veh_kernel.get_headway(vid)) + extra_info["acceleration"].append(veh_kernel.get_accel(vid)) + extra_info["leader_id"].append(veh_kernel.get_leader(vid)) + extra_info["follower_id"].append(veh_kernel.get_follower(vid)) + extra_info["leader_rel_speed"].append(veh_kernel.get_speed( + veh_kernel.get_leader(vid)) - veh_kernel.get_speed(vid)) + extra_info["accel_without_noise"].append(veh_kernel.get_accel_without_noise(vid)) + extra_info["realilzed_accel"].append(veh_kernel.get_realized_accel(vid)) + extra_info["road_grade"].append(veh_kernel.get_road_grade(vid)) + position = veh_kernel.get_2d_position(vid) + extra_info["x"].append(position[0]) + extra_info["y"].append(position[1]) + extra_info["speed"].append(veh_kernel.get_speed(vid)) + + class AthenaQuery: """ Class used to run query. @@ -199,7 +227,7 @@ def update_partition(self, partition): self.existing_partitions.append(partition) return - def run_query(self, query_name, result_location="s3://brent.experiments/query-result/", partition="default"): + def run_query(self, query_name, result_location="s3://circles.data/query-result/", partition="default"): """Start the execution of a query, does not wait for it to finish. Parameters @@ -257,14 +285,14 @@ def test_sql_query(query_name): # Run the respective sql query queryEngine = AthenaQuery() - execution_id = queryEngine.run_query(query_name, result_location="s3://brent.experiments/query-result/query-test", + execution_id = queryEngine.run_query(query_name, result_location="s3://circles.data/query-result/query-test", partition="test") if queryEngine.wait_for_execution(execution_id): raise RuntimeError("execution timed out") # get the Athena query result from S3 s3 = boto3.resource("s3") - s3.Bucket("brent.experiments").download_file("query-result/query-test/"+execution_id+".csv", + s3.Bucket("circles.data").download_file("query-result/query-test/"+execution_id+".csv", "data/athena_result.csv") athena_result = pd.read_csv("data/athena_result.csv") athena_result = athena_result.sort_values(by=["time", "id"]) diff --git a/flow/data_pipeline/lambda_function.py b/flow/data_pipeline/lambda_function.py index afef55a4b..3f0abb8a1 100644 --- a/flow/data_pipeline/lambda_function.py +++ b/flow/data_pipeline/lambda_function.py @@ -25,7 +25,7 @@ def lambda_handler(event, context): response = s3.head_object(Bucket=bucket, Key=key) run_query = response["Metadata"]["run-query"] - if bucket == 'brent.experiments' and 'trajectory-output/' in key: + if bucket == 'circles.data' and 'trajectory-output/' in key: if run_query == "all": query_list = tags["analysis"] elif not run_query: @@ -33,4 +33,4 @@ def lambda_handler(event, context): else: query_list = run_query.split("\', \'") for query_name in query_list: - queryEngine.run_query(query_name, 's3://brent.experiments/query-result/auto/', partition) + queryEngine.run_query(query_name, 's3://circles.data/query-result/auto/', partition) diff --git a/flow/visualize/i210_replay.py b/flow/visualize/i210_replay.py index a37bac95b..c50f12a05 100644 --- a/flow/visualize/i210_replay.py +++ b/flow/visualize/i210_replay.py @@ -32,6 +32,9 @@ from examples.exp_configs.rl.multiagent.multiagent_i210 import flow_params as I210_MA_DEFAULT_FLOW_PARAMS from examples.exp_configs.rl.multiagent.multiagent_i210 import custom_callables +from flow.data_pipeline.data_pipeline import generate_trajectory_from_flow, upload_to_s3, extra_init, get_extra_info +import uuid + EXAMPLE_USAGE = """ example usage: python i210_replay.py -r /ray_results/experiment_dir/result_dir -c 1 @@ -205,9 +208,10 @@ def replay(args, flow_params, output_dir=None, transfer_test=None, rllib_config= key: [] for key in custom_callables.keys() }) - i = 0 - while i < args.num_rollouts: - print("Rollout iter", i) + extra_info = extra_init() + source_id = uuid.uuid4().hex + + for i in range(args.num_rollouts): vel = [] per_vehicle_energy_trace = defaultdict(lambda: []) completed_veh_types = {} @@ -243,6 +247,10 @@ def replay(args, flow_params, output_dir=None, transfer_test=None, rllib_config= veh_ids = env.k.vehicle.get_ids() vel.append(np.mean(env.k.vehicle.get_speed(veh_ids))) + # Collect information from flow for the trajectory output + get_extra_info(env.k.vehicle, extra_info, veh_ids) + extra_info["source_id"].extend([source_id + "run" + str(i)] * len(veh_ids)) + # Compute the results for the custom callables. for (key, lambda_func) in custom_callables.items(): custom_vals[key].append(lambda_func(env)) From bc8584a30d3736169d9c0f985ddc677d34144dfd Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Mon, 18 May 2020 12:28:17 -0700 Subject: [PATCH 46/89] removed the old tests --- flow/data_pipeline/datapipeline_test.py | 37 ------------------------- 1 file changed, 37 deletions(-) delete mode 100644 flow/data_pipeline/datapipeline_test.py diff --git a/flow/data_pipeline/datapipeline_test.py b/flow/data_pipeline/datapipeline_test.py deleted file mode 100644 index 0e1a50518..000000000 --- a/flow/data_pipeline/datapipeline_test.py +++ /dev/null @@ -1,37 +0,0 @@ -"""functions that calculates the expected result for testing.""" -import math - -# Vehicle Mass -M = 1200 -# Gravity -g = 9.81 -# Density of Air -ro_air = 1.225 -# Rolling resistance coefficient -C_r = .005 -# Aerodynamic drag coefficient -C_a = 0.3 -# Vehicle Cross sectional Area -A = 2.6 -# Road grade -theta = 0 - - -def heavyside(inp): - """Return 1 if input is positive.""" - return 0 if inp <= 0 else 1 - - -def calculate_power(mu, acceleration, M=M, g=g, theta=theta, C_r=C_r, ro_air=ro_air, A=A, C_a=C_a): - """Calculate the expected power for POWER_DEMAND_MODEL query.""" - acceleration = (0.8 + ((1 - 0.8) * heavyside(acceleration)) * acceleration) - accel_and_slope = M * mu * (acceleration + g * math.sin(theta)) - rolling_friction = M * g * C_r * mu - air_drag = .5 * ro_air * A * C_a * mu**3 - power = accel_and_slope + rolling_friction + air_drag - return power - - -def apply_energy_one(row): - """Apply the power calculation to a row of the dataframe.""" - return [row[0], row[1], calculate_power(row[4], row[6])] From 0ee66469dcb5f21d542a57b464b3ad5fe7b11008 Mon Sep 17 00:00:00 2001 From: Eugene Vinitsky Date: Wed, 18 Mar 2020 16:43:22 -0700 Subject: [PATCH 47/89] Add an on ramp option --- examples/exp_configs/non_rl/i210_subnetwork.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/exp_configs/non_rl/i210_subnetwork.py b/examples/exp_configs/non_rl/i210_subnetwork.py index 3704a7a1c..8970e6165 100644 --- a/examples/exp_configs/non_rl/i210_subnetwork.py +++ b/examples/exp_configs/non_rl/i210_subnetwork.py @@ -117,7 +117,7 @@ sim=SumoParams( sim_step=0.4, render=False, - color_by_speed=True, + color_by_speed=False, use_ballistic=True ), From 3af559503e36d69c4f1481ee405778aab01c6840 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Mon, 6 Apr 2020 15:28:57 -0700 Subject: [PATCH 48/89] datapip pipeline implemented --- examples/data_pipeline.py | 179 ++++++++++++++++++++++++++++++++++++++ examples/query.py | 8 ++ examples/run_query.py | 34 ++++++++ 3 files changed, 221 insertions(+) create mode 100644 examples/data_pipeline.py create mode 100644 examples/query.py create mode 100644 examples/run_query.py diff --git a/examples/data_pipeline.py b/examples/data_pipeline.py new file mode 100644 index 000000000..5fdc30cf2 --- /dev/null +++ b/examples/data_pipeline.py @@ -0,0 +1,179 @@ +import pandas as pd +import boto3 +from botocore.exceptions import ClientError +from examples.query import QueryStrings +from time import time + + +def generate_trajectory_table(data_path, extra_info, partition_name): + """ generate desired output for the trajectory_table based on standard SUMO emission + + Parameters + ---------- + data_path : str + path to the standard SUMO emission + extra_info: dict + extra information needed in the trajectory table, collected from flow + partition_name: str + the name of the partition to put this output to + Returns + ------- + output_file_path: str + the local path of the outputted csv file + """ + raw_output = pd.read_csv(data_path, index_col=["time", "id"]) + required_cols = {"time", "id", "speed", "x", "y"} + raw_output = raw_output.drop(set(raw_output.columns) - required_cols, axis=1) + + extra_info = pd.DataFrame.from_dict(extra_info) + extra_info.set_index(["time", "id"]) + raw_output = raw_output.merge(extra_info, how="left", left_on=["time", "id"], right_on=["time", "id"]) + + # add the partition column + raw_output['partition'] = partition_name + + output_file_path = data_path[:-4]+"_trajectory.csv" + raw_output.to_csv(output_file_path, index=False) + return output_file_path + + +def upload_to_s3(bucket_name, bucket_key, file_path): + """ upload a file to S3 bucket + + Parameters + ---------- + bucket_name : str + the bucket to upload to + bucket_key: str + the key within the bucket for the file + file_path: str + the path of the file to be uploaded + """ + s3 = boto3.resource("s3") + s3.Bucket(bucket_name).upload_file(file_path, bucket_key) + return + + +class AthenaQuery: + + def __init__(self): + self.MAX_WAIT = 60 + self.client = boto3.client("athena") + self.existing_partitions = self.get_existing_partitions() + + def get_existing_partitions(self): + """prints the existing partitions in the S3 bucket""" + + response = self.client.start_query_execution( + QueryString='SHOW PARTITIONS trajectory_table', + QueryExecutionContext={ + 'Database': 'simulation' + }, + WorkGroup='primary' + ) + if self.wait_for_execution(response['QueryExecutionId']): + raise RuntimeError("get current partitions timed out") + response = self.client.get_query_results( + QueryExecutionId=response['QueryExecutionId'], + MaxResults=1000 + ) + return [data['Data'][0]['VarCharValue'].split('=')[-1] for data in response['ResultSet']['Rows']] + + def check_status(self, execution_id): + """ Return the status of the execution with given id + + Parameters + ---------- + execution_id : string + id of the execution that is checked for + Returns + ------- + status: str + QUEUED|RUNNING|SUCCEEDED|FAILED|CANCELLED + """ + + response = self.client.get_query_execution( + QueryExecutionId=execution_id + ) + return response['QueryExecution']['Status']['State'] + + def wait_for_execution(self, execution_id): + """ wait for the execution to finish or time-out + + Parameters + ---------- + execution_id : str + id of the execution this is watiing for + Returns + ------- + time_out: bool + True if time-out, False if success + Raises + ------ + RuntimeError: if execution failed or get canceled + """ + start = time() + while time() - start < self.MAX_WAIT: + state = self.check_status(execution_id) + if state == 'FAILED' or state == 'CANCELLED': + raise RuntimeError("update partition failed") + elif state == 'SUCCEEDED': + return False + return True + + def update_partition(self, partition): + """ load the given partition to the trajectory_table on Athena + + Parameters + ---------- + partition : str + the new partition that needs to be loaded + """ + response = self.client.start_query_execution( + QueryString=QueryStrings['UPDATE_PARTITION'].value.format(partition=partition), + QueryExecutionContext={ + 'Database': 'simulation' + }, + WorkGroup='primary' + ) + if self.wait_for_execution(response['QueryExecutionId']): + raise RuntimeError("update partition timed out") + self.existing_partitions.append(partition) + return + + def run_query(self, query_name, result_location="s3://brent.experiments/query-result/", partition="default"): + """ start the execution of a query, does not wait for it to finish + + Parameters + ---------- + query_name : str + name of the query in QueryStrings enum that will be run + result_location: str, optional + location on the S3 bucket where the result will be stored + partition: str, optional + name of the partition to run this query on + Returns + ------- + execution_id: str + the execution id of the execution started by this method + Raises + ------ + ValueError: if tries to run a query not existed in QueryStrings enum + """ + if query_name not in QueryStrings.__members__: + raise ValueError("query not existed: please add it to query.py") + + if partition not in self.existing_partitions: + self.update_partition(partition) + + response = self.client.start_query_execution( + QueryString=QueryStrings[query_name].value.format(partition=partition), + QueryExecutionContext={ + 'Database': 'simulation' + }, + ResultConfiguration={ + 'OutputLocation': result_location, + }, + WorkGroup='primary' + ) + return response['QueryExecutionId'] \ No newline at end of file diff --git a/examples/query.py b/examples/query.py new file mode 100644 index 000000000..3fbbe69e1 --- /dev/null +++ b/examples/query.py @@ -0,0 +1,8 @@ +from enum import Enum + +tags = {} + + +class QueryStrings(Enum): + SAMPLE = "SELECT * FROM trajectory_table WHERE partition_name=\'{partition}\' LIMIT 15;" + UPDATE_PARTITION = "ALTER TABLE trajectory_table ADD IF NOT EXISTS PARTITION (partition_name=\'{partition}\');" \ No newline at end of file diff --git a/examples/run_query.py b/examples/run_query.py new file mode 100644 index 000000000..7b4a5af7d --- /dev/null +++ b/examples/run_query.py @@ -0,0 +1,34 @@ +import argparse +import sys +from examples.data_pipeline import AthenaQuery +from examples.query import QueryStrings + +parser = argparse.ArgumentParser(prog="run_query", description="runs query on AWS Athena and stores the result to" + "a S3 location") +parser.add_argument("--run", type=str, nargs="+") +parser.add_argument("--result_location", type=str, nargs='?', default="s3://brent.experiments/query-result/") +parser.add_argument("--partition", type=str, nargs='?', default="default") +parser.add_argument("--list_partitions", action="store_true") +parser.add_argument("--check_status", type=str, nargs='+') +parser.add_argument("--list_queries", action="store_true") + + +if __name__ == "__main__": + args = parser.parse_args() + queryEngine = AthenaQuery() + + if args.run: + execution_ids = [] + for query_name in args.run: + execution_ids.append(queryEngine.run_query(query_name, args.result_location, args.partition)) + print(execution_ids) + if args.list_partitions: + print(queryEngine.existing_partitions) + if args.check_status: + status = dict() + for execution_id in args.check_status: + status[execution_id] = queryEngine.check_status(execution_id) + print(status) + if args.list_queries: + for q in QueryStrings: + print(q) From 8d4ad2904bb76afeb6c03cd8d90d8ea1e038df15 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Fri, 10 Apr 2020 19:54:30 -0700 Subject: [PATCH 49/89] multiple runs issue solved, testing added --- examples/data_pipeline.py | 55 +++++++- examples/datapipeline_test.py | 33 +++++ examples/query.py | 13 +- examples/run_query.py | 6 +- flow/core/experiment.py | 229 +------------------------------ flow/core/kernel/vehicle/base.py | 4 + 6 files changed, 104 insertions(+), 236 deletions(-) create mode 100644 examples/datapipeline_test.py diff --git a/examples/data_pipeline.py b/examples/data_pipeline.py index 5fdc30cf2..9d56548c2 100644 --- a/examples/data_pipeline.py +++ b/examples/data_pipeline.py @@ -1,7 +1,8 @@ import pandas as pd +import numpy as np import boto3 from botocore.exceptions import ClientError -from examples.query import QueryStrings +from examples.query import QueryStrings, testing_functions from time import time @@ -30,13 +31,22 @@ def generate_trajectory_table(data_path, extra_info, partition_name): raw_output = raw_output.merge(extra_info, how="left", left_on=["time", "id"], right_on=["time", "id"]) # add the partition column - raw_output['partition'] = partition_name - + # raw_output['partition'] = partition_name + raw_output = raw_output.sort_values(by=["time", "id"]) output_file_path = data_path[:-4]+"_trajectory.csv" raw_output.to_csv(output_file_path, index=False) return output_file_path +def generate_trajectory_from_flow(data_path, extra_info, partition_name): + extra_info = pd.DataFrame.from_dict(extra_info) + # extra_info["partition"] = partition_name + extra_info.to_csv(data_path, index=False) + upload_only_file_path = data_path[:-4] + "_upload" + ".csv" + extra_info.to_csv(upload_only_file_path, index=False, header=False) + return upload_only_file_path + + def upload_to_s3(bucket_name, bucket_key, file_path): """ upload a file to S3 bucket @@ -176,4 +186,41 @@ def run_query(self, query_name, result_location="s3://brent.experiments/query-re }, WorkGroup='primary' ) - return response['QueryExecutionId'] \ No newline at end of file + return response['QueryExecutionId'] + +########################################################################### +# Helpers for testing the SQL Queries # +########################################################################### + + +def test_sql_query(query_name): + if query_name not in testing_functions: + raise ValueError("no tests supported for this query") + + # Run the respective sql query + queryEngine = AthenaQuery() + execution_id = queryEngine.run_query(query_name, result_location="s3://brent.experiments/query-result/query-test", + partition="test") + if queryEngine.wait_for_execution(execution_id): + raise RuntimeError("execution timed out") + + # get the Athena query result from S3 + s3 = boto3.resource("s3") + s3.Bucket("brent.experiments").download_file("query-result/query-test/"+execution_id+".csv", + "data/athena_result.csv") + athena_result = pd.read_csv("data/athena_result.csv") + athena_result = athena_result.sort_values(by=["time", "id"]) + + # get the python expected result + expected_result = pd.read_csv("data/test_data.csv") + expected_result = expected_result.apply(testing_functions[query_name], axis=1, result_type="expand") + expected_result.columns = ["time", "id", "power"] + expected_result = expected_result.sort_values(by=["time", "id"]) + + difference = athena_result["power"] - expected_result["power"] + print("average difference is: " + str(np.mean(difference))) + print("std of difference is: " + str(np.std(difference))) + print("average ratio of difference to expected is: " + + str(np.mean(np.divide(difference, expected_result["power"])))) + difference = pd.DataFrame(difference) + difference.to_csv("./difference.csv") diff --git a/examples/datapipeline_test.py b/examples/datapipeline_test.py new file mode 100644 index 000000000..564060d3b --- /dev/null +++ b/examples/datapipeline_test.py @@ -0,0 +1,33 @@ +import math + +# Vehicle Mass +M = 1200 +# Gravity +g = 9.81 +# Density of Air +ro_air = 1.225 +# Rolling resistance coefficient +C_r = .005 +# Aerodynamic drag coefficient +C_a = 0.3 +# Vehicle Cross sectional Area +A = 2.6 +# Road grade +theta = 0 + + +def heavyside(inp): + return 0 if inp <= 0 else 1 + + +def calculate_power(mu, acceleration, M=M, g=g, theta=theta, C_r=C_r, ro_air=ro_air, A=A, C_a=C_a): + acceleration = (0.8 + ((1 - 0.8) * heavyside(acceleration)) * acceleration) + accel_and_slope = M * mu * (acceleration + g * math.sin(theta)) + rolling_friction = M * g * C_r * mu + air_drag = .5 * ro_air * A * C_a * mu**3 + power = accel_and_slope + rolling_friction + air_drag + return power + + +def apply_energy_one(row): + return [row[0], row[1], calculate_power(row[4], row[6])] \ No newline at end of file diff --git a/examples/query.py b/examples/query.py index 3fbbe69e1..6354cec3b 100644 --- a/examples/query.py +++ b/examples/query.py @@ -1,8 +1,17 @@ from enum import Enum +from examples.datapipeline_test import apply_energy_one -tags = {} +tags = {"energy": ["ENERGY_ONE"]} + +testing_functions = {"ENERGY_ONE": apply_energy_one} class QueryStrings(Enum): SAMPLE = "SELECT * FROM trajectory_table WHERE partition_name=\'{partition}\' LIMIT 15;" - UPDATE_PARTITION = "ALTER TABLE trajectory_table ADD IF NOT EXISTS PARTITION (partition_name=\'{partition}\');" \ No newline at end of file + UPDATE_PARTITION = "ALTER TABLE trajectory_table ADD IF NOT EXISTS PARTITION (partition_name=\'{partition}\');" + ENERGY_ONE = "SELECT id, time, 1200 * speed * " \ + "((CASE WHEN acceleration > 0 THEN 1 ELSE 0 END * (1-0.8) * acceleration) + 0.8 " \ + "+ 9.81 * SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 " \ + "* POW(speed,3) AS power, 1 AS energy_model_id, source_id " \ + "FROM trajectory_table " \ + "WHERE partition_name=\'{partition}\'" \ No newline at end of file diff --git a/examples/run_query.py b/examples/run_query.py index 7b4a5af7d..ea8839b09 100644 --- a/examples/run_query.py +++ b/examples/run_query.py @@ -1,6 +1,5 @@ import argparse -import sys -from examples.data_pipeline import AthenaQuery +from examples.data_pipeline import AthenaQuery, test_sql_query from examples.query import QueryStrings parser = argparse.ArgumentParser(prog="run_query", description="runs query on AWS Athena and stores the result to" @@ -11,6 +10,7 @@ parser.add_argument("--list_partitions", action="store_true") parser.add_argument("--check_status", type=str, nargs='+') parser.add_argument("--list_queries", action="store_true") +parser.add_argument("--test_query", nargs=1) if __name__ == "__main__": @@ -32,3 +32,5 @@ if args.list_queries: for q in QueryStrings: print(q) + if args.test_query: + test_sql_query(args.test_query[0]) \ No newline at end of file diff --git a/flow/core/experiment.py b/flow/core/experiment.py index 2296ef635..97467adb5 100755 --- a/flow/core/experiment.py +++ b/flow/core/experiment.py @@ -1,228 +1 @@ -"""Contains an experiment class for running simulations.""" -from flow.core.util import emission_to_csv -from flow.utils.registry import make_create_env -from flow.data_pipeline.data_pipeline import generate_trajectory_from_flow, upload_to_s3, extra_init, get_extra_info -import datetime -import logging -import time -from datetime import date -import os -import numpy as np -import uuid - - -class Experiment: - """ - Class for systematically running simulations in any supported simulator. - - This class acts as a runner for a network and environment. In order to use - it to run an network and environment in the absence of a method specifying - the actions of RL agents in the network, type the following: - - >>> from flow.envs import Env - >>> flow_params = dict(...) # see the examples in exp_config - >>> exp = Experiment(flow_params) # for some experiment configuration - >>> exp.run(num_runs=1) - - If you wish to specify the actions of RL agents in the network, this may be - done as follows: - - >>> rl_actions = lambda state: 0 # replace with something appropriate - >>> exp.run(num_runs=1, rl_actions=rl_actions) - - Finally, if you would like to like to plot and visualize your results, this - class can generate csv files from emission files produced by sumo. These - files will contain the speeds, positions, edges, etc... of every vehicle - in the network at every time step. - - In order to ensure that the simulator constructs an emission file, set the - ``emission_path`` attribute in ``SimParams`` to some path. - - >>> from flow.core.params import SimParams - >>> flow_params['sim'] = SimParams(emission_path="./data") - - Once you have included this in your environment, run your Experiment object - as follows: - - >>> exp.run(num_runs=1, convert_to_csv=True) - - After the experiment is complete, look at the "./data" directory. There - will be two files, one with the suffix .xml and another with the suffix - .csv. The latter should be easily interpretable from any csv reader (e.g. - Excel), and can be parsed using tools such as numpy and pandas. - - Attributes - ---------- - custom_callables : dict < str, lambda > - strings and lambda functions corresponding to some information we want - to extract from the environment. The lambda will be called at each step - to extract information from the env and it will be stored in a dict - keyed by the str. - env : flow.envs.Env - the environment object the simulator will run - """ - - def __init__(self, flow_params, custom_callables=None): - """Instantiate the Experiment class. - - Parameters - ---------- - flow_params : dict - flow-specific parameters - custom_callables : dict < str, lambda > - strings and lambda functions corresponding to some information we - want to extract from the environment. The lambda will be called at - each step to extract information from the env and it will be stored - in a dict keyed by the str. - """ - self.custom_callables = custom_callables or {} - - # Get the env name and a creator for the environment. - create_env, _ = make_create_env(flow_params) - - # Create the environment. - self.env = create_env() - - logging.info(" Starting experiment {} at {}".format( - self.env.network.name, str(datetime.datetime.utcnow()))) - - logging.info("Initializing environment.") - - def run(self, num_runs, rl_actions=None, convert_to_csv=False, partition_name=None, only_query=None): - """Run the given network for a set number of runs. - - Parameters - ---------- - num_runs : int - number of runs the experiment should perform - rl_actions : method, optional - maps states to actions to be performed by the RL agents (if - there are any) - convert_to_csv : bool - Specifies whether to convert the emission file created by sumo - into a csv file - partition_name: str - Specifies the S3 partition you want to store the output file, - will be used to later for query. If NONE, won't upload output - to S3. - only_query: str - Specifies whether queries should be automatically run the - simulation data when it gets uploaded to s3 - - Returns - ------- - info_dict : dict < str, Any > - contains returns, average speed per step - """ - num_steps = self.env.env_params.horizon - - # raise an error if convert_to_csv is set to True but no emission - # file will be generated, to avoid getting an error at the end of the - # simulation - if convert_to_csv and self.env.sim_params.emission_path is None: - raise ValueError( - 'The experiment was run with convert_to_csv set ' - 'to True, but no emission file will be generated. If you wish ' - 'to generate an emission file, you should set the parameter ' - 'emission_path in the simulation parameters (SumoParams or ' - 'AimsunParams) to the path of the folder where emissions ' - 'output should be generated. If you do not wish to generate ' - 'emissions, set the convert_to_csv parameter to False.') - - # used to store - info_dict = { - "returns": [], - "velocities": [], - "outflows": [], - } - info_dict.update({ - key: [] for key in self.custom_callables.keys() - }) - - if rl_actions is None: - def rl_actions(*_): - return None - - # time profiling information - t = time.time() - times = [] - extra_info = extra_init() - source_id = uuid.uuid4().hex - - for i in range(num_runs): - ret = 0 - vel = [] - custom_vals = {key: [] for key in self.custom_callables.keys()} - state = self.env.reset() - for j in range(num_steps): - t0 = time.time() - state, reward, done, _ = self.env.step(rl_actions(state)) - t1 = time.time() - times.append(1 / (t1 - t0)) - - # Compute the velocity speeds and cumulative returns. - veh_ids = self.env.k.vehicle.get_ids() - vel.append(np.mean(self.env.k.vehicle.get_speed(veh_ids))) - ret += reward - - # collect additional information for the data pipeline - get_extra_info(self.env.k.vehicle, extra_info, veh_ids) - extra_info["source_id"].extend([source_id+"run" + str(i)] * len(veh_ids)) - - # Compute the results for the custom callables. - for (key, lambda_func) in self.custom_callables.items(): - custom_vals[key].append(lambda_func(self.env)) - - if done: - break - - # Store the information from the run in info_dict. - outflow = self.env.k.vehicle.get_outflow_rate(int(500)) - info_dict["returns"].append(ret) - info_dict["velocities"].append(np.mean(vel)) - info_dict["outflows"].append(outflow) - for key in custom_vals.keys(): - info_dict[key].append(np.mean(custom_vals[key])) - - print("Round {0}, return: {1}".format(i, ret)) - - # Print the averages/std for all variables in the info_dict. - for key in info_dict.keys(): - print("Average, std {}: {}, {}".format( - key, np.mean(info_dict[key]), np.std(info_dict[key]))) - - print("Total time:", time.time() - t) - print("steps/second:", np.mean(times)) - self.env.terminate() - - if convert_to_csv and self.env.simulator == "traci": - # wait a short period of time to ensure the xml file is readable - time.sleep(0.1) - - # collect the location of the emission file - dir_path = self.env.sim_params.emission_path - emission_filename = \ - "{0}-emission.xml".format(self.env.network.name) - emission_path = os.path.join(dir_path, emission_filename) - - # convert the emission file into a csv - emission_to_csv(emission_path) - - # Delete the .xml version of the emission file. - os.remove(emission_path) - - trajectory_table_path = './data/' + source_id + ".csv" - upload_file_path = generate_trajectory_from_flow(trajectory_table_path, extra_info, partition_name) - - if partition_name: - if partition_name == "default": - partition_name = source_id[0:3] - partition_name = date.today().isoformat() + " " + partition_name - upload_to_s3('circles.data', 'trajectory-output/' + 'partition_name=' + partition_name + '/' - + upload_file_path.split('/')[-1].split('_')[0] + '.csv', - upload_file_path, str(only_query)[2:-2]) - - # delete the S3-only version of the trajectory file - os.remove(upload_file_path) - - return info_dict +"""Contains an experiment class for running simulations.""" from flow.core.util import emission_to_csv from flow.utils.registry import make_create_env from flow.data_pipeline.data_pipeline import generate_trajectory_from_flow, upload_to_s3, extra_init, get_extra_info import datetime import logging import time from datetime import date import os import numpy as np import uuid class Experiment: """ Class for systematically running simulations in any supported simulator. This class acts as a runner for a network and environment. In order to use it to run an network and environment in the absence of a method specifying the actions of RL agents in the network, type the following: >>> from flow.envs import Env >>> flow_params = dict(...) # see the examples in exp_config >>> exp = Experiment(flow_params) # for some experiment configuration >>> exp.run(num_runs=1) If you wish to specify the actions of RL agents in the network, this may be done as follows: >>> rl_actions = lambda state: 0 # replace with something appropriate >>> exp.run(num_runs=1, rl_actions=rl_actions) Finally, if you would like to like to plot and visualize your results, this class can generate csv files from emission files produced by sumo. These files will contain the speeds, positions, edges, etc... of every vehicle in the network at every time step. In order to ensure that the simulator constructs an emission file, set the ``emission_path`` attribute in ``SimParams`` to some path. >>> from flow.core.params import SimParams >>> flow_params['sim'] = SimParams(emission_path="./data") Once you have included this in your environment, run your Experiment object as follows: >>> exp.run(num_runs=1, convert_to_csv=True) After the experiment is complete, look at the "./data" directory. There will be two files, one with the suffix .xml and another with the suffix .csv. The latter should be easily interpretable from any csv reader (e.g. Excel), and can be parsed using tools such as numpy and pandas. Attributes ---------- custom_callables : dict < str, lambda > strings and lambda functions corresponding to some information we want to extract from the environment. The lambda will be called at each step to extract information from the env and it will be stored in a dict keyed by the str. env : flow.envs.Env the environment object the simulator will run """ def __init__(self, flow_params, custom_callables=None): """Instantiate the Experiment class. Parameters ---------- flow_params : dict flow-specific parameters custom_callables : dict < str, lambda > strings and lambda functions corresponding to some information we want to extract from the environment. The lambda will be called at each step to extract information from the env and it will be stored in a dict keyed by the str. """ self.custom_callables = custom_callables or {} # Get the env name and a creator for the environment. create_env, _ = make_create_env(flow_params) # Create the environment. self.env = create_env() logging.info(" Starting experiment {} at {}".format( self.env.network.name, str(datetime.datetime.utcnow()))) logging.info("Initializing environment.") def run(self, num_runs, rl_actions=None, convert_to_csv=False, partition_name=None, only_query=None): """Run the given network for a set number of runs. Parameters ---------- num_runs : int number of runs the experiment should perform rl_actions : method, optional maps states to actions to be performed by the RL agents (if there are any) convert_to_csv : bool Specifies whether to convert the emission file created by sumo into a csv file partition_name: str Specifies the S3 partition you want to store the output file, will be used to later for query. If NONE, won't upload output to S3. only_query: str Specifies whether queries should be automatically run the simulation data when it gets uploaded to s3 Returns ------- info_dict : dict < str, Any > contains returns, average speed per step """ num_steps = self.env.env_params.horizon # raise an error if convert_to_csv is set to True but no emission # file will be generated, to avoid getting an error at the end of the # simulation if convert_to_csv and self.env.sim_params.emission_path is None: raise ValueError( 'The experiment was run with convert_to_csv set ' 'to True, but no emission file will be generated. If you wish ' 'to generate an emission file, you should set the parameter ' 'emission_path in the simulation parameters (SumoParams or ' 'AimsunParams) to the path of the folder where emissions ' 'output should be generated. If you do not wish to generate ' 'emissions, set the convert_to_csv parameter to False.') # used to store info_dict = { "returns": [], "velocities": [], "outflows": [], } info_dict.update({ key: [] for key in self.custom_callables.keys() }) if rl_actions is None: def rl_actions(*_): return None # time profiling information t = time.time() times = [] extra_info = extra_init() source_id = uuid.uuid4().hex for i in range(num_runs): ret = 0 vel = [] custom_vals = {key: [] for key in self.custom_callables.keys()} state = self.env.reset() for j in range(num_steps): t0 = time.time() state, reward, done, _ = self.env.step(rl_actions(state)) t1 = time.time() times.append(1 / (t1 - t0)) # Compute the velocity speeds and cumulative returns. veh_ids = self.env.k.vehicle.get_ids() vel.append(np.mean(self.env.k.vehicle.get_speed(veh_ids))) ret += reward # collect additional information for the data pipeline get_extra_info(self.env.k.vehicle, extra_info, veh_ids) extra_info["source_id"].extend([source_id+"run" + str(i)] * len(veh_ids)) # Compute the results for the custom callables. for (key, lambda_func) in self.custom_callables.items(): custom_vals[key].append(lambda_func(self.env)) if done: break # Store the information from the run in info_dict. outflow = self.env.k.vehicle.get_outflow_rate(int(500)) info_dict["returns"].append(ret) info_dict["velocities"].append(np.mean(vel)) info_dict["outflows"].append(outflow) for key in custom_vals.keys(): info_dict[key].append(np.mean(custom_vals[key])) print("Round {0}, return: {1}".format(i, ret)) # Print the averages/std for all variables in the info_dict. for key in info_dict.keys(): print("Average, std {}: {}, {}".format( key, np.mean(info_dict[key]), np.std(info_dict[key]))) print("Total time:", time.time() - t) print("steps/second:", np.mean(times)) self.env.terminate() if convert_to_csv and self.env.simulator == "traci": # wait a short period of time to ensure the xml file is readable time.sleep(0.1) # collect the location of the emission file dir_path = self.env.sim_params.emission_path emission_filename = \ "{0}-emission.xml".format(self.env.network.name) emission_path = os.path.join(dir_path, emission_filename) # convert the emission file into a csv emission_to_csv(emission_path) # Delete the .xml version of the emission file. os.remove(emission_path) trajectory_table_path = './data/' + source_id + ".csv" upload_file_path = generate_trajectory_from_flow(trajectory_table_path, extra_info, partition_name) if partition_name: if partition_name == "default": partition_name = source_id[0:3] partition_name = date.today().isoformat() + " " + partition_name upload_to_s3('circles.data', 'trajectory-output/' + 'partition_name=' + partition_name + '/' + upload_file_path.split('/')[-1].split('_')[0] + '.csv', upload_file_path, str(only_query)[2:-2]) # delete the S3-only version of the trajectory file os.remove(upload_file_path) return info_dict \ No newline at end of file diff --git a/flow/core/kernel/vehicle/base.py b/flow/core/kernel/vehicle/base.py index 647ef37fe..16331ad08 100644 --- a/flow/core/kernel/vehicle/base.py +++ b/flow/core/kernel/vehicle/base.py @@ -701,6 +701,10 @@ def get_2d_position(self, veh_id, error=-1001): """Return (x, y) position of vehicle with veh_id.""" raise NotImplementedError + def get_2D_position(self, veh_id, error=-1001): + """ see traci class """ + raise NotImplementedError + def get_accel_without_noise(self, veh_id): """Return the acceleration without noise of vehicle with veh_id.""" raise NotImplementedError From aa14dbf247bbe5610d4f3741ed81581152596293 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Wed, 22 Apr 2020 05:22:01 -0700 Subject: [PATCH 50/89] added more support for lambda function --- examples/data_pipeline.py | 28 ++++++++++++++++++++++++++-- examples/lambda_function.py | 26 ++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 examples/lambda_function.py diff --git a/examples/data_pipeline.py b/examples/data_pipeline.py index 9d56548c2..28d3b5e73 100644 --- a/examples/data_pipeline.py +++ b/examples/data_pipeline.py @@ -39,6 +39,24 @@ def generate_trajectory_table(data_path, extra_info, partition_name): def generate_trajectory_from_flow(data_path, extra_info, partition_name): + """ generate desired output for the trajectory_table based only on flow output + + Parameters + ---------- + data_path : str + output file path + extra_info: dict + extra information needed in the trajectory table, collected from flow + partition_name: str + the name of the partition to put this output to + Returns + ------- + output_file_path: str + the local path of the outputted csv file that should be used for + upload to s3 only, it does not the human readable column names and + will be deleted after uploading to s3. A copy of this file with all + the column name will remain in the ./data folder + """ extra_info = pd.DataFrame.from_dict(extra_info) # extra_info["partition"] = partition_name extra_info.to_csv(data_path, index=False) @@ -47,7 +65,7 @@ def generate_trajectory_from_flow(data_path, extra_info, partition_name): return upload_only_file_path -def upload_to_s3(bucket_name, bucket_key, file_path): +def upload_to_s3(bucket_name, bucket_key, file_path, only_query): """ upload a file to S3 bucket Parameters @@ -58,9 +76,15 @@ def upload_to_s3(bucket_name, bucket_key, file_path): the key within the bucket for the file file_path: str the path of the file to be uploaded + only_query: str + specify which query should be run on this file by lambda: + if empty: run none of them + if "all": run all available analysis query + if a string of list of queries: run only those mentioned in the list """ s3 = boto3.resource("s3") - s3.Bucket(bucket_name).upload_file(file_path, bucket_key) + s3.Bucket(bucket_name).upload_file(file_path, bucket_key, + ExtraArgs={"Metadata": {"run-query": only_query}}) return diff --git a/examples/lambda_function.py b/examples/lambda_function.py new file mode 100644 index 000000000..01ce1512a --- /dev/null +++ b/examples/lambda_function.py @@ -0,0 +1,26 @@ +import boto3 +from urllib.parse import unquote_plus +from examples.data_pipeline import AthenaQuery +from examples.query import tags + +s3 = boto3.client('s3') +queryEngine = AthenaQuery() + + +def lambda_handler(event, context): + for record in event['Records']: + bucket = record['s3']['bucket']['name'] + key = unquote_plus(record['s3']['object']['key']) + partition = key.split('/')[-2].split('=')[-1] + response = s3.head_object(Bucket=bucket, Key=key) + run_query = response["Metadata"]["run-query"] + + if bucket == 'brent.experiments' and 'trajectory-output/' in key: + if run_query == "all": + query_list = tags["analysis"] + elif not run_query: + break + else: + query_list = run_query.split("\', \'") + for query_name in query_list: + queryEngine.run_query(query_name, 's3://brent.experiments/query-result/auto/', partition) \ No newline at end of file From 00a526b43f8ee069c768b27629233b074ca60260 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Thu, 23 Apr 2020 02:54:33 -0700 Subject: [PATCH 51/89] fix windoes line ending issue with experiment.py --- flow/core/experiment.py | 229 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 228 insertions(+), 1 deletion(-) diff --git a/flow/core/experiment.py b/flow/core/experiment.py index 97467adb5..2296ef635 100755 --- a/flow/core/experiment.py +++ b/flow/core/experiment.py @@ -1 +1,228 @@ -"""Contains an experiment class for running simulations.""" from flow.core.util import emission_to_csv from flow.utils.registry import make_create_env from flow.data_pipeline.data_pipeline import generate_trajectory_from_flow, upload_to_s3, extra_init, get_extra_info import datetime import logging import time from datetime import date import os import numpy as np import uuid class Experiment: """ Class for systematically running simulations in any supported simulator. This class acts as a runner for a network and environment. In order to use it to run an network and environment in the absence of a method specifying the actions of RL agents in the network, type the following: >>> from flow.envs import Env >>> flow_params = dict(...) # see the examples in exp_config >>> exp = Experiment(flow_params) # for some experiment configuration >>> exp.run(num_runs=1) If you wish to specify the actions of RL agents in the network, this may be done as follows: >>> rl_actions = lambda state: 0 # replace with something appropriate >>> exp.run(num_runs=1, rl_actions=rl_actions) Finally, if you would like to like to plot and visualize your results, this class can generate csv files from emission files produced by sumo. These files will contain the speeds, positions, edges, etc... of every vehicle in the network at every time step. In order to ensure that the simulator constructs an emission file, set the ``emission_path`` attribute in ``SimParams`` to some path. >>> from flow.core.params import SimParams >>> flow_params['sim'] = SimParams(emission_path="./data") Once you have included this in your environment, run your Experiment object as follows: >>> exp.run(num_runs=1, convert_to_csv=True) After the experiment is complete, look at the "./data" directory. There will be two files, one with the suffix .xml and another with the suffix .csv. The latter should be easily interpretable from any csv reader (e.g. Excel), and can be parsed using tools such as numpy and pandas. Attributes ---------- custom_callables : dict < str, lambda > strings and lambda functions corresponding to some information we want to extract from the environment. The lambda will be called at each step to extract information from the env and it will be stored in a dict keyed by the str. env : flow.envs.Env the environment object the simulator will run """ def __init__(self, flow_params, custom_callables=None): """Instantiate the Experiment class. Parameters ---------- flow_params : dict flow-specific parameters custom_callables : dict < str, lambda > strings and lambda functions corresponding to some information we want to extract from the environment. The lambda will be called at each step to extract information from the env and it will be stored in a dict keyed by the str. """ self.custom_callables = custom_callables or {} # Get the env name and a creator for the environment. create_env, _ = make_create_env(flow_params) # Create the environment. self.env = create_env() logging.info(" Starting experiment {} at {}".format( self.env.network.name, str(datetime.datetime.utcnow()))) logging.info("Initializing environment.") def run(self, num_runs, rl_actions=None, convert_to_csv=False, partition_name=None, only_query=None): """Run the given network for a set number of runs. Parameters ---------- num_runs : int number of runs the experiment should perform rl_actions : method, optional maps states to actions to be performed by the RL agents (if there are any) convert_to_csv : bool Specifies whether to convert the emission file created by sumo into a csv file partition_name: str Specifies the S3 partition you want to store the output file, will be used to later for query. If NONE, won't upload output to S3. only_query: str Specifies whether queries should be automatically run the simulation data when it gets uploaded to s3 Returns ------- info_dict : dict < str, Any > contains returns, average speed per step """ num_steps = self.env.env_params.horizon # raise an error if convert_to_csv is set to True but no emission # file will be generated, to avoid getting an error at the end of the # simulation if convert_to_csv and self.env.sim_params.emission_path is None: raise ValueError( 'The experiment was run with convert_to_csv set ' 'to True, but no emission file will be generated. If you wish ' 'to generate an emission file, you should set the parameter ' 'emission_path in the simulation parameters (SumoParams or ' 'AimsunParams) to the path of the folder where emissions ' 'output should be generated. If you do not wish to generate ' 'emissions, set the convert_to_csv parameter to False.') # used to store info_dict = { "returns": [], "velocities": [], "outflows": [], } info_dict.update({ key: [] for key in self.custom_callables.keys() }) if rl_actions is None: def rl_actions(*_): return None # time profiling information t = time.time() times = [] extra_info = extra_init() source_id = uuid.uuid4().hex for i in range(num_runs): ret = 0 vel = [] custom_vals = {key: [] for key in self.custom_callables.keys()} state = self.env.reset() for j in range(num_steps): t0 = time.time() state, reward, done, _ = self.env.step(rl_actions(state)) t1 = time.time() times.append(1 / (t1 - t0)) # Compute the velocity speeds and cumulative returns. veh_ids = self.env.k.vehicle.get_ids() vel.append(np.mean(self.env.k.vehicle.get_speed(veh_ids))) ret += reward # collect additional information for the data pipeline get_extra_info(self.env.k.vehicle, extra_info, veh_ids) extra_info["source_id"].extend([source_id+"run" + str(i)] * len(veh_ids)) # Compute the results for the custom callables. for (key, lambda_func) in self.custom_callables.items(): custom_vals[key].append(lambda_func(self.env)) if done: break # Store the information from the run in info_dict. outflow = self.env.k.vehicle.get_outflow_rate(int(500)) info_dict["returns"].append(ret) info_dict["velocities"].append(np.mean(vel)) info_dict["outflows"].append(outflow) for key in custom_vals.keys(): info_dict[key].append(np.mean(custom_vals[key])) print("Round {0}, return: {1}".format(i, ret)) # Print the averages/std for all variables in the info_dict. for key in info_dict.keys(): print("Average, std {}: {}, {}".format( key, np.mean(info_dict[key]), np.std(info_dict[key]))) print("Total time:", time.time() - t) print("steps/second:", np.mean(times)) self.env.terminate() if convert_to_csv and self.env.simulator == "traci": # wait a short period of time to ensure the xml file is readable time.sleep(0.1) # collect the location of the emission file dir_path = self.env.sim_params.emission_path emission_filename = \ "{0}-emission.xml".format(self.env.network.name) emission_path = os.path.join(dir_path, emission_filename) # convert the emission file into a csv emission_to_csv(emission_path) # Delete the .xml version of the emission file. os.remove(emission_path) trajectory_table_path = './data/' + source_id + ".csv" upload_file_path = generate_trajectory_from_flow(trajectory_table_path, extra_info, partition_name) if partition_name: if partition_name == "default": partition_name = source_id[0:3] partition_name = date.today().isoformat() + " " + partition_name upload_to_s3('circles.data', 'trajectory-output/' + 'partition_name=' + partition_name + '/' + upload_file_path.split('/')[-1].split('_')[0] + '.csv', upload_file_path, str(only_query)[2:-2]) # delete the S3-only version of the trajectory file os.remove(upload_file_path) return info_dict \ No newline at end of file +"""Contains an experiment class for running simulations.""" +from flow.core.util import emission_to_csv +from flow.utils.registry import make_create_env +from flow.data_pipeline.data_pipeline import generate_trajectory_from_flow, upload_to_s3, extra_init, get_extra_info +import datetime +import logging +import time +from datetime import date +import os +import numpy as np +import uuid + + +class Experiment: + """ + Class for systematically running simulations in any supported simulator. + + This class acts as a runner for a network and environment. In order to use + it to run an network and environment in the absence of a method specifying + the actions of RL agents in the network, type the following: + + >>> from flow.envs import Env + >>> flow_params = dict(...) # see the examples in exp_config + >>> exp = Experiment(flow_params) # for some experiment configuration + >>> exp.run(num_runs=1) + + If you wish to specify the actions of RL agents in the network, this may be + done as follows: + + >>> rl_actions = lambda state: 0 # replace with something appropriate + >>> exp.run(num_runs=1, rl_actions=rl_actions) + + Finally, if you would like to like to plot and visualize your results, this + class can generate csv files from emission files produced by sumo. These + files will contain the speeds, positions, edges, etc... of every vehicle + in the network at every time step. + + In order to ensure that the simulator constructs an emission file, set the + ``emission_path`` attribute in ``SimParams`` to some path. + + >>> from flow.core.params import SimParams + >>> flow_params['sim'] = SimParams(emission_path="./data") + + Once you have included this in your environment, run your Experiment object + as follows: + + >>> exp.run(num_runs=1, convert_to_csv=True) + + After the experiment is complete, look at the "./data" directory. There + will be two files, one with the suffix .xml and another with the suffix + .csv. The latter should be easily interpretable from any csv reader (e.g. + Excel), and can be parsed using tools such as numpy and pandas. + + Attributes + ---------- + custom_callables : dict < str, lambda > + strings and lambda functions corresponding to some information we want + to extract from the environment. The lambda will be called at each step + to extract information from the env and it will be stored in a dict + keyed by the str. + env : flow.envs.Env + the environment object the simulator will run + """ + + def __init__(self, flow_params, custom_callables=None): + """Instantiate the Experiment class. + + Parameters + ---------- + flow_params : dict + flow-specific parameters + custom_callables : dict < str, lambda > + strings and lambda functions corresponding to some information we + want to extract from the environment. The lambda will be called at + each step to extract information from the env and it will be stored + in a dict keyed by the str. + """ + self.custom_callables = custom_callables or {} + + # Get the env name and a creator for the environment. + create_env, _ = make_create_env(flow_params) + + # Create the environment. + self.env = create_env() + + logging.info(" Starting experiment {} at {}".format( + self.env.network.name, str(datetime.datetime.utcnow()))) + + logging.info("Initializing environment.") + + def run(self, num_runs, rl_actions=None, convert_to_csv=False, partition_name=None, only_query=None): + """Run the given network for a set number of runs. + + Parameters + ---------- + num_runs : int + number of runs the experiment should perform + rl_actions : method, optional + maps states to actions to be performed by the RL agents (if + there are any) + convert_to_csv : bool + Specifies whether to convert the emission file created by sumo + into a csv file + partition_name: str + Specifies the S3 partition you want to store the output file, + will be used to later for query. If NONE, won't upload output + to S3. + only_query: str + Specifies whether queries should be automatically run the + simulation data when it gets uploaded to s3 + + Returns + ------- + info_dict : dict < str, Any > + contains returns, average speed per step + """ + num_steps = self.env.env_params.horizon + + # raise an error if convert_to_csv is set to True but no emission + # file will be generated, to avoid getting an error at the end of the + # simulation + if convert_to_csv and self.env.sim_params.emission_path is None: + raise ValueError( + 'The experiment was run with convert_to_csv set ' + 'to True, but no emission file will be generated. If you wish ' + 'to generate an emission file, you should set the parameter ' + 'emission_path in the simulation parameters (SumoParams or ' + 'AimsunParams) to the path of the folder where emissions ' + 'output should be generated. If you do not wish to generate ' + 'emissions, set the convert_to_csv parameter to False.') + + # used to store + info_dict = { + "returns": [], + "velocities": [], + "outflows": [], + } + info_dict.update({ + key: [] for key in self.custom_callables.keys() + }) + + if rl_actions is None: + def rl_actions(*_): + return None + + # time profiling information + t = time.time() + times = [] + extra_info = extra_init() + source_id = uuid.uuid4().hex + + for i in range(num_runs): + ret = 0 + vel = [] + custom_vals = {key: [] for key in self.custom_callables.keys()} + state = self.env.reset() + for j in range(num_steps): + t0 = time.time() + state, reward, done, _ = self.env.step(rl_actions(state)) + t1 = time.time() + times.append(1 / (t1 - t0)) + + # Compute the velocity speeds and cumulative returns. + veh_ids = self.env.k.vehicle.get_ids() + vel.append(np.mean(self.env.k.vehicle.get_speed(veh_ids))) + ret += reward + + # collect additional information for the data pipeline + get_extra_info(self.env.k.vehicle, extra_info, veh_ids) + extra_info["source_id"].extend([source_id+"run" + str(i)] * len(veh_ids)) + + # Compute the results for the custom callables. + for (key, lambda_func) in self.custom_callables.items(): + custom_vals[key].append(lambda_func(self.env)) + + if done: + break + + # Store the information from the run in info_dict. + outflow = self.env.k.vehicle.get_outflow_rate(int(500)) + info_dict["returns"].append(ret) + info_dict["velocities"].append(np.mean(vel)) + info_dict["outflows"].append(outflow) + for key in custom_vals.keys(): + info_dict[key].append(np.mean(custom_vals[key])) + + print("Round {0}, return: {1}".format(i, ret)) + + # Print the averages/std for all variables in the info_dict. + for key in info_dict.keys(): + print("Average, std {}: {}, {}".format( + key, np.mean(info_dict[key]), np.std(info_dict[key]))) + + print("Total time:", time.time() - t) + print("steps/second:", np.mean(times)) + self.env.terminate() + + if convert_to_csv and self.env.simulator == "traci": + # wait a short period of time to ensure the xml file is readable + time.sleep(0.1) + + # collect the location of the emission file + dir_path = self.env.sim_params.emission_path + emission_filename = \ + "{0}-emission.xml".format(self.env.network.name) + emission_path = os.path.join(dir_path, emission_filename) + + # convert the emission file into a csv + emission_to_csv(emission_path) + + # Delete the .xml version of the emission file. + os.remove(emission_path) + + trajectory_table_path = './data/' + source_id + ".csv" + upload_file_path = generate_trajectory_from_flow(trajectory_table_path, extra_info, partition_name) + + if partition_name: + if partition_name == "default": + partition_name = source_id[0:3] + partition_name = date.today().isoformat() + " " + partition_name + upload_to_s3('circles.data', 'trajectory-output/' + 'partition_name=' + partition_name + '/' + + upload_file_path.split('/')[-1].split('_')[0] + '.csv', + upload_file_path, str(only_query)[2:-2]) + + # delete the S3-only version of the trajectory file + os.remove(upload_file_path) + + return info_dict From de35f9009e9de0c75de7ba4c1eccdccac794e877 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Thu, 23 Apr 2020 12:35:54 -0700 Subject: [PATCH 52/89] fix style issue --- examples/data_pipeline.py | 113 ++++++++++++++++++++----------- examples/datapipeline_test.py | 4 ++ examples/lambda_function.py | 10 +++ examples/query.py | 11 ++- examples/run_query.py | 1 + flow/core/kernel/vehicle/base.py | 2 +- 6 files changed, 98 insertions(+), 43 deletions(-) diff --git a/examples/data_pipeline.py b/examples/data_pipeline.py index 28d3b5e73..03b0f87e5 100644 --- a/examples/data_pipeline.py +++ b/examples/data_pipeline.py @@ -1,3 +1,4 @@ +"""contains class and helper functions for the data pipeline.""" import pandas as pd import numpy as np import boto3 @@ -7,21 +8,21 @@ def generate_trajectory_table(data_path, extra_info, partition_name): - """ generate desired output for the trajectory_table based on standard SUMO emission + """Generate desired output for the trajectory_table based on standard SUMO emission. - Parameters - ---------- - data_path : str - path to the standard SUMO emission - extra_info: dict - extra information needed in the trajectory table, collected from flow - partition_name: str - the name of the partition to put this output to - Returns - ------- - output_file_path: str - the local path of the outputted csv file - """ + Parameters + ---------- + data_path : str + path to the standard SUMO emission + extra_info: dict + extra information needed in the trajectory table, collected from flow + partition_name: str + the name of the partition to put this output to + Returns + ------- + output_file_path: str + the local path of the outputted csv file + """ raw_output = pd.read_csv(data_path, index_col=["time", "id"]) required_cols = {"time", "id", "speed", "x", "y"} raw_output = raw_output.drop(set(raw_output.columns) - required_cols, axis=1) @@ -39,24 +40,24 @@ def generate_trajectory_table(data_path, extra_info, partition_name): def generate_trajectory_from_flow(data_path, extra_info, partition_name): - """ generate desired output for the trajectory_table based only on flow output - - Parameters - ---------- - data_path : str - output file path - extra_info: dict - extra information needed in the trajectory table, collected from flow - partition_name: str - the name of the partition to put this output to - Returns - ------- - output_file_path: str - the local path of the outputted csv file that should be used for - upload to s3 only, it does not the human readable column names and - will be deleted after uploading to s3. A copy of this file with all - the column name will remain in the ./data folder - """ + """Generate desired output for the trajectory_table based only on flow output. + + Parameters + ---------- + data_path : str + output file path + extra_info: dict + extra information needed in the trajectory table, collected from flow + partition_name: str + the name of the partition to put this output to + Returns + ------- + output_file_path: str + the local path of the outputted csv file that should be used for + upload to s3 only, it does not the human readable column names and + will be deleted after uploading to s3. A copy of this file with all + the column name will remain in the ./data folder + """ extra_info = pd.DataFrame.from_dict(extra_info) # extra_info["partition"] = partition_name extra_info.to_csv(data_path, index=False) @@ -66,7 +67,7 @@ def generate_trajectory_from_flow(data_path, extra_info, partition_name): def upload_to_s3(bucket_name, bucket_key, file_path, only_query): - """ upload a file to S3 bucket + """Upload a file to S3 bucket. Parameters ---------- @@ -89,15 +90,40 @@ def upload_to_s3(bucket_name, bucket_key, file_path, only_query): class AthenaQuery: + """ + Class used to run query. + + Act as a query engine, maintains an open session with AWS Athena. + + Attributes + ---------- + MAX_WAIT: int + maximum number of seconds to wait before declares time-out + client: boto3.client + the athena client that is used to run the query + existing_partitions: list + a list of partitions that is already recorded in Athena's datalog, + this is obtained through query at the initialization of this class + instance. + """ def __init__(self): + """Initialize AthenaQuery instance. + + initialize a client session with AWS Athena, + query Athena to obtain extisting_partition. + """ self.MAX_WAIT = 60 self.client = boto3.client("athena") self.existing_partitions = self.get_existing_partitions() def get_existing_partitions(self): - """prints the existing partitions in the S3 bucket""" + """Return the existing partitions in the S3 bucket. + Returns + ------- + partitions: a list of existing partitions on S3 bucket + """ response = self.client.start_query_execution( QueryString='SHOW PARTITIONS trajectory_table', QueryExecutionContext={ @@ -114,7 +140,7 @@ def get_existing_partitions(self): return [data['Data'][0]['VarCharValue'].split('=')[-1] for data in response['ResultSet']['Rows']] def check_status(self, execution_id): - """ Return the status of the execution with given id + """Return the status of the execution with given id. Parameters ---------- @@ -125,14 +151,13 @@ def check_status(self, execution_id): status: str QUEUED|RUNNING|SUCCEEDED|FAILED|CANCELLED """ - response = self.client.get_query_execution( QueryExecutionId=execution_id ) return response['QueryExecution']['Status']['State'] def wait_for_execution(self, execution_id): - """ wait for the execution to finish or time-out + """Wait for the execution to finish or time-out. Parameters ---------- @@ -156,7 +181,7 @@ def wait_for_execution(self, execution_id): return True def update_partition(self, partition): - """ load the given partition to the trajectory_table on Athena + """Load the given partition to the trajectory_table on Athena. Parameters ---------- @@ -176,7 +201,7 @@ def update_partition(self, partition): return def run_query(self, query_name, result_location="s3://brent.experiments/query-result/", partition="default"): - """ start the execution of a query, does not wait for it to finish + """Start the execution of a query, does not wait for it to finish. Parameters ---------- @@ -218,6 +243,16 @@ def run_query(self, query_name, result_location="s3://brent.experiments/query-re def test_sql_query(query_name): + """Start the execution of a query, does not wait for it to finish. + + Parameters + ---------- + query_name : str + name of the query in QueryStrings enum that will be tested + Raises + ------ + RuntimeError: if timeout + """ if query_name not in testing_functions: raise ValueError("no tests supported for this query") diff --git a/examples/datapipeline_test.py b/examples/datapipeline_test.py index 564060d3b..ae0ea382f 100644 --- a/examples/datapipeline_test.py +++ b/examples/datapipeline_test.py @@ -1,3 +1,4 @@ +"""functions that calculates the expected result for testing.""" import math # Vehicle Mass @@ -17,10 +18,12 @@ def heavyside(inp): + """Return 1 if input is positive.""" return 0 if inp <= 0 else 1 def calculate_power(mu, acceleration, M=M, g=g, theta=theta, C_r=C_r, ro_air=ro_air, A=A, C_a=C_a): + """Calculate the expected power for POWER_DEMAND_MODEL query.""" acceleration = (0.8 + ((1 - 0.8) * heavyside(acceleration)) * acceleration) accel_and_slope = M * mu * (acceleration + g * math.sin(theta)) rolling_friction = M * g * C_r * mu @@ -30,4 +33,5 @@ def calculate_power(mu, acceleration, M=M, g=g, theta=theta, C_r=C_r, ro_air=ro_ def apply_energy_one(row): + """Apply the power calculation to a row of the dataframe.""" return [row[0], row[1], calculate_power(row[4], row[6])] \ No newline at end of file diff --git a/examples/lambda_function.py b/examples/lambda_function.py index 01ce1512a..4f7937c85 100644 --- a/examples/lambda_function.py +++ b/examples/lambda_function.py @@ -1,3 +1,4 @@ +"""lambda function on AWS Lambda.""" import boto3 from urllib.parse import unquote_plus from examples.data_pipeline import AthenaQuery @@ -8,6 +9,15 @@ def lambda_handler(event, context): + """Invoke by AWS Lambda upon triggered by an event. + + Parameters + ---------- + event : dic < str: dic > + an S3 event + context: + not used + """ for record in event['Records']: bucket = record['s3']['bucket']['name'] key = unquote_plus(record['s3']['object']['key']) diff --git a/examples/query.py b/examples/query.py index 6354cec3b..0f0ee13b4 100644 --- a/examples/query.py +++ b/examples/query.py @@ -1,15 +1,20 @@ +"""stores all the pre-defined query strings.""" from enum import Enum from examples.datapipeline_test import apply_energy_one -tags = {"energy": ["ENERGY_ONE"]} +# tags for different queries +tags = {"energy": ["POWER_DEMAND_MODEL"], "analysis": ["POWER_DEMAND_MODEL"]} -testing_functions = {"ENERGY_ONE": apply_energy_one} +# specify the function to calculate the expected result of each query +testing_functions = {"POWER_DEMAND_MODEL": apply_energy_one} class QueryStrings(Enum): + """An enumeration of all the pre-defined query strings.""" + SAMPLE = "SELECT * FROM trajectory_table WHERE partition_name=\'{partition}\' LIMIT 15;" UPDATE_PARTITION = "ALTER TABLE trajectory_table ADD IF NOT EXISTS PARTITION (partition_name=\'{partition}\');" - ENERGY_ONE = "SELECT id, time, 1200 * speed * " \ + POWER_DEMAND_MODEL = "SELECT id, time, 1200 * speed * " \ "((CASE WHEN acceleration > 0 THEN 1 ELSE 0 END * (1-0.8) * acceleration) + 0.8 " \ "+ 9.81 * SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 " \ "* POW(speed,3) AS power, 1 AS energy_model_id, source_id " \ diff --git a/examples/run_query.py b/examples/run_query.py index ea8839b09..64baa6656 100644 --- a/examples/run_query.py +++ b/examples/run_query.py @@ -1,3 +1,4 @@ +"""runner script for invoking query manually.""" import argparse from examples.data_pipeline import AthenaQuery, test_sql_query from examples.query import QueryStrings diff --git a/flow/core/kernel/vehicle/base.py b/flow/core/kernel/vehicle/base.py index 16331ad08..20a11cf99 100644 --- a/flow/core/kernel/vehicle/base.py +++ b/flow/core/kernel/vehicle/base.py @@ -702,7 +702,7 @@ def get_2d_position(self, veh_id, error=-1001): raise NotImplementedError def get_2D_position(self, veh_id, error=-1001): - """ see traci class """ + """Return (x, y) position of vehicle with veh_id.""" raise NotImplementedError def get_accel_without_noise(self, veh_id): From 979d0476fbd2e3308d4bc75f0fc3576306ae6ad5 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Thu, 23 Apr 2020 12:38:47 -0700 Subject: [PATCH 53/89] reorganized file locations --- examples/data_pipeline.py | 285 ------------------ examples/lambda_function.py | 36 --- examples/query.py | 22 -- examples/run_query.py | 37 --- .../data_pipeline}/datapipeline_test.py | 0 5 files changed, 380 deletions(-) delete mode 100644 examples/data_pipeline.py delete mode 100644 examples/lambda_function.py delete mode 100644 examples/query.py delete mode 100644 examples/run_query.py rename {examples => flow/data_pipeline}/datapipeline_test.py (100%) diff --git a/examples/data_pipeline.py b/examples/data_pipeline.py deleted file mode 100644 index 03b0f87e5..000000000 --- a/examples/data_pipeline.py +++ /dev/null @@ -1,285 +0,0 @@ -"""contains class and helper functions for the data pipeline.""" -import pandas as pd -import numpy as np -import boto3 -from botocore.exceptions import ClientError -from examples.query import QueryStrings, testing_functions -from time import time - - -def generate_trajectory_table(data_path, extra_info, partition_name): - """Generate desired output for the trajectory_table based on standard SUMO emission. - - Parameters - ---------- - data_path : str - path to the standard SUMO emission - extra_info: dict - extra information needed in the trajectory table, collected from flow - partition_name: str - the name of the partition to put this output to - Returns - ------- - output_file_path: str - the local path of the outputted csv file - """ - raw_output = pd.read_csv(data_path, index_col=["time", "id"]) - required_cols = {"time", "id", "speed", "x", "y"} - raw_output = raw_output.drop(set(raw_output.columns) - required_cols, axis=1) - - extra_info = pd.DataFrame.from_dict(extra_info) - extra_info.set_index(["time", "id"]) - raw_output = raw_output.merge(extra_info, how="left", left_on=["time", "id"], right_on=["time", "id"]) - - # add the partition column - # raw_output['partition'] = partition_name - raw_output = raw_output.sort_values(by=["time", "id"]) - output_file_path = data_path[:-4]+"_trajectory.csv" - raw_output.to_csv(output_file_path, index=False) - return output_file_path - - -def generate_trajectory_from_flow(data_path, extra_info, partition_name): - """Generate desired output for the trajectory_table based only on flow output. - - Parameters - ---------- - data_path : str - output file path - extra_info: dict - extra information needed in the trajectory table, collected from flow - partition_name: str - the name of the partition to put this output to - Returns - ------- - output_file_path: str - the local path of the outputted csv file that should be used for - upload to s3 only, it does not the human readable column names and - will be deleted after uploading to s3. A copy of this file with all - the column name will remain in the ./data folder - """ - extra_info = pd.DataFrame.from_dict(extra_info) - # extra_info["partition"] = partition_name - extra_info.to_csv(data_path, index=False) - upload_only_file_path = data_path[:-4] + "_upload" + ".csv" - extra_info.to_csv(upload_only_file_path, index=False, header=False) - return upload_only_file_path - - -def upload_to_s3(bucket_name, bucket_key, file_path, only_query): - """Upload a file to S3 bucket. - - Parameters - ---------- - bucket_name : str - the bucket to upload to - bucket_key: str - the key within the bucket for the file - file_path: str - the path of the file to be uploaded - only_query: str - specify which query should be run on this file by lambda: - if empty: run none of them - if "all": run all available analysis query - if a string of list of queries: run only those mentioned in the list - """ - s3 = boto3.resource("s3") - s3.Bucket(bucket_name).upload_file(file_path, bucket_key, - ExtraArgs={"Metadata": {"run-query": only_query}}) - return - - -class AthenaQuery: - """ - Class used to run query. - - Act as a query engine, maintains an open session with AWS Athena. - - Attributes - ---------- - MAX_WAIT: int - maximum number of seconds to wait before declares time-out - client: boto3.client - the athena client that is used to run the query - existing_partitions: list - a list of partitions that is already recorded in Athena's datalog, - this is obtained through query at the initialization of this class - instance. - """ - - def __init__(self): - """Initialize AthenaQuery instance. - - initialize a client session with AWS Athena, - query Athena to obtain extisting_partition. - """ - self.MAX_WAIT = 60 - self.client = boto3.client("athena") - self.existing_partitions = self.get_existing_partitions() - - def get_existing_partitions(self): - """Return the existing partitions in the S3 bucket. - - Returns - ------- - partitions: a list of existing partitions on S3 bucket - """ - response = self.client.start_query_execution( - QueryString='SHOW PARTITIONS trajectory_table', - QueryExecutionContext={ - 'Database': 'simulation' - }, - WorkGroup='primary' - ) - if self.wait_for_execution(response['QueryExecutionId']): - raise RuntimeError("get current partitions timed out") - response = self.client.get_query_results( - QueryExecutionId=response['QueryExecutionId'], - MaxResults=1000 - ) - return [data['Data'][0]['VarCharValue'].split('=')[-1] for data in response['ResultSet']['Rows']] - - def check_status(self, execution_id): - """Return the status of the execution with given id. - - Parameters - ---------- - execution_id : string - id of the execution that is checked for - Returns - ------- - status: str - QUEUED|RUNNING|SUCCEEDED|FAILED|CANCELLED - """ - response = self.client.get_query_execution( - QueryExecutionId=execution_id - ) - return response['QueryExecution']['Status']['State'] - - def wait_for_execution(self, execution_id): - """Wait for the execution to finish or time-out. - - Parameters - ---------- - execution_id : str - id of the execution this is watiing for - Returns - ------- - time_out: bool - True if time-out, False if success - Raises - ------ - RuntimeError: if execution failed or get canceled - """ - start = time() - while time() - start < self.MAX_WAIT: - state = self.check_status(execution_id) - if state == 'FAILED' or state == 'CANCELLED': - raise RuntimeError("update partition failed") - elif state == 'SUCCEEDED': - return False - return True - - def update_partition(self, partition): - """Load the given partition to the trajectory_table on Athena. - - Parameters - ---------- - partition : str - the new partition that needs to be loaded - """ - response = self.client.start_query_execution( - QueryString=QueryStrings['UPDATE_PARTITION'].value.format(partition=partition), - QueryExecutionContext={ - 'Database': 'simulation' - }, - WorkGroup='primary' - ) - if self.wait_for_execution(response['QueryExecutionId']): - raise RuntimeError("update partition timed out") - self.existing_partitions.append(partition) - return - - def run_query(self, query_name, result_location="s3://brent.experiments/query-result/", partition="default"): - """Start the execution of a query, does not wait for it to finish. - - Parameters - ---------- - query_name : str - name of the query in QueryStrings enum that will be run - result_location: str, optional - location on the S3 bucket where the result will be stored - partition: str, optional - name of the partition to run this query on - Returns - ------- - execution_id: str - the execution id of the execution started by this method - Raises - ------ - ValueError: if tries to run a query not existed in QueryStrings enum - """ - if query_name not in QueryStrings.__members__: - raise ValueError("query not existed: please add it to query.py") - - if partition not in self.existing_partitions: - self.update_partition(partition) - - response = self.client.start_query_execution( - QueryString=QueryStrings[query_name].value.format(partition=partition), - QueryExecutionContext={ - 'Database': 'simulation' - }, - ResultConfiguration={ - 'OutputLocation': result_location, - }, - WorkGroup='primary' - ) - return response['QueryExecutionId'] - -########################################################################### -# Helpers for testing the SQL Queries # -########################################################################### - - -def test_sql_query(query_name): - """Start the execution of a query, does not wait for it to finish. - - Parameters - ---------- - query_name : str - name of the query in QueryStrings enum that will be tested - Raises - ------ - RuntimeError: if timeout - """ - if query_name not in testing_functions: - raise ValueError("no tests supported for this query") - - # Run the respective sql query - queryEngine = AthenaQuery() - execution_id = queryEngine.run_query(query_name, result_location="s3://brent.experiments/query-result/query-test", - partition="test") - if queryEngine.wait_for_execution(execution_id): - raise RuntimeError("execution timed out") - - # get the Athena query result from S3 - s3 = boto3.resource("s3") - s3.Bucket("brent.experiments").download_file("query-result/query-test/"+execution_id+".csv", - "data/athena_result.csv") - athena_result = pd.read_csv("data/athena_result.csv") - athena_result = athena_result.sort_values(by=["time", "id"]) - - # get the python expected result - expected_result = pd.read_csv("data/test_data.csv") - expected_result = expected_result.apply(testing_functions[query_name], axis=1, result_type="expand") - expected_result.columns = ["time", "id", "power"] - expected_result = expected_result.sort_values(by=["time", "id"]) - - difference = athena_result["power"] - expected_result["power"] - print("average difference is: " + str(np.mean(difference))) - print("std of difference is: " + str(np.std(difference))) - print("average ratio of difference to expected is: " + - str(np.mean(np.divide(difference, expected_result["power"])))) - difference = pd.DataFrame(difference) - difference.to_csv("./difference.csv") diff --git a/examples/lambda_function.py b/examples/lambda_function.py deleted file mode 100644 index 4f7937c85..000000000 --- a/examples/lambda_function.py +++ /dev/null @@ -1,36 +0,0 @@ -"""lambda function on AWS Lambda.""" -import boto3 -from urllib.parse import unquote_plus -from examples.data_pipeline import AthenaQuery -from examples.query import tags - -s3 = boto3.client('s3') -queryEngine = AthenaQuery() - - -def lambda_handler(event, context): - """Invoke by AWS Lambda upon triggered by an event. - - Parameters - ---------- - event : dic < str: dic > - an S3 event - context: - not used - """ - for record in event['Records']: - bucket = record['s3']['bucket']['name'] - key = unquote_plus(record['s3']['object']['key']) - partition = key.split('/')[-2].split('=')[-1] - response = s3.head_object(Bucket=bucket, Key=key) - run_query = response["Metadata"]["run-query"] - - if bucket == 'brent.experiments' and 'trajectory-output/' in key: - if run_query == "all": - query_list = tags["analysis"] - elif not run_query: - break - else: - query_list = run_query.split("\', \'") - for query_name in query_list: - queryEngine.run_query(query_name, 's3://brent.experiments/query-result/auto/', partition) \ No newline at end of file diff --git a/examples/query.py b/examples/query.py deleted file mode 100644 index 0f0ee13b4..000000000 --- a/examples/query.py +++ /dev/null @@ -1,22 +0,0 @@ -"""stores all the pre-defined query strings.""" -from enum import Enum -from examples.datapipeline_test import apply_energy_one - -# tags for different queries -tags = {"energy": ["POWER_DEMAND_MODEL"], "analysis": ["POWER_DEMAND_MODEL"]} - -# specify the function to calculate the expected result of each query -testing_functions = {"POWER_DEMAND_MODEL": apply_energy_one} - - -class QueryStrings(Enum): - """An enumeration of all the pre-defined query strings.""" - - SAMPLE = "SELECT * FROM trajectory_table WHERE partition_name=\'{partition}\' LIMIT 15;" - UPDATE_PARTITION = "ALTER TABLE trajectory_table ADD IF NOT EXISTS PARTITION (partition_name=\'{partition}\');" - POWER_DEMAND_MODEL = "SELECT id, time, 1200 * speed * " \ - "((CASE WHEN acceleration > 0 THEN 1 ELSE 0 END * (1-0.8) * acceleration) + 0.8 " \ - "+ 9.81 * SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 " \ - "* POW(speed,3) AS power, 1 AS energy_model_id, source_id " \ - "FROM trajectory_table " \ - "WHERE partition_name=\'{partition}\'" \ No newline at end of file diff --git a/examples/run_query.py b/examples/run_query.py deleted file mode 100644 index 64baa6656..000000000 --- a/examples/run_query.py +++ /dev/null @@ -1,37 +0,0 @@ -"""runner script for invoking query manually.""" -import argparse -from examples.data_pipeline import AthenaQuery, test_sql_query -from examples.query import QueryStrings - -parser = argparse.ArgumentParser(prog="run_query", description="runs query on AWS Athena and stores the result to" - "a S3 location") -parser.add_argument("--run", type=str, nargs="+") -parser.add_argument("--result_location", type=str, nargs='?', default="s3://brent.experiments/query-result/") -parser.add_argument("--partition", type=str, nargs='?', default="default") -parser.add_argument("--list_partitions", action="store_true") -parser.add_argument("--check_status", type=str, nargs='+') -parser.add_argument("--list_queries", action="store_true") -parser.add_argument("--test_query", nargs=1) - - -if __name__ == "__main__": - args = parser.parse_args() - queryEngine = AthenaQuery() - - if args.run: - execution_ids = [] - for query_name in args.run: - execution_ids.append(queryEngine.run_query(query_name, args.result_location, args.partition)) - print(execution_ids) - if args.list_partitions: - print(queryEngine.existing_partitions) - if args.check_status: - status = dict() - for execution_id in args.check_status: - status[execution_id] = queryEngine.check_status(execution_id) - print(status) - if args.list_queries: - for q in QueryStrings: - print(q) - if args.test_query: - test_sql_query(args.test_query[0]) \ No newline at end of file diff --git a/examples/datapipeline_test.py b/flow/data_pipeline/datapipeline_test.py similarity index 100% rename from examples/datapipeline_test.py rename to flow/data_pipeline/datapipeline_test.py From fdd983eb19b7a4acd75b9101568dfa8441c86294 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Thu, 23 Apr 2020 12:58:44 -0700 Subject: [PATCH 54/89] fix some more style issues --- flow/data_pipeline/datapipeline_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/data_pipeline/datapipeline_test.py b/flow/data_pipeline/datapipeline_test.py index ae0ea382f..0e1a50518 100644 --- a/flow/data_pipeline/datapipeline_test.py +++ b/flow/data_pipeline/datapipeline_test.py @@ -34,4 +34,4 @@ def calculate_power(mu, acceleration, M=M, g=g, theta=theta, C_r=C_r, ro_air=ro_ def apply_energy_one(row): """Apply the power calculation to a row of the dataframe.""" - return [row[0], row[1], calculate_power(row[4], row[6])] \ No newline at end of file + return [row[0], row[1], calculate_power(row[4], row[6])] From 6af7e02c86ddfbce78851d2c85a2042ae3b9ea6c Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Tue, 19 May 2020 04:01:41 -0700 Subject: [PATCH 55/89] added auto upload to s3 feature for the reply scipt and fix some other minor issues --- flow/core/experiment.py | 15 ++--- flow/core/kernel/vehicle/traci.py | 5 +- flow/data_pipeline/data_pipeline.py | 12 ++-- flow/data_pipeline/lambda_function.py | 4 +- flow/data_pipeline/query.py | 86 ++++++++++++++------------- flow/data_pipeline/run_query.py | 2 +- flow/visualize/i210_replay.py | 21 ++++++- 7 files changed, 82 insertions(+), 63 deletions(-) diff --git a/flow/core/experiment.py b/flow/core/experiment.py index 2296ef635..84a34d0e3 100755 --- a/flow/core/experiment.py +++ b/flow/core/experiment.py @@ -88,7 +88,7 @@ def __init__(self, flow_params, custom_callables=None): logging.info("Initializing environment.") - def run(self, num_runs, rl_actions=None, convert_to_csv=False, partition_name=None, only_query=None): + def run(self, num_runs, rl_actions=None, convert_to_csv=False, partition_name=None, only_query=""): """Run the given network for a set number of runs. Parameters @@ -106,8 +106,9 @@ def run(self, num_runs, rl_actions=None, convert_to_csv=False, partition_name=No will be used to later for query. If NONE, won't upload output to S3. only_query: str - Specifies whether queries should be automatically run the - simulation data when it gets uploaded to s3 + Specifies which queries should be automatically run when the + simulation data gets uploaded to S3. If an empty str is passed in, + then it implies no queries should be run on this. Returns ------- @@ -147,7 +148,7 @@ def rl_actions(*_): t = time.time() times = [] extra_info = extra_init() - source_id = uuid.uuid4().hex + source_id = 'flow_{}'.format(uuid.uuid4().hex) for i in range(num_runs): ret = 0 @@ -167,7 +168,7 @@ def rl_actions(*_): # collect additional information for the data pipeline get_extra_info(self.env.k.vehicle, extra_info, veh_ids) - extra_info["source_id"].extend([source_id+"run" + str(i)] * len(veh_ids)) + extra_info["source_id"].extend(['{}_run_{}'.format(source_id, i)] * len(veh_ids)) # Compute the results for the custom callables. for (key, lambda_func) in self.custom_callables.items(): @@ -218,8 +219,8 @@ def rl_actions(*_): if partition_name == "default": partition_name = source_id[0:3] partition_name = date.today().isoformat() + " " + partition_name - upload_to_s3('circles.data', 'trajectory-output/' + 'partition_name=' + partition_name + '/' - + upload_file_path.split('/')[-1].split('_')[0] + '.csv', + upload_to_s3('circles.data.pipeline', 'trajectory-output/partition_name={}/{}.csv'.format( + partition_name, upload_file_path.split('/')[-1].split('_')[0]), upload_file_path, str(only_query)[2:-2]) # delete the S3-only version of the trajectory file diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index f40eed99c..824ec4b0c 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -970,7 +970,6 @@ def apply_acceleration(self, veh_ids, acc): self.__vehicles[vid]["accel"] = acc[i] this_vel = self.get_speed(vid) next_vel = max([this_vel + acc[i] * self.sim_step, 0]) - #self.__vehicles[vid]["next_v"] = next_vel self.kernel_api.vehicle.slowDown(vid, next_vel, 1e-3) def apply_lane_change(self, veh_ids, direction): @@ -1135,7 +1134,7 @@ def set_max_speed(self, veh_id, max_speed): # add for data pipeline def get_accel(self, veh_id): """See parent class.""" - if not "accel" in self.__vehicles[veh_id]: + if "accel" not in self.__vehicles[veh_id]: self.__vehicles[veh_id]["accel"] = None return self.__vehicles[veh_id]["accel"] @@ -1145,7 +1144,7 @@ def update_accel_without_noise(self, veh_id, accel_without_noise): def get_accel_without_noise(self, veh_id): """See parent class.""" - if not "accel_without_noise" in self.__vehicles[veh_id]: + if "accel_without_noise" not in self.__vehicles[veh_id]: self.__vehicles[veh_id]["accel_without_noise"] = None return self.__vehicles[veh_id]["accel_without_noise"] diff --git a/flow/data_pipeline/data_pipeline.py b/flow/data_pipeline/data_pipeline.py index 0cd0cbc79..fbd975c5e 100644 --- a/flow/data_pipeline/data_pipeline.py +++ b/flow/data_pipeline/data_pipeline.py @@ -38,7 +38,7 @@ def generate_trajectory_table(data_path, extra_info, partition_name): return output_file_path -def generate_trajectory_from_flow(data_path, extra_info, partition_name): +def generate_trajectory_from_flow(data_path, extra_info, partition_name=None): """Generate desired output for the trajectory_table based only on flow output. Parameters @@ -227,7 +227,7 @@ def update_partition(self, partition): self.existing_partitions.append(partition) return - def run_query(self, query_name, result_location="s3://circles.data/query-result/", partition="default"): + def run_query(self, query_name, result_location="s3://circles.data.pipeline/query-result/", partition="default"): """Start the execution of a query, does not wait for it to finish. Parameters @@ -285,15 +285,15 @@ def test_sql_query(query_name): # Run the respective sql query queryEngine = AthenaQuery() - execution_id = queryEngine.run_query(query_name, result_location="s3://circles.data/query-result/query-test", - partition="test") + execution_id = queryEngine.run_query(query_name, result_location="s3://circles.data.pipeline/" + "query-result/query-test", partition="test") if queryEngine.wait_for_execution(execution_id): raise RuntimeError("execution timed out") # get the Athena query result from S3 s3 = boto3.resource("s3") - s3.Bucket("circles.data").download_file("query-result/query-test/"+execution_id+".csv", - "data/athena_result.csv") + s3.Bucket("circles.data.pipeline").download_file("query-result/query-test/"+execution_id+".csv", + "data/athena_result.csv") athena_result = pd.read_csv("data/athena_result.csv") athena_result = athena_result.sort_values(by=["time", "id"]) diff --git a/flow/data_pipeline/lambda_function.py b/flow/data_pipeline/lambda_function.py index 3f0abb8a1..fd50ba8f5 100644 --- a/flow/data_pipeline/lambda_function.py +++ b/flow/data_pipeline/lambda_function.py @@ -25,7 +25,7 @@ def lambda_handler(event, context): response = s3.head_object(Bucket=bucket, Key=key) run_query = response["Metadata"]["run-query"] - if bucket == 'circles.data' and 'trajectory-output/' in key: + if bucket == 'circles.data.pipeline' and 'trajectory-output/' in key: if run_query == "all": query_list = tags["analysis"] elif not run_query: @@ -33,4 +33,4 @@ def lambda_handler(event, context): else: query_list = run_query.split("\', \'") for query_name in query_list: - queryEngine.run_query(query_name, 's3://circles.data/query-result/auto/', partition) + queryEngine.run_query(query_name, 's3://circles.data.pipeline/query-result/auto/', partition) diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index 9054364e6..7b8cf70c8 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -15,45 +15,47 @@ class QueryStrings(Enum): SAMPLE = "SELECT * FROM trajectory_table WHERE partition_name=\'{partition}\' LIMIT 15;" UPDATE_PARTITION = "ALTER TABLE trajectory_table ADD IF NOT EXISTS PARTITION (partition_name=\'{partition}\');" - POWER_DEMAND_MODEL = "SELECT id, time, speed, acceleration, 1200 * speed * " \ - "((CASE WHEN acceleration > 0 THEN 1 ELSE 0 END * (1-0.8) * acceleration) + 0.8 " \ - "+ 9.81 * SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 " \ - "* POW(speed,3) AS power, 'POWER_DEMAND_MODEL' AS energy_model_id, source_id " \ - "FROM trajectory_table " \ - "WHERE partition_name=\'{partition}\' " \ - "ORDER BY id, time " - POWER_DEMAND_MODEL_DENOISED_ACCEL = \ - "SELECT id, time, speed, accel_without_noise, 1200 * speed * " \ - "((CASE WHEN accel_without_noise > 0 THEN 1 ELSE 0 END * (1-0.8) * accel_without_noise)+0.8 " \ - "+ 9.81 * SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 " \ - "* POW(speed,3) AS power, 'POWER_DEMAND_MODEL_DENOISED_ACCEL' AS energy_model_id, source_id " \ - "FROM trajectory_table " \ - "WHERE partition_name=\'{partition}\' " \ - "ORDER BY id, time " - POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL = \ - "WITH sub1 AS ( " \ - "SELECT time, id, speed, acceleration, accel_without_noise, road_grade, source_id, " \ - "time - LAG(time, 1) " \ - "OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS sim_step, " \ - "LAG(speed, 1) " \ - "OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_speed, " \ - "LAG(acceleration, 1) " \ - "OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_accel, " \ - "LAG(accel_without_noise, 1) " \ - "OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_accel_denoised "\ - "FROM trajectory_table " \ - "WHERE partition_name=\'{partition}\'" \ - ")," \ - "sub2 AS (" \ - "SELECT time, id, speed, acceleration, accel_without_noise, road_grade, source_id, " \ - "prev_speed+accel_without_noise*sim_step AS speed_denoised " \ - "FROM sub1" \ - ") " \ - "SELECT id, time, speed_denoised, accel_without_noise, " \ - "1200 * speed_denoised * ((CASE WHEN accel_without_noise > 0 " \ - "THEN 1 ELSE 0 END * (1-0.8) * accel_without_noise) + 0.8 + 9.81 " \ - "* SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed_denoised + 0.5 * 1.225 " \ - "* 2.6 * 0.3 * POW(speed_denoised,3) AS power, " \ - "'POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL' AS energy_model, source_id " \ - "FROM sub2 " \ - "ORDER BY id, time " + POWER_DEMAND_MODEL = """ + SELECT id, time, speed, acceleration, 1200 * speed * + ((CASE WHEN acceleration > 0 THEN 1 ELSE 0 END * (1-0.8) * acceleration) + 0.8 + + 9.81 * SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 + * POW(speed,3) AS power, + 'POWER_DEMAND_MODEL' AS energy_model_id, source_id + FROM trajectory_table + WHERE partition_name=\'{partition}\' + ORDER BY id, time """ + POWER_DEMAND_MODEL_DENOISED_ACCEL = """ + SELECT id, time, speed, accel_without_noise, + 1200 * speed * ((CASE WHEN accel_without_noise > 0 THEN 1 ELSE 0 END * (1-0.8) + * accel_without_noise)+0.8 + 9.81 * SIN(road_grade)) + + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 * POW(speed,3) AS power, + 'POWER_DEMAND_MODEL_DENOISED_ACCEL' AS energy_model_id, source_id + FROM trajectory_table + WHERE partition_name=\'{partition}\' + ORDER BY id, time """ + POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL = """ + WITH lagged_timestep AS ( + SELECT time, id, speed, acceleration, accel_without_noise, road_grade, source_id, + time - LAG(time, 1) + OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS sim_step, + LAG(speed, 1) + OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_speed, + LAG(acceleration, 1) + OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_accel, + LAG(accel_without_noise, 1) + OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_accel_denoised + FROM trajectory_table + WHERE partition_name=\'{partition}\'), + speed_denoised_table AS ( + SELECT time, id, speed, acceleration, accel_without_noise, road_grade, source_id, + prev_speed+accel_without_noise*sim_step AS speed_denoised + FROM lagged_timestep + ) + SELECT id, time, speed_denoised, accel_without_noise, + 1200 * speed_denoised * ((CASE WHEN accel_without_noise > 0 + THEN 1 ELSE 0 END * (1-0.8) * accel_without_noise) + 0.8 + 9.81 + * SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed_denoised + 0.5 * 1.225 + * 2.6 * 0.3 * POW(speed_denoised,3) AS power, + 'POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL' AS energy_model, source_id + FROM speed_denoised_table + ORDER BY id, time """ diff --git a/flow/data_pipeline/run_query.py b/flow/data_pipeline/run_query.py index f065a726e..ac927c749 100644 --- a/flow/data_pipeline/run_query.py +++ b/flow/data_pipeline/run_query.py @@ -6,7 +6,7 @@ parser = argparse.ArgumentParser(prog="run_query", description="runs query on AWS Athena and stores the result to" "a S3 location") parser.add_argument("--run", type=str, nargs="+") -parser.add_argument("--result_location", type=str, nargs='?', default="s3://brent.experiments/query-result/") +parser.add_argument("--result_location", type=str, nargs='?', default="s3://circles.data.pipeline/query-result/") parser.add_argument("--partition", type=str, nargs='?', default="default") parser.add_argument("--list_partitions", action="store_true") parser.add_argument("--check_status", type=str, nargs='+') diff --git a/flow/visualize/i210_replay.py b/flow/visualize/i210_replay.py index c50f12a05..0df23942e 100644 --- a/flow/visualize/i210_replay.py +++ b/flow/visualize/i210_replay.py @@ -209,7 +209,7 @@ def replay(args, flow_params, output_dir=None, transfer_test=None, rllib_config= }) extra_info = extra_init() - source_id = uuid.uuid4().hex + source_id = 'flow_{}'.format(uuid.uuid4().hex) for i in range(args.num_rollouts): vel = [] @@ -249,7 +249,7 @@ def replay(args, flow_params, output_dir=None, transfer_test=None, rllib_config= # Collect information from flow for the trajectory output get_extra_info(env.k.vehicle, extra_info, veh_ids) - extra_info["source_id"].extend([source_id + "run" + str(i)] * len(veh_ids)) + extra_info["source_id"].extend(['{}_run_{}'.format(source_id, i)] * len(veh_ids)) # Compute the results for the custom callables. for (key, lambda_func) in custom_callables.items(): @@ -326,6 +326,17 @@ def replay(args, flow_params, output_dir=None, transfer_test=None, rllib_config= # convert the emission file into a csv file emission_to_csv(emission_path, output_path=output_path) + # generate the trajectory output file + trajectory_table_path = './data/' + source_id + ".csv" + upload_file_path = generate_trajectory_from_flow(trajectory_table_path, extra_info) + + # upload to s3 if asked + if args.use_s3: + partition_name = date.today().isoformat() + " " + source_id[0:3] + upload_to_s3('circles.data.pipeline', 'trajectory-output/' + 'partition_name=' + partition_name + '/' + + upload_file_path.split('/')[-1].split('_')[0] + '.csv', + upload_file_path, str(args.only_query)[2:-2]) + # print the location of the emission csv file print("\nGenerated emission file at " + output_path) @@ -435,6 +446,12 @@ def create_parser(): 'be run in cluster mode') parser.add_argument('--exp_title', type=str, required=False, default=None, help='Informative experiment title to help distinguish results') + parser.add_argument( + '--only_query', + nargs='*', default="[\'all\']", + help='specify which query should be run by lambda' + 'for detail, see upload_to_s3 in data_pipeline.py' + ) return parser From 72d4733f07458a2863bb2c95cb7ef75c89935d33 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Tue, 19 May 2020 04:07:29 -0700 Subject: [PATCH 56/89] fix trailing white space style issue --- flow/data_pipeline/query.py | 79 ++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index 7b8cf70c8..c915d44bf 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -16,46 +16,45 @@ class QueryStrings(Enum): SAMPLE = "SELECT * FROM trajectory_table WHERE partition_name=\'{partition}\' LIMIT 15;" UPDATE_PARTITION = "ALTER TABLE trajectory_table ADD IF NOT EXISTS PARTITION (partition_name=\'{partition}\');" POWER_DEMAND_MODEL = """ - SELECT id, time, speed, acceleration, 1200 * speed * - ((CASE WHEN acceleration > 0 THEN 1 ELSE 0 END * (1-0.8) * acceleration) + 0.8 - + 9.81 * SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 - * POW(speed,3) AS power, - 'POWER_DEMAND_MODEL' AS energy_model_id, source_id - FROM trajectory_table - WHERE partition_name=\'{partition}\' - ORDER BY id, time """ + SELECT id, time, speed, acceleration, 1200 * speed * + ((CASE WHEN acceleration > 0 THEN 1 ELSE 0 END * (1-0.8) * acceleration) + 0.8 + + 9.81 * SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 + * POW(speed,3) AS power, + 'POWER_DEMAND_MODEL' AS energy_model_id, source_id + FROM trajectory_table + WHERE partition_name=\'{partition}\' + ORDER BY id, time""" POWER_DEMAND_MODEL_DENOISED_ACCEL = """ - SELECT id, time, speed, accel_without_noise, - 1200 * speed * ((CASE WHEN accel_without_noise > 0 THEN 1 ELSE 0 END * (1-0.8) - * accel_without_noise)+0.8 + 9.81 * SIN(road_grade)) - + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 * POW(speed,3) AS power, - 'POWER_DEMAND_MODEL_DENOISED_ACCEL' AS energy_model_id, source_id - FROM trajectory_table - WHERE partition_name=\'{partition}\' - ORDER BY id, time """ + SELECT id, time, speed, accel_without_noise, + 1200 * speed * ((CASE WHEN accel_without_noise > 0 THEN 1 ELSE 0 END * (1-0.8) + * accel_without_noise)+0.8 + 9.81 * SIN(road_grade)) + + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 * POW(speed,3) AS power, + 'POWER_DEMAND_MODEL_DENOISED_ACCEL' AS energy_model_id, source_id + FROM trajectory_table + WHERE partition_name=\'{partition}\' + ORDER BY id, time""" POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL = """ - WITH lagged_timestep AS ( - SELECT time, id, speed, acceleration, accel_without_noise, road_grade, source_id, - time - LAG(time, 1) - OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS sim_step, - LAG(speed, 1) - OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_speed, - LAG(acceleration, 1) - OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_accel, - LAG(accel_without_noise, 1) - OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_accel_denoised - FROM trajectory_table + WITH lagged_timestep AS ( + SELECT time, id, speed, acceleration, accel_without_noise, road_grade, source_id, + time - LAG(time, 1) + OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS sim_step, + LAG(speed, 1) + OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_speed, + LAG(acceleration, 1) + OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_accel, + LAG(accel_without_noise, 1) + OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_accel_denoised + FROM trajectory_table WHERE partition_name=\'{partition}\'), - speed_denoised_table AS ( - SELECT time, id, speed, acceleration, accel_without_noise, road_grade, source_id, - prev_speed+accel_without_noise*sim_step AS speed_denoised - FROM lagged_timestep - ) - SELECT id, time, speed_denoised, accel_without_noise, - 1200 * speed_denoised * ((CASE WHEN accel_without_noise > 0 - THEN 1 ELSE 0 END * (1-0.8) * accel_without_noise) + 0.8 + 9.81 - * SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed_denoised + 0.5 * 1.225 - * 2.6 * 0.3 * POW(speed_denoised,3) AS power, - 'POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL' AS energy_model, source_id - FROM speed_denoised_table - ORDER BY id, time """ + speed_denoised_table AS ( + SELECT time, id, speed, acceleration, accel_without_noise, road_grade, source_id, + prev_speed+accel_without_noise*sim_step AS speed_denoised + FROM lagged_timestep) + SELECT id, time, speed_denoised, accel_without_noise, + 1200 * speed_denoised * ((CASE WHEN accel_without_noise > 0 + THEN 1 ELSE 0 END * (1-0.8) * accel_without_noise) + 0.8 + 9.81 + * SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed_denoised + 0.5 * 1.225 + * 2.6 * 0.3 * POW(speed_denoised,3) AS power, + 'POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL' AS energy_model, source_id + FROM speed_denoised_table + ORDER BY id, time""" From 420ea3f798d00e2a79260b82b79092f304ee9b72 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Tue, 19 May 2020 04:10:43 -0700 Subject: [PATCH 57/89] some minor issue fixed --- flow/data_pipeline/query.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index c915d44bf..d40e14c45 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -26,7 +26,7 @@ class QueryStrings(Enum): ORDER BY id, time""" POWER_DEMAND_MODEL_DENOISED_ACCEL = """ SELECT id, time, speed, accel_without_noise, - 1200 * speed * ((CASE WHEN accel_without_noise > 0 THEN 1 ELSE 0 END * (1-0.8) + 1200 * speed * ((CASE WHEN accel_without_noise > 0 THEN 1 ELSE 0 END * (1-0.8) * accel_without_noise)+0.8 + 9.81 * SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 * POW(speed,3) AS power, 'POWER_DEMAND_MODEL_DENOISED_ACCEL' AS energy_model_id, source_id From e45eb92cc420836fa297c0ccceb2d93d88d06359 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Tue, 19 May 2020 08:42:29 -0700 Subject: [PATCH 58/89] reformatting energy queries --- flow/data_pipeline/query.py | 94 ++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 43 deletions(-) diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index d40e14c45..c6be5efe4 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -13,48 +13,56 @@ class QueryStrings(Enum): """An enumeration of all the pre-defined query strings.""" - SAMPLE = "SELECT * FROM trajectory_table WHERE partition_name=\'{partition}\' LIMIT 15;" - UPDATE_PARTITION = "ALTER TABLE trajectory_table ADD IF NOT EXISTS PARTITION (partition_name=\'{partition}\');" - POWER_DEMAND_MODEL = """ - SELECT id, time, speed, acceleration, 1200 * speed * - ((CASE WHEN acceleration > 0 THEN 1 ELSE 0 END * (1-0.8) * acceleration) + 0.8 - + 9.81 * SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 - * POW(speed,3) AS power, - 'POWER_DEMAND_MODEL' AS energy_model_id, source_id - FROM trajectory_table - WHERE partition_name=\'{partition}\' - ORDER BY id, time""" + SAMPLE = """ + SELECT * + FROM trajectory_table + WHERE partition_name=\'{partition}\' + LIMIT 15; + """ + + UPDATE_PARTITION = """ + ALTER TABLE trajectory_table + ADD IF NOT EXISTS PARTITION (partition_name=\'{partition}\'); + """ + + POWER_DEMAND_MODEL = VEHICLE_POWER_DEMAND_SUBQUERY.format('trajectory_table') + POWER_DEMAND_MODEL_DENOISED_ACCEL = """ - SELECT id, time, speed, accel_without_noise, - 1200 * speed * ((CASE WHEN accel_without_noise > 0 THEN 1 ELSE 0 END * (1-0.8) - * accel_without_noise)+0.8 + 9.81 * SIN(road_grade)) - + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 * POW(speed,3) AS power, - 'POWER_DEMAND_MODEL_DENOISED_ACCEL' AS energy_model_id, source_id - FROM trajectory_table - WHERE partition_name=\'{partition}\' - ORDER BY id, time""" + WITH denoised_accel_cte AS ( + SELECT + id, + "time", + speed, + accel_without_noise AS acceleration, + road_grade, + source_id + FROM trajectory_table + ) + {}""".format(VEHICLE_POWER_DEMAND_SUBQUERY.format('denoised_accel_cte')) + POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL = """ - WITH lagged_timestep AS ( - SELECT time, id, speed, acceleration, accel_without_noise, road_grade, source_id, - time - LAG(time, 1) - OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS sim_step, - LAG(speed, 1) - OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_speed, - LAG(acceleration, 1) - OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_accel, - LAG(accel_without_noise, 1) - OVER (PARTITION BY id ORDER BY time ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_accel_denoised - FROM trajectory_table - WHERE partition_name=\'{partition}\'), - speed_denoised_table AS ( - SELECT time, id, speed, acceleration, accel_without_noise, road_grade, source_id, - prev_speed+accel_without_noise*sim_step AS speed_denoised - FROM lagged_timestep) - SELECT id, time, speed_denoised, accel_without_noise, - 1200 * speed_denoised * ((CASE WHEN accel_without_noise > 0 - THEN 1 ELSE 0 END * (1-0.8) * accel_without_noise) + 0.8 + 9.81 - * SIN(road_grade)) + 1200 * 9.81 * 0.005 * speed_denoised + 0.5 * 1.225 - * 2.6 * 0.3 * POW(speed_denoised,3) AS power, - 'POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL' AS energy_model, source_id - FROM speed_denoised_table - ORDER BY id, time""" + WITH lagged_timestep AS ( + SELECT + "time", + id, + accel_without_noise, + road_grade, + source_id, + "time" - LAG("time", 1) + OVER (PARTITION BY id ORDER BY "time" ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS sim_step, + LAG(speed, 1) + OVER (PARTITION BY id ORDER BY "time" ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_speed + FROM trajectory_table + WHERE 1 = 1 + AND partition_name=\'{partition}\' + ), denoised_speed_cte AS ( + SELECT + id, + "time", + prev_speed + accel_without_noise * sim_step AS speed, + accel_without_noise AS acceleration, + road_grade, + source_id + FROM lagged_timestep + ) + {}""".format(VEHICLE_POWER_DEMAND_SUBQUERY.format('denoised_speed_cte')) From d578e6337b117316ce9d0633c7e18070ec27d6dc Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Tue, 19 May 2020 08:52:17 -0700 Subject: [PATCH 59/89] rename vehicle power demand query --- flow/data_pipeline/query.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index c6be5efe4..826c28242 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -6,9 +6,24 @@ tags = {"energy": ["POWER_DEMAND_MODEL", "POWER_DEMAND_MODEL_DENOISED_ACCEL", "POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL"], "analysis": ["POWER_DEMAND_MODEL"]} -# specify the function to calculate the expected result of each query -testing_functions = {"POWER_DEMAND_MODEL": apply_energy_one} - +VEHICLE_POWER_DEMAND_FINAL_SELECT = """ + SELECT + id, + "time", + speed, + acceleration, + road_grade, + 1200 * speed * ( + (CASE WHEN acceleration > 0 THEN 1 ELSE 0 END * (1-0.8) * acceleration) + + 0.8 + 9.81 * SIN(road_grade) + ) + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 * POW(speed,3) AS power, + 'POWER_DEMAND_MODEL' AS energy_model_id, + source_id + FROM {} + WHERE 1 = 1 + AND partition_name=\'{partition}\' + ORDER BY id, "time" + """ class QueryStrings(Enum): """An enumeration of all the pre-defined query strings.""" @@ -25,7 +40,7 @@ class QueryStrings(Enum): ADD IF NOT EXISTS PARTITION (partition_name=\'{partition}\'); """ - POWER_DEMAND_MODEL = VEHICLE_POWER_DEMAND_SUBQUERY.format('trajectory_table') + POWER_DEMAND_MODEL = VEHICLE_POWER_DEMAND_FINAL_SELECT.format('trajectory_table') POWER_DEMAND_MODEL_DENOISED_ACCEL = """ WITH denoised_accel_cte AS ( @@ -38,7 +53,7 @@ class QueryStrings(Enum): source_id FROM trajectory_table ) - {}""".format(VEHICLE_POWER_DEMAND_SUBQUERY.format('denoised_accel_cte')) + {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('denoised_accel_cte')) POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL = """ WITH lagged_timestep AS ( @@ -65,4 +80,4 @@ class QueryStrings(Enum): source_id FROM lagged_timestep ) - {}""".format(VEHICLE_POWER_DEMAND_SUBQUERY.format('denoised_speed_cte')) + {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('denoised_speed_cte')) From 32c052866e2d750fb4e4911c06320cb89ccd3157 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Tue, 19 May 2020 10:44:06 -0700 Subject: [PATCH 60/89] move partition condition to cte's --- flow/data_pipeline/query.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index 826c28242..2eb2146f2 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -20,8 +20,6 @@ 'POWER_DEMAND_MODEL' AS energy_model_id, source_id FROM {} - WHERE 1 = 1 - AND partition_name=\'{partition}\' ORDER BY id, "time" """ @@ -40,7 +38,20 @@ class QueryStrings(Enum): ADD IF NOT EXISTS PARTITION (partition_name=\'{partition}\'); """ - POWER_DEMAND_MODEL = VEHICLE_POWER_DEMAND_FINAL_SELECT.format('trajectory_table') + POWER_DEMAND_MODEL = """ + WITH regular_cte AS ( + SELECT + id, + "time", + speed, + acceleration, + road_grade, + source_id + FROM trajectory_table + WHERE 1 = 1 + AND partition_name=\'{partition}\' + ) + {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('regular_cte')) POWER_DEMAND_MODEL_DENOISED_ACCEL = """ WITH denoised_accel_cte AS ( @@ -52,14 +63,16 @@ class QueryStrings(Enum): road_grade, source_id FROM trajectory_table + WHERE 1 = 1 + AND partition_name=\'{partition}\' ) {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('denoised_accel_cte')) POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL = """ WITH lagged_timestep AS ( SELECT - "time", id, + "time", accel_without_noise, road_grade, source_id, From c7cd96303620e97530bceb9507a085d6e4089cc9 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Tue, 19 May 2020 13:41:17 -0700 Subject: [PATCH 61/89] fix some query string formatting issue --- flow/data_pipeline/query.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index 2eb2146f2..ca59a12b1 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -49,7 +49,7 @@ class QueryStrings(Enum): source_id FROM trajectory_table WHERE 1 = 1 - AND partition_name=\'{partition}\' + AND partition_name=\'{{partition}}\' ) {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('regular_cte')) @@ -64,7 +64,7 @@ class QueryStrings(Enum): source_id FROM trajectory_table WHERE 1 = 1 - AND partition_name=\'{partition}\' + AND partition_name=\'{{partition}}\' ) {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('denoised_accel_cte')) @@ -82,7 +82,7 @@ class QueryStrings(Enum): OVER (PARTITION BY id ORDER BY "time" ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_speed FROM trajectory_table WHERE 1 = 1 - AND partition_name=\'{partition}\' + AND partition_name=\'{{partition}}\' ), denoised_speed_cte AS ( SELECT id, From b5be92ac038b118b4055ef6489612a9836cf00f2 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Tue, 19 May 2020 15:28:54 -0700 Subject: [PATCH 62/89] fix some style issue --- flow/data_pipeline/query.py | 1 + 1 file changed, 1 insertion(+) diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index ca59a12b1..e1f98aaf1 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -23,6 +23,7 @@ ORDER BY id, "time" """ + class QueryStrings(Enum): """An enumeration of all the pre-defined query strings.""" From 6884960aecf8adb4704143b17383fcddd2aa0ffa Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Tue, 19 May 2020 15:56:53 -0700 Subject: [PATCH 63/89] get up to date with i210_dev --- examples/exp_configs/non_rl/highway.py | 40 ++++++---- .../exp_configs/non_rl/i210_subnetwork.py | 2 +- .../exp_configs/templates/sumo/test2.net.xml | 78 +++++-------------- 3 files changed, 48 insertions(+), 72 deletions(-) diff --git a/examples/exp_configs/non_rl/highway.py b/examples/exp_configs/non_rl/highway.py index 1905e2f7f..e7505f2d7 100644 --- a/examples/exp_configs/non_rl/highway.py +++ b/examples/exp_configs/non_rl/highway.py @@ -5,19 +5,25 @@ from flow.core.params import VehicleParams, InFlows from flow.envs.ring.lane_change_accel import ADDITIONAL_ENV_PARAMS from flow.networks.highway import HighwayNetwork, ADDITIONAL_NET_PARAMS -from flow.envs import TestEnv +from flow.envs import LaneChangeAccelEnv vehicles = VehicleParams() vehicles.add( - "human", - num_vehicles=0, - lane_change_params=SumoLaneChangeParams( - lane_change_mode="strategic", - ), - acceleration_controller=(IDMController, { - "a": 0.3, "b": 2.0, "noise": 0.5 - }), - ) + veh_id="human", + acceleration_controller=(IDMController, {}), + lane_change_params=SumoLaneChangeParams( + model="SL2015", + lc_sublane=2.0, + ), + num_vehicles=20) +vehicles.add( + veh_id="human2", + acceleration_controller=(IDMController, {}), + lane_change_params=SumoLaneChangeParams( + model="SL2015", + lc_sublane=2.0, + ), + num_vehicles=20) env_params = EnvParams(additional_params=ADDITIONAL_ENV_PARAMS) @@ -25,7 +31,13 @@ inflow.add( veh_type="human", edge="highway_0", - vehs_per_hour=10800 / 5.0, + probability=0.25, + departLane="free", + departSpeed=20) +inflow.add( + veh_type="human2", + edge="highway_0", + probability=0.25, departLane="free", departSpeed=20) @@ -35,7 +47,7 @@ exp_tag='highway', # name of the flow environment the experiment is running on - env_name=TestEnv, + env_name=LaneChangeAccelEnv, # name of the network class the experiment is running on network=HighwayNetwork, @@ -46,12 +58,12 @@ # sumo-related parameters (see flow.core.params.SumoParams) sim=SumoParams( render=True, - sim_step=0.5 + lateral_resolution=1.0, ), # environment related parameters (see flow.core.params.EnvParams) env=EnvParams( - horizon=4000, + horizon=1500, additional_params=ADDITIONAL_ENV_PARAMS.copy(), ), diff --git a/examples/exp_configs/non_rl/i210_subnetwork.py b/examples/exp_configs/non_rl/i210_subnetwork.py index 8970e6165..3704a7a1c 100644 --- a/examples/exp_configs/non_rl/i210_subnetwork.py +++ b/examples/exp_configs/non_rl/i210_subnetwork.py @@ -117,7 +117,7 @@ sim=SumoParams( sim_step=0.4, render=False, - color_by_speed=False, + color_by_speed=True, use_ballistic=True ), diff --git a/examples/exp_configs/templates/sumo/test2.net.xml b/examples/exp_configs/templates/sumo/test2.net.xml index 16170b917..00e3edcd5 100644 --- a/examples/exp_configs/templates/sumo/test2.net.xml +++ b/examples/exp_configs/templates/sumo/test2.net.xml @@ -1,41 +1,5 @@ - - @@ -4716,24 +4680,24 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + @@ -4837,10 +4801,10 @@ - + - - + + From 7e549be514a427b1877f19cab3ecb603a02c4f50 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Tue, 19 May 2020 21:18:43 -0700 Subject: [PATCH 64/89] update lambda function, change partition into multi-column --- flow/core/experiment.py | 8 +-- flow/data_pipeline/data_pipeline.py | 84 ++++++++------------------- flow/data_pipeline/lambda_function.py | 26 +++------ flow/data_pipeline/query.py | 29 ++++----- flow/data_pipeline/run_query.py | 6 +- flow/visualize/i210_replay.py | 7 ++- 6 files changed, 58 insertions(+), 102 deletions(-) diff --git a/flow/core/experiment.py b/flow/core/experiment.py index 84a34d0e3..01f732379 100755 --- a/flow/core/experiment.py +++ b/flow/core/experiment.py @@ -217,10 +217,10 @@ def rl_actions(*_): if partition_name: if partition_name == "default": - partition_name = source_id[0:3] - partition_name = date.today().isoformat() + " " + partition_name - upload_to_s3('circles.data.pipeline', 'trajectory-output/partition_name={}/{}.csv'.format( - partition_name, upload_file_path.split('/')[-1].split('_')[0]), + partition_name = source_id[-3:] + cur_date = date.today().isoformat() + upload_to_s3('circles.data.pipeline', 'trajectory-output/date={}/partition_name={}/{}.csv'.format( + cur_date, partition_name, upload_file_path.split('/')[-1].split('_upload')[0]), upload_file_path, str(only_query)[2:-2]) # delete the S3-only version of the trajectory file diff --git a/flow/data_pipeline/data_pipeline.py b/flow/data_pipeline/data_pipeline.py index fbd975c5e..111c41994 100644 --- a/flow/data_pipeline/data_pipeline.py +++ b/flow/data_pipeline/data_pipeline.py @@ -2,8 +2,9 @@ import pandas as pd import numpy as np import boto3 -from flow.data_pipeline.query import QueryStrings, testing_functions +from flow.data_pipeline.query import QueryStrings from time import time +from datetime import date def generate_trajectory_table(data_path, extra_info, partition_name): @@ -90,7 +91,7 @@ def upload_to_s3(bucket_name, bucket_key, file_path, only_query): def extra_init(): """Return the dictionary with all the feild pre-populated with empty list.""" - extra_info = {"time": [], "id": [], "x": [], "y": [], "speed": [], "headway": [], "acceleration": [], + extra_info = {"time_step": [], "id": [], "x": [], "y": [], "speed": [], "headway": [], "acceleration": [], "accel_without_noise": [], "realilzed_accel": [], "leader_id": [], "follower_id": [], "leader_rel_speed": [], "road_grade": [], "source_id": []} return extra_info @@ -99,7 +100,7 @@ def extra_init(): def get_extra_info(veh_kernel, extra_info, veh_ids): """Get all the necessary information for the trajectory output from flow.""" for vid in veh_ids: - extra_info["time"].append(veh_kernel.get_timestep(vid) / 1000) + extra_info["time_step"].append(veh_kernel.get_timestep(vid) / 1000) extra_info["id"].append(vid) extra_info["headway"].append(veh_kernel.get_headway(vid)) extra_info["acceleration"].append(veh_kernel.get_accel(vid)) @@ -154,7 +155,7 @@ def get_existing_partitions(self): response = self.client.start_query_execution( QueryString='SHOW PARTITIONS trajectory_table', QueryExecutionContext={ - 'Database': 'simulation' + 'Database': 'circles' }, WorkGroup='primary' ) @@ -164,7 +165,7 @@ def get_existing_partitions(self): QueryExecutionId=response['QueryExecutionId'], MaxResults=1000 ) - return [data['Data'][0]['VarCharValue'].split('=')[-1] for data in response['ResultSet']['Rows']] + return [data['Data'][0]['VarCharValue'] for data in response['ResultSet']['Rows']] def check_status(self, execution_id): """Return the status of the execution with given id. @@ -207,27 +208,30 @@ def wait_for_execution(self, execution_id): return False return True - def update_partition(self, partition): + def update_partition(self, query_date, partition): """Load the given partition to the trajectory_table on Athena. Parameters ---------- + query_date : str + the new partition date that needs to be loaded partition : str the new partition that needs to be loaded """ response = self.client.start_query_execution( - QueryString=QueryStrings['UPDATE_PARTITION'].value.format(partition=partition), + QueryString=QueryStrings['UPDATE_PARTITION'].value.format(date=query_date, partition=partition), QueryExecutionContext={ - 'Database': 'simulation' + 'Database': 'circles' }, WorkGroup='primary' ) if self.wait_for_execution(response['QueryExecutionId']): raise RuntimeError("update partition timed out") - self.existing_partitions.append(partition) + self.existing_partitions.append("date={}/partition_name={}".format(query_date, partition)) return - def run_query(self, query_name, result_location="s3://circles.data.pipeline/query-result/", partition="default"): + def run_query(self, query_name, result_location="s3://circles.data.pipeline/result/", + query_date="today", partition="default"): """Start the execution of a query, does not wait for it to finish. Parameters @@ -236,6 +240,8 @@ def run_query(self, query_name, result_location="s3://circles.data.pipeline/quer name of the query in QueryStrings enum that will be run result_location: str, optional location on the S3 bucket where the result will be stored + query_date : str + name of the partition date to run this query on partition: str, optional name of the partition to run this query on Returns @@ -249,13 +255,16 @@ def run_query(self, query_name, result_location="s3://circles.data.pipeline/quer if query_name not in QueryStrings.__members__: raise ValueError("query not existed: please add it to query.py") - if partition not in self.existing_partitions: - self.update_partition(partition) + if query_date == "today": + query_date = date.today().isoformat() + + if "date={}/partition_name={}".format(query_date, partition) not in self.existing_partitions: + self.update_partition(query_date, partition) response = self.client.start_query_execution( - QueryString=QueryStrings[query_name].value.format(partition=partition), + QueryString=QueryStrings[query_name].value.format(date=query_date, partition=partition), QueryExecutionContext={ - 'Database': 'simulation' + 'Database': 'circles' }, ResultConfiguration={ 'OutputLocation': result_location, @@ -263,50 +272,3 @@ def run_query(self, query_name, result_location="s3://circles.data.pipeline/quer WorkGroup='primary' ) return response['QueryExecutionId'] - -########################################################################### -# Helpers for testing the SQL Queries # -########################################################################### - - -def test_sql_query(query_name): - """Start the execution of a query, does not wait for it to finish. - - Parameters - ---------- - query_name : str - name of the query in QueryStrings enum that will be tested - Raises - ------ - RuntimeError: if timeout - """ - if query_name not in testing_functions: - raise ValueError("no tests supported for this query") - - # Run the respective sql query - queryEngine = AthenaQuery() - execution_id = queryEngine.run_query(query_name, result_location="s3://circles.data.pipeline/" - "query-result/query-test", partition="test") - if queryEngine.wait_for_execution(execution_id): - raise RuntimeError("execution timed out") - - # get the Athena query result from S3 - s3 = boto3.resource("s3") - s3.Bucket("circles.data.pipeline").download_file("query-result/query-test/"+execution_id+".csv", - "data/athena_result.csv") - athena_result = pd.read_csv("data/athena_result.csv") - athena_result = athena_result.sort_values(by=["time", "id"]) - - # get the python expected result - expected_result = pd.read_csv("data/test_data.csv") - expected_result = expected_result.apply(testing_functions[query_name], axis=1, result_type="expand") - expected_result.columns = ["time", "id", "power"] - expected_result = expected_result.sort_values(by=["time", "id"]) - - difference = athena_result["power"] - expected_result["power"] - print("average difference is: " + str(np.mean(difference))) - print("std of difference is: " + str(np.std(difference))) - print("average ratio of difference to expected is: " + - str(np.mean(np.divide(difference, expected_result["power"])))) - difference = pd.DataFrame(difference) - difference.to_csv("./difference.csv") diff --git a/flow/data_pipeline/lambda_function.py b/flow/data_pipeline/lambda_function.py index fd50ba8f5..35dcbfba8 100644 --- a/flow/data_pipeline/lambda_function.py +++ b/flow/data_pipeline/lambda_function.py @@ -1,36 +1,28 @@ """lambda function on AWS Lambda.""" import boto3 from urllib.parse import unquote_plus -from examples.data_pipeline import AthenaQuery -from examples.query import tags +from flow.data_pipeline.data_pipeline import AthenaQuery +from flow.data_pipeline.query import tags s3 = boto3.client('s3') queryEngine = AthenaQuery() def lambda_handler(event, context): - """Invoke by AWS Lambda upon triggered by an event. - - Parameters - ---------- - event : dic < str: dic > - an S3 event - context: - not used - """ for record in event['Records']: bucket = record['s3']['bucket']['name'] key = unquote_plus(record['s3']['object']['key']) + query_date = key.split('/')[-3].split('=')[-1] partition = key.split('/')[-2].split('=')[-1] response = s3.head_object(Bucket=bucket, Key=key) - run_query = response["Metadata"]["run-query"] + required_query = response["Metadata"]["run-query"] if bucket == 'circles.data.pipeline' and 'trajectory-output/' in key: - if run_query == "all": - query_list = tags["analysis"] - elif not run_query: + if required_query == "all": + query_list = tags["energy"] + elif not required_query: break else: - query_list = run_query.split("\', \'") + query_list = required_query.split("\', \'") for query_name in query_list: - queryEngine.run_query(query_name, 's3://circles.data.pipeline/query-result/auto/', partition) + queryEngine.run_query(query_name, 's3://circles.data.pipeline/result/auto/', query_date, partition) diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index e1f98aaf1..1d805279b 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -3,13 +3,12 @@ from flow.data_pipeline.datapipeline_test import apply_energy_one # tags for different queries -tags = {"energy": ["POWER_DEMAND_MODEL", "POWER_DEMAND_MODEL_DENOISED_ACCEL", "POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL"], - "analysis": ["POWER_DEMAND_MODEL"]} +tags = {"energy": ["POWER_DEMAND_MODEL", "POWER_DEMAND_MODEL_DENOISED_ACCEL", "POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL"]} VEHICLE_POWER_DEMAND_FINAL_SELECT = """ SELECT id, - "time", + time_step, speed, acceleration, road_grade, @@ -20,7 +19,7 @@ 'POWER_DEMAND_MODEL' AS energy_model_id, source_id FROM {} - ORDER BY id, "time" + ORDER BY id, time_step """ @@ -30,26 +29,28 @@ class QueryStrings(Enum): SAMPLE = """ SELECT * FROM trajectory_table - WHERE partition_name=\'{partition}\' + WHERE date = \'{date}\' + AND partition_name=\'{partition}\' LIMIT 15; """ UPDATE_PARTITION = """ ALTER TABLE trajectory_table - ADD IF NOT EXISTS PARTITION (partition_name=\'{partition}\'); + ADD IF NOT EXISTS PARTITION (date = \'{date}\', partition_name=\'{partition}\'); """ POWER_DEMAND_MODEL = """ WITH regular_cte AS ( SELECT id, - "time", + time_step, speed, acceleration, road_grade, source_id FROM trajectory_table WHERE 1 = 1 + AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' ) {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('regular_cte')) @@ -58,13 +59,14 @@ class QueryStrings(Enum): WITH denoised_accel_cte AS ( SELECT id, - "time", + time_step, speed, accel_without_noise AS acceleration, road_grade, source_id FROM trajectory_table WHERE 1 = 1 + AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' ) {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('denoised_accel_cte')) @@ -73,21 +75,22 @@ class QueryStrings(Enum): WITH lagged_timestep AS ( SELECT id, - "time", + time_step, accel_without_noise, road_grade, source_id, - "time" - LAG("time", 1) - OVER (PARTITION BY id ORDER BY "time" ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS sim_step, + time_step - LAG(time_step, 1) + OVER (PARTITION BY id ORDER BY time_step ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS sim_step, LAG(speed, 1) - OVER (PARTITION BY id ORDER BY "time" ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_speed + OVER (PARTITION BY id ORDER BY time_step ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_speed FROM trajectory_table WHERE 1 = 1 + AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' ), denoised_speed_cte AS ( SELECT id, - "time", + time_step, prev_speed + accel_without_noise * sim_step AS speed, accel_without_noise AS acceleration, road_grade, diff --git a/flow/data_pipeline/run_query.py b/flow/data_pipeline/run_query.py index ac927c749..1eb802205 100644 --- a/flow/data_pipeline/run_query.py +++ b/flow/data_pipeline/run_query.py @@ -1,6 +1,6 @@ """runner script for invoking query manually.""" import argparse -from flow.data_pipeline.data_pipeline import AthenaQuery, test_sql_query +from flow.data_pipeline.data_pipeline import AthenaQuery from flow.data_pipeline.query import QueryStrings parser = argparse.ArgumentParser(prog="run_query", description="runs query on AWS Athena and stores the result to" @@ -21,7 +21,7 @@ if args.run: execution_ids = [] for query_name in args.run: - execution_ids.append(queryEngine.run_query(query_name, args.result_location, args.partition)) + execution_ids.append(queryEngine.run_query(query_name, args.result_location, partition=args.partition)) print(execution_ids) if args.list_partitions: print(queryEngine.existing_partitions) @@ -33,5 +33,3 @@ if args.list_queries: for q in QueryStrings: print(q) - if args.test_query: - test_sql_query(args.test_query[0]) diff --git a/flow/visualize/i210_replay.py b/flow/visualize/i210_replay.py index 0df23942e..f21808705 100644 --- a/flow/visualize/i210_replay.py +++ b/flow/visualize/i210_replay.py @@ -332,9 +332,10 @@ def replay(args, flow_params, output_dir=None, transfer_test=None, rllib_config= # upload to s3 if asked if args.use_s3: - partition_name = date.today().isoformat() + " " + source_id[0:3] - upload_to_s3('circles.data.pipeline', 'trajectory-output/' + 'partition_name=' + partition_name + '/' - + upload_file_path.split('/')[-1].split('_')[0] + '.csv', + partition_name = source_id[-3:] + cur_date = date.today().isoformat() + upload_to_s3('circles.data.pipeline', 'trajectory-output/date={}/partition_name={}/{}.csv'.format( + cur_date, partition_name, upload_file_path.split('/')[-1].split('_upload')[0]), upload_file_path, str(args.only_query)[2:-2]) # print the location of the emission csv file From a799abda655a821b66828b44669210c8a8dd35ea Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Tue, 19 May 2020 20:45:08 -0700 Subject: [PATCH 65/89] remove dupe imports --- examples/train.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/examples/train.py b/examples/train.py index d9e7dde07..7cb84d361 100644 --- a/examples/train.py +++ b/examples/train.py @@ -124,8 +124,6 @@ def run_model_stablebaseline(flow_params, stable_baselines.* the trained model """ - from stable_baselines.common.vec_env import DummyVecEnv, SubprocVecEnv - from stable_baselines import PPO2 if num_cpus == 1: constructor = env_constructor(params=flow_params, version=0)() @@ -175,12 +173,7 @@ def setup_exps_rllib(flow_params, dict training configuration parameters """ - from ray import tune from ray.tune.registry import register_env - try: - from ray.rllib.agents.agent import get_agent_class - except ImportError: - from ray.rllib.agents.registry import get_agent_class horizon = flow_params['env'].horizon @@ -263,7 +256,6 @@ def on_episode_end(info): def train_rllib(submodule, flags): """Train policies using the PPO algorithm in RLlib.""" - import ray flow_params = submodule.flow_params flow_params['sim'].render = flags.render @@ -412,8 +404,6 @@ def train_h_baselines(flow_params, args, multiagent): def train_stable_baselines(submodule, flags): """Train policies using the PPO algorithm in stable-baselines.""" - from stable_baselines.common.vec_env import DummyVecEnv - from stable_baselines import PPO2 flow_params = submodule.flow_params # Path to the saved files From f4fa42632a13c17b76ba49e73d67d13559f19062 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Tue, 19 May 2020 20:51:14 -0700 Subject: [PATCH 66/89] remove blank lines after docstrings --- examples/train.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/train.py b/examples/train.py index 7cb84d361..5a9ab5903 100644 --- a/examples/train.py +++ b/examples/train.py @@ -124,7 +124,6 @@ def run_model_stablebaseline(flow_params, stable_baselines.* the trained model """ - if num_cpus == 1: constructor = env_constructor(params=flow_params, version=0)() # The algorithms require a vectorized environment to run @@ -256,7 +255,6 @@ def on_episode_end(info): def train_rllib(submodule, flags): """Train policies using the PPO algorithm in RLlib.""" - flow_params = submodule.flow_params flow_params['sim'].render = flags.render policy_graphs = getattr(submodule, "POLICY_GRAPHS", None) @@ -404,7 +402,6 @@ def train_h_baselines(flow_params, args, multiagent): def train_stable_baselines(submodule, flags): """Train policies using the PPO algorithm in stable-baselines.""" - flow_params = submodule.flow_params # Path to the saved files exp_tag = flow_params['exp_tag'] From 2563818e4e31cf61606f53955a7b7aed35557a7b Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Tue, 19 May 2020 20:59:00 -0700 Subject: [PATCH 67/89] add back ray import --- examples/train.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/train.py b/examples/train.py index 5a9ab5903..50720b756 100644 --- a/examples/train.py +++ b/examples/train.py @@ -255,6 +255,8 @@ def on_episode_end(info): def train_rllib(submodule, flags): """Train policies using the PPO algorithm in RLlib.""" + import ray + flow_params = submodule.flow_params flow_params['sim'].render = flags.render policy_graphs = getattr(submodule, "POLICY_GRAPHS", None) From 498e08aa1f35d2c37bb1551b35b5d8c98635afa4 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Tue, 19 May 2020 21:04:56 -0700 Subject: [PATCH 68/89] remove whitespace --- examples/train.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/train.py b/examples/train.py index 50720b756..1689d846f 100644 --- a/examples/train.py +++ b/examples/train.py @@ -256,7 +256,7 @@ def on_episode_end(info): def train_rllib(submodule, flags): """Train policies using the PPO algorithm in RLlib.""" import ray - + flow_params = submodule.flow_params flow_params['sim'].render = flags.render policy_graphs = getattr(submodule, "POLICY_GRAPHS", None) From d7da535e81b50dd7b14b2cbb5c72d8cd65fa1825 Mon Sep 17 00:00:00 2001 From: Brent Zhao Date: Tue, 19 May 2020 21:47:19 -0700 Subject: [PATCH 69/89] style fixed --- flow/data_pipeline/data_pipeline.py | 1 - flow/data_pipeline/query.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/flow/data_pipeline/data_pipeline.py b/flow/data_pipeline/data_pipeline.py index 111c41994..a999b6eb1 100644 --- a/flow/data_pipeline/data_pipeline.py +++ b/flow/data_pipeline/data_pipeline.py @@ -1,6 +1,5 @@ """contains class and helper functions for the data pipeline.""" import pandas as pd -import numpy as np import boto3 from flow.data_pipeline.query import QueryStrings from time import time diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index 1d805279b..c2a64013c 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -80,9 +80,9 @@ class QueryStrings(Enum): road_grade, source_id, time_step - LAG(time_step, 1) - OVER (PARTITION BY id ORDER BY time_step ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS sim_step, + OVER (PARTITION BY id ORDER BY time_step ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS sim_step, LAG(speed, 1) - OVER (PARTITION BY id ORDER BY time_step ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_speed + OVER (PARTITION BY id ORDER BY time_step ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_speed FROM trajectory_table WHERE 1 = 1 AND date = \'{{date}}\' From 3df23123743b183a737adb0c7f29516771f2d353 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Wed, 20 May 2020 11:49:38 -0700 Subject: [PATCH 70/89] specify power demand model names --- flow/data_pipeline/query.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index c2a64013c..a319550e2 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -16,7 +16,7 @@ (CASE WHEN acceleration > 0 THEN 1 ELSE 0 END * (1-0.8) * acceleration) + 0.8 + 9.81 * SIN(road_grade) ) + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 * POW(speed,3) AS power, - 'POWER_DEMAND_MODEL' AS energy_model_id, + \'{}\' AS energy_model_id, source_id FROM {} ORDER BY id, time_step @@ -53,7 +53,7 @@ class QueryStrings(Enum): AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' ) - {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('regular_cte')) + {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL', 'regular_cte')) POWER_DEMAND_MODEL_DENOISED_ACCEL = """ WITH denoised_accel_cte AS ( @@ -69,7 +69,7 @@ class QueryStrings(Enum): AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' ) - {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('denoised_accel_cte')) + {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL_DENOISED_ACCEL', 'denoised_accel_cte')) POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL = """ WITH lagged_timestep AS ( @@ -97,4 +97,4 @@ class QueryStrings(Enum): source_id FROM lagged_timestep ) - {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('denoised_speed_cte')) + {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL', 'denoised_speed_cte')) From 28d4f73c4170c05b8fde403d8a6148347d2d1351 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Sun, 24 May 2020 21:29:11 -0700 Subject: [PATCH 71/89] fix bug in vehicle power demand --- flow/data_pipeline/query.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index a319550e2..bbc0b9709 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -12,9 +12,12 @@ speed, acceleration, road_grade, - 1200 * speed * ( - (CASE WHEN acceleration > 0 THEN 1 ELSE 0 END * (1-0.8) * acceleration) - + 0.8 + 9.81 * SIN(road_grade) + 1200 * speed * MAX(0, ( + CASE + WHEN acceleration > 0 THEN 1 + WHEN acceleration < 0 THEN 0 + ELSE 0.5 + END * (1 - 0.8) + 0.8) * acceleration + 9.81 * SIN(road_grade) ) + 1200 * 9.81 * 0.005 * speed + 0.5 * 1.225 * 2.6 * 0.3 * POW(speed,3) AS power, \'{}\' AS energy_model_id, source_id From 077983206ea4454190ffa98987dc81e4ba5d2954 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Wed, 20 May 2020 21:31:20 -0700 Subject: [PATCH 72/89] Add several accelerations (with/without noise, with/without failsafes) to custom output --- flow/controllers/base_controller.py | 18 +++++++++------ flow/core/kernel/vehicle/base.py | 20 ++++++++++++++-- flow/core/kernel/vehicle/traci.py | 36 +++++++++++++++++++++++------ flow/data_pipeline/data_pipeline.py | 19 ++++++++++----- 4 files changed, 71 insertions(+), 22 deletions(-) diff --git a/flow/controllers/base_controller.py b/flow/controllers/base_controller.py index 7adcdf310..c417bb73a 100755 --- a/flow/controllers/base_controller.py +++ b/flow/controllers/base_controller.py @@ -88,8 +88,10 @@ def get_action(self, env): float the modified form of the acceleration """ - # clear the current stored accel_without_noise of this vehicle None - env.k.vehicle.update_accel_without_noise(self.veh_id, None) + # clear the current stored accel_no_noise_no_failsafe of this vehicle None + env.k.vehicle.update_accel_no_noise_no_failsafe(self.veh_id, None) + env.k.vehicle.update_accel_no_noise_with_failsafe(self.veh_id, None) + env.k.vehicle.update_accel_with_noise_no_failsafe(self.veh_id, None) # this is to avoid abrupt decelerations when a vehicle has just entered # a network and it's data is still not subscribed @@ -110,23 +112,25 @@ def get_action(self, env): # store the acceleration without noise to each vehicle # run fail safe if requested - accel_without_noise = accel + env.k.vehicle.update_accel_no_noise_no_failsafe(self.veh_id, accel) if self.fail_safe == 'instantaneous': - accel_without_noise = self.get_safe_action_instantaneous(env, accel_without_noise) + accel_no_noise_with_failsafe = self.get_safe_action_instantaneous(env, accel) elif self.fail_safe == 'safe_velocity': - accel_without_noise = self.get_safe_velocity_action(env, accel_without_noise) - env.k.vehicle.update_accel_without_noise(self.veh_id, accel_without_noise) + accel_no_noise_with_failsafe = self.get_safe_velocity_action(env, accel) + env.k.vehicle.update_accel_no_noise_with_failsafe(self.veh_id, accel_no_noise_with_failsafe) # add noise to the accelerations, if requested if self.accel_noise > 0: accel += np.sqrt(env.sim_step) * np.random.normal(0, self.accel_noise) + env.k.vehicle.update_accel_with_noise_no_failsafe(self.veh_id, accel) # run the fail-safes, if requested if self.fail_safe == 'instantaneous': accel = self.get_safe_action_instantaneous(env, accel) elif self.fail_safe == 'safe_velocity': accel = self.get_safe_velocity_action(env, accel) - + env.k.vehicle.update_accel_with_noise_with_failsafe(self.veh_id, accel) + return accel def get_safe_action_instantaneous(self, env, action): diff --git a/flow/core/kernel/vehicle/base.py b/flow/core/kernel/vehicle/base.py index 20a11cf99..eb88ff397 100644 --- a/flow/core/kernel/vehicle/base.py +++ b/flow/core/kernel/vehicle/base.py @@ -693,7 +693,15 @@ def get_accel(self, veh_id): """Return the acceleration of vehicle with veh_id.""" raise NotImplementedError - def update_accel_without_noise(self, veh_id, accel_without_noise): + def update_accel_no_noise_no_failsafe(self, veh_id, accel_no_noise_no_failsafe): + """Update stored acceleration without noise of vehicle with veh_id.""" + raise NotImplementedError + + def update_accel_no_noise_with_failsafe(self, veh_id, accel_no_noise_with_failsafe): + """Update stored acceleration without noise of vehicle with veh_id.""" + raise NotImplementedError + + def update_accel_with_noise_no_failsafe(self, veh_id, accel_with_noise_no_failsafe): """Update stored acceleration without noise of vehicle with veh_id.""" raise NotImplementedError @@ -705,7 +713,15 @@ def get_2D_position(self, veh_id, error=-1001): """Return (x, y) position of vehicle with veh_id.""" raise NotImplementedError - def get_accel_without_noise(self, veh_id): + def get_accel_no_noise_no_failsafe(self, veh_id): + """Return the acceleration without noise of vehicle with veh_id.""" + raise NotImplementedError + + def get_accel_no_noise_with_failsafe(self, veh_id): + """Return the acceleration without noise of vehicle with veh_id.""" + raise NotImplementedError + + def get_accel_with_noise_no_failsafe(self, veh_id): """Return the acceleration without noise of vehicle with veh_id.""" raise NotImplementedError diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index 824ec4b0c..344bcfde2 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -113,7 +113,9 @@ def initialize(self, vehicles): self.__vehicles[veh_id] = dict() self.__vehicles[veh_id]['type'] = typ['veh_id'] self.__vehicles[veh_id]['initial_speed'] = typ['initial_speed'] - self.__vehicles[veh_id]["accel_without_noise"] = None + self.__vehicles[veh_id]["accel_no_noise_no_failsafe"] = None + self.__vehicles[veh_id]["accel_no_noise_with_failsafe"] = None + self.__vehicles[veh_id]["accel_with_noise_no_failsafe"] = None self.num_vehicles += 1 if typ['acceleration_controller'][0] == RLController: self.num_rl_vehicles += 1 @@ -1138,15 +1140,35 @@ def get_accel(self, veh_id): self.__vehicles[veh_id]["accel"] = None return self.__vehicles[veh_id]["accel"] - def update_accel_without_noise(self, veh_id, accel_without_noise): + def update_accel_no_noise_no_failsafe(self, veh_id, accel_no_noise_no_failsafe): """See parent class.""" - self.__vehicles[veh_id]["accel_without_noise"] = accel_without_noise + self.__vehicles[veh_id]["accel_no_noise_no_failsafe"] = accel_no_noise_no_failsafe - def get_accel_without_noise(self, veh_id): + def update_accel_no_noise_with_failsafe(self, veh_id, accel_no_noise_with_failsafe): """See parent class.""" - if "accel_without_noise" not in self.__vehicles[veh_id]: - self.__vehicles[veh_id]["accel_without_noise"] = None - return self.__vehicles[veh_id]["accel_without_noise"] + self.__vehicles[veh_id]["accel_no_noise_with_failsafe"] = accel_no_noise_with_failsafe + + def update_accel_with_noise_no_failsafe(self, veh_id, accel_with_noise_no_failsafe): + """See parent class.""" + self.__vehicles[veh_id]["accel_with_noise_no_failsafe"] = accel_with_noise_no_failsafe + + def get_accel_no_noise_no_failsafe(self, veh_id): + """See parent class.""" + if "accel_no_noise_no_failsafe" not in self.__vehicles[veh_id]: + self.__vehicles[veh_id]["accel_no_noise_no_failsafe"] = None + return self.__vehicles[veh_id]["accel_no_noise_no_failsafe"] + + def get_accel_no_noise_with_failsafe(self, veh_id): + """See parent class.""" + if "accel_no_noise_with_failsafe" not in self.__vehicles[veh_id]: + self.__vehicles[veh_id]["accel_no_noise_with_failsafe"] = None + return self.__vehicles[veh_id]["accel_no_noise_with_failsafe"] + + def get_accel_with_noise_no_failsafe(self, veh_id): + """See parent class.""" + if "accel_with_noise_no_failsafe" not in self.__vehicles[veh_id]: + self.__vehicles[veh_id]["accel_with_noise_no_failsafe"] = None + return self.__vehicles[veh_id]["accel_with_noise_no_failsafe"] def get_realized_accel(self, veh_id): """See parent class.""" diff --git a/flow/data_pipeline/data_pipeline.py b/flow/data_pipeline/data_pipeline.py index a999b6eb1..11d85cb0d 100644 --- a/flow/data_pipeline/data_pipeline.py +++ b/flow/data_pipeline/data_pipeline.py @@ -89,9 +89,11 @@ def upload_to_s3(bucket_name, bucket_key, file_path, only_query): def extra_init(): - """Return the dictionary with all the feild pre-populated with empty list.""" - extra_info = {"time_step": [], "id": [], "x": [], "y": [], "speed": [], "headway": [], "acceleration": [], - "accel_without_noise": [], "realilzed_accel": [], "leader_id": [], "follower_id": [], + """Return the dictionary with all the field pre-populated with empty list.""" + extra_info = {"time_step": [], "id": [], "x": [], "y": [], "speed": [], "headway": [], + "target_accel_with_noise_with_failsafe": [], "target_accel_no_noise_no_failsafe": [], + "target_accel_with_noise_no_failsafe": [], "target_accel_no_noise_with_failsafe": [], + "realized_accel": [], "leader_id": [], "follower_id": [], "leader_rel_speed": [], "road_grade": [], "source_id": []} return extra_info @@ -102,13 +104,18 @@ def get_extra_info(veh_kernel, extra_info, veh_ids): extra_info["time_step"].append(veh_kernel.get_timestep(vid) / 1000) extra_info["id"].append(vid) extra_info["headway"].append(veh_kernel.get_headway(vid)) - extra_info["acceleration"].append(veh_kernel.get_accel(vid)) + extra_info["target_accel_with_noise_with_failsafe"].append(veh_kernel.get_accel(vid)) extra_info["leader_id"].append(veh_kernel.get_leader(vid)) extra_info["follower_id"].append(veh_kernel.get_follower(vid)) extra_info["leader_rel_speed"].append(veh_kernel.get_speed( veh_kernel.get_leader(vid)) - veh_kernel.get_speed(vid)) - extra_info["accel_without_noise"].append(veh_kernel.get_accel_without_noise(vid)) - extra_info["realilzed_accel"].append(veh_kernel.get_realized_accel(vid)) + extra_info["target_accel_no_noise_no_failsafe"].append( + veh_kernel.get_accel_no_noise_no_failsafe(vid)) + extra_info["target_accel_with_noise_no_failsafe"].append( + veh_kernel.get_accel_with_noise_no_failsafe(vid)) + extra_info["target_accel_no_noise_with_failsafe"].append( + veh_kernel.get_accel_no_noise_with_failsafe(vid)) + extra_info["realized_accel"].append(veh_kernel.get_realized_accel(vid)) extra_info["road_grade"].append(veh_kernel.get_road_grade(vid)) position = veh_kernel.get_2d_position(vid) extra_info["x"].append(position[0]) From b3f15a3c2a4527b59139ed1d9198f68110c93270 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Wed, 20 May 2020 21:44:15 -0700 Subject: [PATCH 73/89] update queries with new column names --- flow/data_pipeline/query.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index bbc0b9709..d3f136a72 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -31,14 +31,14 @@ class QueryStrings(Enum): SAMPLE = """ SELECT * - FROM trajectory_table + FROM fact_vehicle_trace WHERE date = \'{date}\' AND partition_name=\'{partition}\' LIMIT 15; """ UPDATE_PARTITION = """ - ALTER TABLE trajectory_table + ALTER TABLE fact_vehicle_trace ADD IF NOT EXISTS PARTITION (date = \'{date}\', partition_name=\'{partition}\'); """ @@ -48,10 +48,10 @@ class QueryStrings(Enum): id, time_step, speed, - acceleration, + target_accel_with_noise_with_failsafe AS acceleration, road_grade, source_id - FROM trajectory_table + FROM fact_vehicle_trace WHERE 1 = 1 AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' @@ -64,10 +64,10 @@ class QueryStrings(Enum): id, time_step, speed, - accel_without_noise AS acceleration, + target_accel_no_noise_with_failsafe AS acceleration, road_grade, source_id - FROM trajectory_table + FROM fact_vehicle_trace WHERE 1 = 1 AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' @@ -79,14 +79,14 @@ class QueryStrings(Enum): SELECT id, time_step, - accel_without_noise, + target_accel_no_noise_with_failsafe, road_grade, source_id, time_step - LAG(time_step, 1) OVER (PARTITION BY id ORDER BY time_step ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS sim_step, LAG(speed, 1) OVER (PARTITION BY id ORDER BY time_step ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_speed - FROM trajectory_table + FROM fact_vehicle_trace WHERE 1 = 1 AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' @@ -94,8 +94,8 @@ class QueryStrings(Enum): SELECT id, time_step, - prev_speed + accel_without_noise * sim_step AS speed, - accel_without_noise AS acceleration, + prev_speed + target_accel_no_noise_with_failsafe * sim_step AS speed, + target_accel_no_noise_with_failsafe AS acceleration, road_grade, source_id FROM lagged_timestep From d66a0ab6542a6075ce9495991790afadf8a4d3e4 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Wed, 20 May 2020 21:47:44 -0700 Subject: [PATCH 74/89] fix flake8 issues --- flow/controllers/base_controller.py | 2 +- flow/data_pipeline/query.py | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/flow/controllers/base_controller.py b/flow/controllers/base_controller.py index c417bb73a..3f6a0f4ae 100755 --- a/flow/controllers/base_controller.py +++ b/flow/controllers/base_controller.py @@ -130,7 +130,7 @@ def get_action(self, env): elif self.fail_safe == 'safe_velocity': accel = self.get_safe_velocity_action(env, accel) env.k.vehicle.update_accel_with_noise_with_failsafe(self.veh_id, accel) - + return accel def get_safe_action_instantaneous(self, env, action): diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index d3f136a72..b8cd24b55 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -3,7 +3,13 @@ from flow.data_pipeline.datapipeline_test import apply_energy_one # tags for different queries -tags = {"energy": ["POWER_DEMAND_MODEL", "POWER_DEMAND_MODEL_DENOISED_ACCEL", "POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL"]} +tags = { + "energy": [ + "POWER_DEMAND_MODEL", + "POWER_DEMAND_MODEL_DENOISED_ACCEL", + "POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL" + ] + } VEHICLE_POWER_DEMAND_FINAL_SELECT = """ SELECT @@ -56,7 +62,8 @@ class QueryStrings(Enum): AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' ) - {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL', 'regular_cte')) + {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL', + 'regular_cte')) POWER_DEMAND_MODEL_DENOISED_ACCEL = """ WITH denoised_accel_cte AS ( @@ -72,7 +79,8 @@ class QueryStrings(Enum): AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' ) - {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL_DENOISED_ACCEL', 'denoised_accel_cte')) + {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL_DENOISED_ACCEL', + 'denoised_accel_cte')) POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL = """ WITH lagged_timestep AS ( @@ -100,4 +108,5 @@ class QueryStrings(Enum): source_id FROM lagged_timestep ) - {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL', 'denoised_speed_cte')) + {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL', + 'denoised_speed_cte')) From 38af177a02bd47cc691201083f4192f61fa2dedc Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Wed, 20 May 2020 21:51:46 -0700 Subject: [PATCH 75/89] remove trailing whitespaces --- flow/data_pipeline/query.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index b8cd24b55..57def52de 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -5,8 +5,8 @@ # tags for different queries tags = { "energy": [ - "POWER_DEMAND_MODEL", - "POWER_DEMAND_MODEL_DENOISED_ACCEL", + "POWER_DEMAND_MODEL", + "POWER_DEMAND_MODEL_DENOISED_ACCEL", "POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL" ] } @@ -62,7 +62,7 @@ class QueryStrings(Enum): AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' ) - {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL', + {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL', 'regular_cte')) POWER_DEMAND_MODEL_DENOISED_ACCEL = """ @@ -108,5 +108,5 @@ class QueryStrings(Enum): source_id FROM lagged_timestep ) - {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL', + {}""".format(VEHICLE_POWER_DEMAND_FINAL_SELECT.format('POWER_DEMAND_MODEL_DENOISED_ACCEL_VEL', 'denoised_speed_cte')) From fceedf874599c68852aa8feb016921b12abd358e Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Wed, 20 May 2020 21:31:20 -0700 Subject: [PATCH 76/89] Add several accelerations (with/without noise, with/without failsafes) to custom output --- flow/core/kernel/vehicle/base.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/flow/core/kernel/vehicle/base.py b/flow/core/kernel/vehicle/base.py index eb88ff397..ed53773cb 100644 --- a/flow/core/kernel/vehicle/base.py +++ b/flow/core/kernel/vehicle/base.py @@ -709,10 +709,6 @@ def get_2d_position(self, veh_id, error=-1001): """Return (x, y) position of vehicle with veh_id.""" raise NotImplementedError - def get_2D_position(self, veh_id, error=-1001): - """Return (x, y) position of vehicle with veh_id.""" - raise NotImplementedError - def get_accel_no_noise_no_failsafe(self, veh_id): """Return the acceleration without noise of vehicle with veh_id.""" raise NotImplementedError From df182ad6c820b1fd2b05db9ce6a305aee248cec5 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Sun, 24 May 2020 23:20:29 -0700 Subject: [PATCH 77/89] fix accel with noise with failsafe output --- flow/controllers/base_controller.py | 1 + flow/core/kernel/vehicle/base.py | 20 ++++++++++++++------ flow/core/kernel/vehicle/traci.py | 10 ++++++++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/flow/controllers/base_controller.py b/flow/controllers/base_controller.py index 3f6a0f4ae..1169ce5b8 100755 --- a/flow/controllers/base_controller.py +++ b/flow/controllers/base_controller.py @@ -113,6 +113,7 @@ def get_action(self, env): # store the acceleration without noise to each vehicle # run fail safe if requested env.k.vehicle.update_accel_no_noise_no_failsafe(self.veh_id, accel) + accel_no_noise_with_failsafe = accel if self.fail_safe == 'instantaneous': accel_no_noise_with_failsafe = self.get_safe_action_instantaneous(env, accel) elif self.fail_safe == 'safe_velocity': diff --git a/flow/core/kernel/vehicle/base.py b/flow/core/kernel/vehicle/base.py index ed53773cb..f6f8ee382 100644 --- a/flow/core/kernel/vehicle/base.py +++ b/flow/core/kernel/vehicle/base.py @@ -694,15 +694,19 @@ def get_accel(self, veh_id): raise NotImplementedError def update_accel_no_noise_no_failsafe(self, veh_id, accel_no_noise_no_failsafe): - """Update stored acceleration without noise of vehicle with veh_id.""" + """Update stored acceleration without noise without failsafe of vehicle with veh_id.""" raise NotImplementedError def update_accel_no_noise_with_failsafe(self, veh_id, accel_no_noise_with_failsafe): - """Update stored acceleration without noise of vehicle with veh_id.""" + """Update stored acceleration without noise with failsafe of vehicle with veh_id.""" raise NotImplementedError def update_accel_with_noise_no_failsafe(self, veh_id, accel_with_noise_no_failsafe): - """Update stored acceleration without noise of vehicle with veh_id.""" + """Update stored acceleration with noise without failsafe of vehicle with veh_id.""" + raise NotImplementedError + + def update_accel_with_noise_with_failsafe(self, veh_id, accel_with_noise_with_failsafe): + """Update stored acceleration with noise with failsafe of vehicle with veh_id.""" raise NotImplementedError def get_2d_position(self, veh_id, error=-1001): @@ -710,15 +714,19 @@ def get_2d_position(self, veh_id, error=-1001): raise NotImplementedError def get_accel_no_noise_no_failsafe(self, veh_id): - """Return the acceleration without noise of vehicle with veh_id.""" + """Return the acceleration without noise without failsafe of vehicle with veh_id.""" raise NotImplementedError def get_accel_no_noise_with_failsafe(self, veh_id): - """Return the acceleration without noise of vehicle with veh_id.""" + """Return the acceleration without noise with failsafe of vehicle with veh_id.""" raise NotImplementedError def get_accel_with_noise_no_failsafe(self, veh_id): - """Return the acceleration without noise of vehicle with veh_id.""" + """Return the acceleration with noise without failsafe of vehicle with veh_id.""" + raise NotImplementedError + + def get_accel_with_noise_with_failsafe(self, veh_id): + """Return the acceleration with noise with failsafe of vehicle with veh_id.""" raise NotImplementedError def get_realized_accel(self, veh_id): diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index 344bcfde2..5de35956f 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -1152,6 +1152,10 @@ def update_accel_with_noise_no_failsafe(self, veh_id, accel_with_noise_no_failsa """See parent class.""" self.__vehicles[veh_id]["accel_with_noise_no_failsafe"] = accel_with_noise_no_failsafe + def update_accel_with_noise_with_failsafe(self, veh_id, accel_with_noise_with_failsafe): + """See parent class.""" + self.__vehicles[veh_id]["accel_with_noise_with_failsafe"] = accel_with_noise_with_failsafe + def get_accel_no_noise_no_failsafe(self, veh_id): """See parent class.""" if "accel_no_noise_no_failsafe" not in self.__vehicles[veh_id]: @@ -1170,6 +1174,12 @@ def get_accel_with_noise_no_failsafe(self, veh_id): self.__vehicles[veh_id]["accel_with_noise_no_failsafe"] = None return self.__vehicles[veh_id]["accel_with_noise_no_failsafe"] + def get_accel_with_noise_with_failsafe(self, veh_id): + """See parent class.""" + if "accel_with_noise_with_failsafe" not in self.__vehicles[veh_id]: + self.__vehicles[veh_id]["accel_with_noise_with_failsafe"] = None + return self.__vehicles[veh_id]["accel_with_noise_with_failsafe"] + def get_realized_accel(self, veh_id): """See parent class.""" return (self.get_speed(veh_id) - self.get_previous_speed(veh_id)) / self.sim_step From d88840578f88c70da428d829b7b9d22024d6bf52 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Mon, 25 May 2020 16:57:52 -0700 Subject: [PATCH 78/89] fix rebase errors --- flow/controllers/base_controller.py | 1 + flow/core/kernel/vehicle/traci.py | 8 -------- flow/data_pipeline/data_pipeline.py | 10 ---------- 3 files changed, 1 insertion(+), 18 deletions(-) diff --git a/flow/controllers/base_controller.py b/flow/controllers/base_controller.py index 1169ce5b8..ac29bca2e 100755 --- a/flow/controllers/base_controller.py +++ b/flow/controllers/base_controller.py @@ -92,6 +92,7 @@ def get_action(self, env): env.k.vehicle.update_accel_no_noise_no_failsafe(self.veh_id, None) env.k.vehicle.update_accel_no_noise_with_failsafe(self.veh_id, None) env.k.vehicle.update_accel_with_noise_no_failsafe(self.veh_id, None) + env.k.vehicle.update_accel_with_noise_with_failsafe(self.veh_id, None) # this is to avoid abrupt decelerations when a vehicle has just entered # a network and it's data is still not subscribed diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index 5de35956f..1c0b5f19b 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -223,14 +223,6 @@ def update(self, reset): self.num_not_departed += sim_obs[tc.VAR_LOADED_VEHICLES_NUMBER] - \ sim_obs[tc.VAR_DEPARTED_VEHICLES_NUMBER] - # update the number of not departed vehicles - self.num_not_departed += sim_obs[tc.VAR_LOADED_VEHICLES_NUMBER] - \ - sim_obs[tc.VAR_DEPARTED_VEHICLES_NUMBER] - - # update the number of not departed vehicles - self.num_not_departed += sim_obs[tc.VAR_LOADED_VEHICLES_NUMBER] - \ - sim_obs[tc.VAR_DEPARTED_VEHICLES_NUMBER] - # update the "headway", "leader", and "follower" variables for veh_id in self.__ids: try: diff --git a/flow/data_pipeline/data_pipeline.py b/flow/data_pipeline/data_pipeline.py index 11d85cb0d..aea9b349c 100644 --- a/flow/data_pipeline/data_pipeline.py +++ b/flow/data_pipeline/data_pipeline.py @@ -88,16 +88,6 @@ def upload_to_s3(bucket_name, bucket_key, file_path, only_query): return -def extra_init(): - """Return the dictionary with all the field pre-populated with empty list.""" - extra_info = {"time_step": [], "id": [], "x": [], "y": [], "speed": [], "headway": [], - "target_accel_with_noise_with_failsafe": [], "target_accel_no_noise_no_failsafe": [], - "target_accel_with_noise_no_failsafe": [], "target_accel_no_noise_with_failsafe": [], - "realized_accel": [], "leader_id": [], "follower_id": [], - "leader_rel_speed": [], "road_grade": [], "source_id": []} - return extra_info - - def get_extra_info(veh_kernel, extra_info, veh_ids): """Get all the necessary information for the trajectory output from flow.""" for vid in veh_ids: From 69f6f5536a3be4d885652471c3008da258e58416 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Mon, 25 May 2020 17:57:41 -0700 Subject: [PATCH 79/89] rm deleted file --- flow/data_pipeline/datapipeline_test.py | 37 ------------------------- 1 file changed, 37 deletions(-) delete mode 100644 flow/data_pipeline/datapipeline_test.py diff --git a/flow/data_pipeline/datapipeline_test.py b/flow/data_pipeline/datapipeline_test.py deleted file mode 100644 index 0e1a50518..000000000 --- a/flow/data_pipeline/datapipeline_test.py +++ /dev/null @@ -1,37 +0,0 @@ -"""functions that calculates the expected result for testing.""" -import math - -# Vehicle Mass -M = 1200 -# Gravity -g = 9.81 -# Density of Air -ro_air = 1.225 -# Rolling resistance coefficient -C_r = .005 -# Aerodynamic drag coefficient -C_a = 0.3 -# Vehicle Cross sectional Area -A = 2.6 -# Road grade -theta = 0 - - -def heavyside(inp): - """Return 1 if input is positive.""" - return 0 if inp <= 0 else 1 - - -def calculate_power(mu, acceleration, M=M, g=g, theta=theta, C_r=C_r, ro_air=ro_air, A=A, C_a=C_a): - """Calculate the expected power for POWER_DEMAND_MODEL query.""" - acceleration = (0.8 + ((1 - 0.8) * heavyside(acceleration)) * acceleration) - accel_and_slope = M * mu * (acceleration + g * math.sin(theta)) - rolling_friction = M * g * C_r * mu - air_drag = .5 * ro_air * A * C_a * mu**3 - power = accel_and_slope + rolling_friction + air_drag - return power - - -def apply_energy_one(row): - """Apply the power calculation to a row of the dataframe.""" - return [row[0], row[1], calculate_power(row[4], row[6])] From 4f2f23ec7d47bff699baeac9bf8810af68f2f465 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Mon, 25 May 2020 17:58:58 -0700 Subject: [PATCH 80/89] add return carriage to eof --- examples/exp_configs/non_rl/i210_subnetwork.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/exp_configs/non_rl/i210_subnetwork.py b/examples/exp_configs/non_rl/i210_subnetwork.py index 3704a7a1c..25565bb49 100644 --- a/examples/exp_configs/non_rl/i210_subnetwork.py +++ b/examples/exp_configs/non_rl/i210_subnetwork.py @@ -163,4 +163,4 @@ "avg_density": lambda env: 5 * 1000 * len(env.k.vehicle.get_ids_by_edge( edge_id)) / (env.k.network.edge_length(edge_id) * env.k.network.num_lanes(edge_id)), -} \ No newline at end of file +} From d2ba0694ef7cf0e4f6c913d4e855011fbcdc76e2 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Mon, 25 May 2020 18:00:46 -0700 Subject: [PATCH 81/89] revert accidental change --- flow/core/experiment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/core/experiment.py b/flow/core/experiment.py index f25a9fcac..779fdb0f4 100755 --- a/flow/core/experiment.py +++ b/flow/core/experiment.py @@ -175,7 +175,7 @@ def rl_actions(*_): for (key, lambda_func) in self.custom_callables.items(): custom_vals[key].append(lambda_func(self.env)) - if done: + if type(done) is dict and done['__all__'] or type(done) is not dict and done: break # Store the information from the run in info_dict. From 8eee7722bc28ae05ac330e741e33ee9b659391a2 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Mon, 25 May 2020 18:03:02 -0700 Subject: [PATCH 82/89] rename trajectory table --- flow/data_pipeline/query.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/flow/data_pipeline/query.py b/flow/data_pipeline/query.py index e8ac34abc..b6e66fcec 100644 --- a/flow/data_pipeline/query.py +++ b/flow/data_pipeline/query.py @@ -36,14 +36,14 @@ class QueryStrings(Enum): SAMPLE = """ SELECT * - FROM fact_vehicle_trace + FROM trajectory_table WHERE date = \'{date}\' AND partition_name=\'{partition}\' LIMIT 15; """ UPDATE_PARTITION = """ - ALTER TABLE fact_vehicle_trace + ALTER TABLE trajectory_table ADD IF NOT EXISTS PARTITION (date = \'{date}\', partition_name=\'{partition}\'); """ @@ -56,7 +56,7 @@ class QueryStrings(Enum): target_accel_with_noise_with_failsafe AS acceleration, road_grade, source_id - FROM fact_vehicle_trace + FROM trajectory_table WHERE 1 = 1 AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' @@ -73,7 +73,7 @@ class QueryStrings(Enum): target_accel_no_noise_with_failsafe AS acceleration, road_grade, source_id - FROM fact_vehicle_trace + FROM trajectory_table WHERE 1 = 1 AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' @@ -93,7 +93,7 @@ class QueryStrings(Enum): OVER (PARTITION BY id ORDER BY time_step ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS sim_step, LAG(speed, 1) OVER (PARTITION BY id ORDER BY time_step ASC ROWS BETWEEN 1 PRECEDING and CURRENT ROW) AS prev_speed - FROM fact_vehicle_trace + FROM trajectory_table WHERE 1 = 1 AND date = \'{{date}}\' AND partition_name=\'{{partition}}\' From 3c6dcf71c0ac4219e13da3a3a58471a69dfc88d1 Mon Sep 17 00:00:00 2001 From: Yasharzf Date: Tue, 26 May 2020 13:38:08 -0700 Subject: [PATCH 83/89] added apply acceleratino function which uses setSpeed() method instead of slowDown() --- flow/core/kernel/network/flow_params.json | 2 ++ flow/core/kernel/vehicle/base.py | 17 +++++++++++++++++ flow/core/kernel/vehicle/traci.py | 14 ++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 flow/core/kernel/network/flow_params.json diff --git a/flow/core/kernel/network/flow_params.json b/flow/core/kernel/network/flow_params.json new file mode 100644 index 000000000..c70a22e63 --- /dev/null +++ b/flow/core/kernel/network/flow_params.json @@ -0,0 +1,2 @@ +{ + "env": \ No newline at end of file diff --git a/flow/core/kernel/vehicle/base.py b/flow/core/kernel/vehicle/base.py index 647ef37fe..21edd8d4d 100644 --- a/flow/core/kernel/vehicle/base.py +++ b/flow/core/kernel/vehicle/base.py @@ -124,6 +124,23 @@ def remove(self, veh_id): def apply_acceleration(self, veh_id, acc): """Apply the acceleration requested by a vehicle in the simulator. + In SUMO, this function applies slowDown method which applies smoothing. + + Parameters + ---------- + veh_id : str or list of str + list of vehicle identifiers + acc : float or array_like + requested accelerations from the vehicles + """ + raise NotImplementedError + + def apply_acceleration_not_smooth(self, veh_id, acc): + """Apply the acceleration requested by a vehicle in the simulator. + + In SUMO, this function applies setSpeed method which doesn't apply + smoothing. + Parameters ---------- veh_id : str or list of str diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index 2a4e06257..b56e36ae0 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -964,6 +964,20 @@ def apply_acceleration(self, veh_ids, acc): next_vel = max([this_vel + acc[i] * self.sim_step, 0]) self.kernel_api.vehicle.slowDown(vid, next_vel, 1e-3) + def apply_acceleration_not_smooth(self, veh_ids, acc): + """See parent class.""" + # to hand the case of a single vehicle + if type(veh_ids) == str: + veh_ids = [veh_ids] + acc = [acc] + + for i, vid in enumerate(veh_ids): + if acc[i] is not None and vid in self.get_ids(): + self.__vehicles[vid]["accel"] = acc[i] + this_vel = self.get_speed(vid) + next_vel = max([this_vel + acc[i] * self.sim_step, 0]) + self.kernel_api.vehicle.setSpeed(vid, next_vel) + def apply_lane_change(self, veh_ids, direction): """See parent class.""" # to hand the case of a single vehicle From ddf6a2435d0c2ca7eafe0dd6292ec574626bd397 Mon Sep 17 00:00:00 2001 From: Yasharzf Date: Tue, 26 May 2020 14:12:23 -0700 Subject: [PATCH 84/89] added failsafe methods for max accel/decel and speed limit, and all --- flow/controllers/base_controller.py | 78 +++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 3 deletions(-) diff --git a/flow/controllers/base_controller.py b/flow/controllers/base_controller.py index 7adcdf310..0984349d3 100755 --- a/flow/controllers/base_controller.py +++ b/flow/controllers/base_controller.py @@ -34,7 +34,7 @@ class BaseController: delay : int delay in applying the action (time) fail_safe : str - Should be either "instantaneous" or "safe_velocity" + Should be "instantaneous", "safe_velocity", "feasible_accel", or "all" noise : double variance of the gaussian from which to sample a noisy acceleration """ @@ -75,8 +75,10 @@ def get_action(self, env): time step. This method also augments the controller with the desired level of - stochastic noise, and utlizes the "instantaneous" or "safe_velocity" - failsafes if requested. + stochastic noise, and utlizes the "instantaneous", "safe_velocity", + "feasible_accel", or "all" failsafes if requested. The "all" failsafe + performs all three failsafes with this order: 1)"safe_velocity", + 2) "feasible_accel", 3) "instantaneous". Parameters ---------- @@ -115,6 +117,13 @@ def get_action(self, env): accel_without_noise = self.get_safe_action_instantaneous(env, accel_without_noise) elif self.fail_safe == 'safe_velocity': accel_without_noise = self.get_safe_velocity_action(env, accel_without_noise) + elif self.fail_safe == 'feasible_accel': + accel_without_noise = self.get_feasible_action(accel_without_noise) + elif self.fail_safe == 'all': + accel_without_noise = self.get_safe_velocity_action(env, accel_without_noise) + accel_without_noise = self.get_feasible_action(accel_without_noise) + accel_without_noise = self.get_safe_action_instantaneous(env, accel_without_noise) + env.k.vehicle.update_accel_without_noise(self.veh_id, accel_without_noise) # add noise to the accelerations, if requested @@ -126,6 +135,12 @@ def get_action(self, env): accel = self.get_safe_action_instantaneous(env, accel) elif self.fail_safe == 'safe_velocity': accel = self.get_safe_velocity_action(env, accel) + elif self.fail_safe == 'feasible_accel': + accel = self.get_feasible_action(accel) + elif self.fail_safe == 'all': + accel = self.get_safe_velocity_action(env, accel) + accel = self.get_feasible_action(accel) + accel = self.get_safe_action_instantaneous(env, accel) return accel @@ -172,6 +187,14 @@ def get_safe_action_instantaneous(self, env, action): # if the vehicle will crash into the vehicle ahead of it in the # next time step (assuming the vehicle ahead of it is not # moving), then stop immediately + print( + "=====================================\n" + "Vehicle {} is about to crash. Instantaneous acceleration " + "clipping applied.\n" + "=====================================".format(self.veh_id)) + + print("Vehicle {} is about to crash. Instantaneous acceleration" + "clipping applied.".format(self.veh_id)) return -this_vel / sim_step else: # if the vehicle is not in danger of crashing, continue with @@ -245,4 +268,53 @@ def safe_velocity(self, env): v_safe = 2 * h / env.sim_step + dv - this_vel * (2 * self.delay) + # check for speed limit + this_edge = env.k.vehicle.get_edge(self.veh_id) + edge_speed_limit = env.k.network.speed_limit(this_edge) + + if v_safe > edge_speed_limit: + v_safe = edge_speed_limit + print( + "=====================================\n" + "Speed of vehicle {} is greater than speed limit. Safe " + "velocity clipping applied.\n" + "=====================================".format(self.veh_id)) + return v_safe + + def get_feasible_action(self, action): + """Perform the "feasible_accel" failsafe action. + + Checks if the computed acceleration would put us above maximum + acceleration or deceleration. If it would, output the acceleration + equal to maximum acceleration or deceleration. + + Parameters + ---------- + action : float + requested acceleration action + + Returns + ------- + float + the requested action clipped by the safe velocity + """ + if action > self.max_accel: + action = self.max_accel + + print( + "=====================================\n" + "Acceleration of vehicle {} is greater than the max " + "acceleration. Feasible acceleration clipping applied.\n" + "=====================================".format(self.veh_id)) + + if action < -self.max_deaccel: + action = -self.max_deaccel + + print( + "=====================================\n" + "Deceleration of vehicle {} is greater than the max " + "deceleration. Feasible acceleration clipping applied.\n" + "=====================================".format(self.veh_id)) + + return action From 53cf035684b02668fa2116942c75b02cd4398d29 Mon Sep 17 00:00:00 2001 From: Yasharzf Date: Tue, 26 May 2020 14:22:16 -0700 Subject: [PATCH 85/89] removed json file which was added by mistake --- flow/core/kernel/network/flow_params.json | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 flow/core/kernel/network/flow_params.json diff --git a/flow/core/kernel/network/flow_params.json b/flow/core/kernel/network/flow_params.json deleted file mode 100644 index c70a22e63..000000000 --- a/flow/core/kernel/network/flow_params.json +++ /dev/null @@ -1,2 +0,0 @@ -{ - "env": \ No newline at end of file From 528f0aace706fc8a3de99aba720bda7c0eb309b4 Mon Sep 17 00:00:00 2001 From: Yasharzf Date: Tue, 26 May 2020 15:14:00 -0700 Subject: [PATCH 86/89] fixed docstrings --- flow/controllers/base_controller.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/flow/controllers/base_controller.py b/flow/controllers/base_controller.py index 2f01faef2..7cbbef6db 100755 --- a/flow/controllers/base_controller.py +++ b/flow/controllers/base_controller.py @@ -261,8 +261,8 @@ def safe_velocity(self, env): Returns ------- float - maximum safe velocity given a maximum deceleration and delay in - performing the breaking action + maximum safe velocity given a maximum deceleration, delay in + performing the breaking action, and speed limit """ lead_id = env.k.vehicle.get_leader(self.veh_id) lead_vel = env.k.vehicle.get_speed(lead_id) @@ -302,7 +302,8 @@ def get_feasible_action(self, action): Returns ------- float - the requested action clipped by the safe velocity + the requested action clipped by the feasible acceleration or + deceleration. """ if action > self.max_accel: action = self.max_accel From cbf6a420b727f5bf1d60a9bf8ff7cef92bbfe5ae Mon Sep 17 00:00:00 2001 From: Yasharzf Date: Tue, 26 May 2020 15:15:05 -0700 Subject: [PATCH 87/89] removed duplicated print --- flow/controllers/base_controller.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/flow/controllers/base_controller.py b/flow/controllers/base_controller.py index 7cbbef6db..95ecd1737 100755 --- a/flow/controllers/base_controller.py +++ b/flow/controllers/base_controller.py @@ -198,8 +198,6 @@ def get_safe_action_instantaneous(self, env, action): "clipping applied.\n" "=====================================".format(self.veh_id)) - print("Vehicle {} is about to crash. Instantaneous acceleration" - "clipping applied.".format(self.veh_id)) return -this_vel / sim_step else: # if the vehicle is not in danger of crashing, continue with From 86458115b1f2f4d1f59755fae484daa8af4b00dc Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Tue, 26 May 2020 17:07:41 -0700 Subject: [PATCH 88/89] minor docstring formatting --- flow/controllers/base_controller.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/flow/controllers/base_controller.py b/flow/controllers/base_controller.py index 95ecd1737..2fdb2f399 100755 --- a/flow/controllers/base_controller.py +++ b/flow/controllers/base_controller.py @@ -77,8 +77,10 @@ def get_action(self, env): This method also augments the controller with the desired level of stochastic noise, and utlizes the "instantaneous", "safe_velocity", "feasible_accel", or "all" failsafes if requested. The "all" failsafe - performs all three failsafes with this order: 1)"safe_velocity", - 2) "feasible_accel", 3) "instantaneous". + performs all three failsafes with this order: + 1) "safe_velocity", + 2) "feasible_accel", + 3) "instantaneous". Parameters ---------- From 16697871d63f735a90aad66ace14b5d757ce73e0 Mon Sep 17 00:00:00 2001 From: liljonnystyle Date: Wed, 27 May 2020 10:05:09 -0700 Subject: [PATCH 89/89] addressing comments --- flow/core/kernel/vehicle/traci.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/flow/core/kernel/vehicle/traci.py b/flow/core/kernel/vehicle/traci.py index 2166709b6..9485572b2 100644 --- a/flow/core/kernel/vehicle/traci.py +++ b/flow/core/kernel/vehicle/traci.py @@ -113,10 +113,6 @@ def initialize(self, vehicles): self.__vehicles[veh_id] = dict() self.__vehicles[veh_id]['type'] = typ['veh_id'] self.__vehicles[veh_id]['initial_speed'] = typ['initial_speed'] - self.__vehicles[veh_id]["accel_no_noise_no_failsafe"] = None - self.__vehicles[veh_id]["accel_no_noise_with_failsafe"] = None - self.__vehicles[veh_id]["accel_with_noise_no_failsafe"] = None - self.__vehicles[veh_id]["accel_with_noise_with_failsafe"] = None self.num_vehicles += 1 if typ['acceleration_controller'][0] == RLController: self.num_rl_vehicles += 1 @@ -955,7 +951,7 @@ def _prev_edge_followers(self, veh_id, edge_dict, lane, num_edges): def apply_acceleration(self, veh_ids, acc): """See parent class.""" - # to hand the case of a single vehicle + # to handle the case of a single vehicle if type(veh_ids) == str: veh_ids = [veh_ids] acc = [acc] @@ -969,7 +965,7 @@ def apply_acceleration(self, veh_ids, acc): def apply_acceleration_not_smooth(self, veh_ids, acc): """See parent class.""" - # to hand the case of a single vehicle + # to handle the case of a single vehicle if type(veh_ids) == str: veh_ids = [veh_ids] acc = [acc]