コード例 #1
0
ファイル: test_units.py プロジェクト: amarogs/simtravel
 def test_simulation_speed_to_kmh(self):
     units = Units(speed=10,
                   cell_length=5,
                   simulation_speed=1,
                   battery=24,
                   cs_power=7,
                   autonomy=135)
     self.assertEqual(units.simulation_speed_to_kmh(1), 10,
                      "Wrong conversion from sim speed to real speed")
コード例 #2
0
ファイル: test_units.py プロジェクト: amarogs/simtravel
 def test_minutes_to_steps(self):
     units = Units(speed=10,
                   cell_length=5,
                   simulation_speed=1,
                   battery=24,
                   cs_power=7,
                   autonomy=135)
     self.assertEqual(units.minutes_to_steps(0.6), 20.0,
                      "Wrong conversion from minutes to steps")
コード例 #3
0
ファイル: test_units.py プロジェクト: amarogs/simtravel
 def test_seconds_to_hours(self):
     units = Units(speed=10,
                   cell_length=5,
                   simulation_speed=1,
                   battery=24,
                   cs_power=7,
                   autonomy=135)
     self.assertEqual(units.seconds_to_hours(3600), 1.0,
                      "Wrong units conversion to hours")
コード例 #4
0
ファイル: test_units.py プロジェクト: amarogs/simtravel
 def test_steps_to_minutes(self):
     units = Units(speed=10,
                   cell_length=5,
                   simulation_speed=1,
                   battery=24,
                   cs_power=7,
                   autonomy=135)
     self.assertEqual(units.steps_to_minutes(20), 0.6,
                      "Wrong conversion from steps to minutes")
コード例 #5
0
ファイル: test_units.py プロジェクト: amarogs/simtravel
 def test_steps_to_seconds(self):
     units = Units(speed=10,
                   cell_length=5,
                   simulation_speed=1,
                   battery=24,
                   cs_power=7,
                   autonomy=135)
     self.assertEqual(units.steps_to_seconds(10), 18.0,
                      "Wrong conversion steps to seconds")
コード例 #6
0
ファイル: test_units.py プロジェクト: amarogs/simtravel
 def test_cells_to_meters(self):
     units = Units(speed=10,
                   cell_length=5,
                   simulation_speed=1,
                   battery=24,
                   cs_power=7,
                   autonomy=135)
     self.assertEqual(units.cells_to_meters(10), 50,
                      "Wrong conversion from cell to meters")
コード例 #7
0
ファイル: test_units.py プロジェクト: amarogs/simtravel
 def test_meter_to_km(self):
     units = Units(speed=10,
                   cell_length=5,
                   simulation_speed=1,
                   battery=24,
                   cs_power=7,
                   autonomy=135)
     self.assertEqual(units.meters_to_km(1000), 1.0,
                      "Wrong converesion to km")
コード例 #8
0
    def set_simulation_units(self, speed=10, cell_length=5, simulation_speed=1, battery=24, cs_power=7, autonomy=135):
        self.units = Units(speed, cell_length, simulation_speed,
                           battery, cs_power, autonomy)

        # Write the attributes in orde to save them in the HDF5 file.
        self.SPEED = speed
        self.CELL_LENGTH = cell_length
        self.SIMULATION_SPEED = simulation_speed
        self.BATTERY = battery
        self.CS_POWER = cs_power
        self.AUTONOMY = autonomy
コード例 #9
0
ファイル: test_units.py プロジェクト: amarogs/simtravel
 def test_steps_to_recharge(self):
     units = Units(speed=10,
                   cell_length=5,
                   simulation_speed=1,
                   battery=24,
                   cs_power=7,
                   autonomy=135)
     d = 1.8 * 27000
     n = 10 * (((3.6 * 10**6) * 24) / 7000)
     self.assertEqual(round(units.steps_to_recharge(10),
                            5), round(n / d, 5),
                      "Wrong computation of the steps to recharge")
コード例 #10
0
ファイル: test_units.py プロジェクト: amarogs/simtravel
 def tests_step_to_s(self):
     units = Units(speed=10,
                   cell_length=5,
                   simulation_speed=1,
                   battery=24,
                   cs_power=7,
                   autonomy=135)
     self.assertEqual(
         units.step_to_s, 1.8,
         "Wrong constant value for converting steps to seconds")
コード例 #11
0
    def load_data(self):
        """Takes the attributes of the root folder of the
        HDF5 file and stores them as class attributes. It also
        loads the datasets from different groups and repetitions
        and place them together in numpy arrays creating
        two dictionaries per group: one containing the mean 
        of the different repetitions and other containing the std. """

        with h5py.File(self.filepath, "r") as file:

            # Save the simulation attributes
            for key, val in file.attrs.items():
                self.__setattr__(key, val)

            # Retrieve the data from the groups
            for group in list(file['0'].keys()):

                data_mean, data_std = self.get_data_from_group(file, group)

                self.__setattr__(group+"_mean", data_mean)
                self.__setattr__(group+"_std", data_std)

            # Obtain the total time spent charging and insert it
            # in the dictionary of global attributes
            data_mean, data_std = self.get_data_total_group(file, "global")
            self.global_mean['total'] = data_mean
            self.global_std['total'] = data_std

            # Obtain the mean and std of the velocities group by columns.
            data_mean, data_std = self.get_data_from_group(
                file, 'velocities', axis=1)
            for key, val in data_mean.items():
                self.global_mean[key] = val

        # Once we have loaded the datasets, we have to create a
        # units objects
        self.units = Units(self.SPEED, self.CELL_LENGTH, self.SIMULATION_SPEED,
                           self.BATTERY, self.CS_POWER, self.AUTONOMY)
コード例 #12
0
class Simulation():
    def __init__(self, EV_DEN, TF_DEN, ST_LAYOUT, PATHNAME):
        """Creates a Simulation object receiving:

        :param TF_DEN: traffic density of the simulation, it is expressed
        as a percentage of the total drivable cells. A TF_DEN of 1 means
        that every possible cell in the city is occupied by a vehicle.

        :param EV_DEN: electric vehicle dendisty, it's the proportion
        of EVs relative to the total amount of vehicles.

        :param ST_LAYOUT: is the layout of the stations, can either be
        'distributed', 'central' or 'four'.

        In order for the simulation to work, once the Simulation object
        is created, the following methods must be called:

        Simulation.set_simulation_units()
        Simulation.set_battery_distribution()
        Simulation.set_idle_distribution()
        Simulation.create_city()
        Simulation.stations_placement()
        Simulation.create_simulator()

        """
        super().__init__()

        # Attributes that define a simulation
        self.EV_DEN = EV_DEN
        self.TF_DEN = TF_DEN
        self.ST_LAYOUT = ST_LAYOUT
        self.PATHNAME = PATHNAME
        self.sim_name = "EV_DEN: {} TF_DEN: {} LAYOUT: {}".format(EV_DEN, TF_DEN, ST_LAYOUT)
        self.filename = PATHNAME + "/results/{}#{}#{}".format(
            self.EV_DEN, self.TF_DEN, self.ST_LAYOUT)
        # Attributes filled in the method set_simulation_units()
        self.units = None

        # Attributes filled in the method create_city()
        self.city_builder = None
        self.SCALE = None
        self.AV_LENGTH = None
        self.RB_LENGTH = None
        self.INTERSEC_LENGTH = None
        self.city_map = None
        self.city_matrix = None

        self.avenues = None
        self.SIZE = None
        self.STR_RATE = None
        

        # Attributes filled in the method stations_placement()
        self.TOTAL_PLUGS = None  # Total number of plugs in the city
        self.TOTAL_D_ST = None  # Total number of distributed stations
        self.stations = None
        self.stations_map = None

    def set_simulation_units(self, speed=10, cell_length=5, simulation_speed=1, battery=24, cs_power=7, autonomy=135):
        self.units = Units(speed, cell_length, simulation_speed,
                           battery, cs_power, autonomy)

        # Write the attributes in orde to save them in the HDF5 file.
        self.SPEED = speed
        self.CELL_LENGTH = cell_length
        self.SIMULATION_SPEED = simulation_speed
        self.BATTERY = battery
        self.CS_POWER = cs_power
        self.AUTONOMY = autonomy

    def create_city(self, Builder, RB_LENGTH=6, AV_LENGTH=4*5, SCALE=1, INTERSEC_LENGTH=3):
        """Builder is a CityBuilder class"""

        # Create the city builder
        self.city_builder = Builder(RB_LENGTH, AV_LENGTH, SCALE, INTERSEC_LENGTH)
        self.city_map = self.city_builder.city_map
        self.city_matrix = self.city_builder.city_matrix

        self.avenues = self.city_builder.avenues
        self.roundabouts = self.city_builder.roundabouts
        self.SCALE = SCALE
        self.AV_LENGTH = AV_LENGTH
        self.RB_LENGTH = RB_LENGTH
        self.INTERSEC_LENGTH = INTERSEC_LENGTH

        self.SIZE = self.city_builder.SIZE
        self.STR_RATE = self.city_builder.STR_RATE

        # Create the city districts
        self.districts = self.city_builder.create_districts(self.ST_LAYOUT)

    def stations_placement(self, min_plugs_per_station, min_num_stations):
        """This method computes the number total number of distributed stations
        that are needed in order to make the city symmetrical and compatible 
        between different stations layour, for example the total number of distributed stations
        need to be a divisor of 4. Then computes the placement of the stations
        for this simulation and creates the simulation objects with the correct
        amount of chargers.

        :param min_plugs_per_station: is the minimum number of plugs that we 
        want each distrbuted station to have.
        :param min_num_stations: is the minimum number of distributed stations
        that we want to have in a simulation with the ST_LAYOUT = "distributed"
        """

        # Compute the amount of plugs and the number of distributed stations
        self.TOTAL_PLUGS, self.TOTAL_D_ST = self.city_builder.set_max_chargers_stations(
            min_plugs_per_station, min_num_stations)

        # Place the stations around the city based on the layout
        # self.stations_pos = self.city_builder.place_stations(self.ST_LAYOUT, self.districts, self.TOTAL_D_ST)
        
        self.stations_clusters, self.pos_clusters = self.city_builder.place_stations_new(self.ST_LAYOUT, self.TOTAL_D_ST)
        # Based on the layout, compute the number of plugs that each station will have.
        
        plugs_per_station = min_plugs_per_station
        if self.ST_LAYOUT == "central":
            plugs_per_station = self.TOTAL_PLUGS
        elif self.ST_LAYOUT == "four":
            plugs_per_station = self.TOTAL_PLUGS/4

        # Create the stations and the stations map
        self.stations, self.stations_map = self.create_stations(self.stations_clusters, self.pos_clusters, plugs_per_station)

    def create_stations(self, stations_clusters, pos_clusters, plugs_per_station):
        """
        :param stations_clusters: A list of lists. Each list is a "district" and has the positions
        of the stations belonging to that district.

        :param pos_clusters: A list of lists. Each list is the positions of the city that
        belongs to the stations cluster with the same index.

        :param plugs_per_station: is the number of plugs that a station can have.
         """
        stations = []
        stations_map = {}

        for stations_pos, influence_area in zip(stations_clusters,pos_clusters):
            # For each position in the station cluster, create a proper Station object
            district_stations = [Station(self.city_map[pos], plugs_per_station) for pos in stations_pos]
            # For each position in the influence area of this cluster, link them in the stations map
            for influence_pos in influence_area:
                stations_map[influence_pos] = district_stations
            # Add the stations to the list of stations.
            stations.extend(district_stations)


        return stations, stations_map

    def set_battery_distribution(self, lower, std):
        """This method sets the parameters of the normal distribution used
        to set the goal charge of a vehicle when it wants to recharge.
        :param lower: Is the minimum amount to recharge. 1 sets the lower 
        limit to 100% of the vehicle's autonomy while 0 sets the lower
        limit to 0% of the vehicle's autonomy.
        :param std: is the deviation of the distribution with respect
        to the mean. A value of 1 means a deviation equal to 100% of the mean
        while a value of 0 means no deviation at all.

        Internally the battery values are expressed as autonomy in simulation cells.
        """

        self.BATTERY_UPPER = int(self.units.autonomy)
        self.BATTERY_LOWER = int(lower * self.units.autonomy)
        self.BATTERY_MEAN = (lower + self.BATTERY_UPPER) // 2
        self.BATTERY_STD = std * self.BATTERY_MEAN

    def set_idle_distribution(self, upper, lower, std):
        """This method sets the parameters of the normal distribution used
        to compute the amount of time a vehicle is idle at a destination.
        :param upper: Is the maximum time, in minutes, that a vehicle can
        stay idle.
        :param lower: Is the minimum time, in minutes, that a vehicle can 
        stay idle.
        :param std: controls the deviation with respect to the mean. A value
        of 1 means a deviation equal to 100% while a deviation of 0 no deviation
        at all.

        Internally the idle times are expressed as simulation time steps.
        """
        self.IDLE_UPPER = int(self.units.minutes_to_steps(upper))
        self.IDLE_LOWER = int(self.units.minutes_to_steps(lower))
        self.IDLE_MEAN = (self.IDLE_UPPER + self.IDLE_LOWER) // 2
        self.IDLE_STD = std * self.IDLE_MEAN

    def create_vehicles(self):
        """Creates the vehicles and places them around the city.
        Initially all the vehicles are in the AT_DEST state waiting to be released.
        The realeasing follows the same distribution as the idle time spent at a destination."""

        # Add constants to the simulation object
        self.TOTAL_VEHICLES = int(self.STR_RATE * self.SIZE * self.SIZE * self.TF_DEN)

        self.TOTAL_EV = int(self.EV_DEN * self.TOTAL_VEHICLES)

        # Create the vehicles, place them on the city.
        # Initially all the vehicles are in a AT_DEST
        # state and so they don't occupy a place.
        city_cells = list(self.city_map.values())
        
        random.shuffle(city_cells)

        ev_vehicles = set()
        vehicles = []
        for _ in range(self.TOTAL_EV):
            v = ElectricVehicle(city_cells.pop(),
                                self.simulator.compute_idle(), self.simulator.compute_battery())
            vehicles.append(v)
            ev_vehicles.add(v)
        for _ in range(self.TOTAL_VEHICLES-self.TOTAL_EV):
            v = Vehicle(city_cells.pop(), self.simulator.compute_idle())
            vehicles.append(v)

        self.vehicles = vehicles
        self.ev_vehicles = ev_vehicles

    def create_simulator(self):
        """Creates the simulator object.
        Then it also creates the vehicles calling Simulation.create_vehicles() """
        self.simulator = SimulatorEngine(self)
        self.create_vehicles()

    def print_summary(self):
        msg = """*******************************************************\
            \nInitializing the simulation: {}\
            \nThe size of the city is: {} \
            \nThis will run for {} tsteps, taking measures every {} tsteps.\
            \nThe battery distribution has a mean of {} steps and std of {} steps.\
            \nThe idle distribution has a mean of {} steps and std of {} steps.\
            \nIn total there is {} vehicles being electric {} """\
            .format(self.filename,self.SIZE ,self.TOTAL_TSTEPS, self.DELTA_TSTEPS, self.BATTERY_MEAN,
                    self.BATTERY_STD, self.IDLE_MEAN, self.IDLE_STD, self.TOTAL_VEHICLES, self.TOTAL_EV)
        print(msg)

    def set_progress_message(self, num_progress_msg):
        """Computes a list of tsteps that will prompt a message displaying
        the progress of the simulation. """

        self.progress_tsteps = [int(((i+1)*self.TOTAL_TSTEPS)/(num_progress_msg*self.DELTA_TSTEPS))*self.DELTA_TSTEPS
                                for i in range(num_progress_msg)]

    def print_progress(self, tstep):
        progress = int(100*(tstep/self.TOTAL_TSTEPS))
        msg = """Simulation: {} Repetition: {} Progress: {}% """.format(
            self.filename, self.repetition, progress)
        print(msg)

    def prepare_results_file(self):
        """Checks if the results folder exists and truncates the destination
        file."""

        # Check if the results folder exists.
        if not os.path.exists(self.PATHNAME + "/results"):
            os.makedirs(self.PATHNAME + "/results")
        else:
            with open(self.filename + ".hdf5", "w"):
                pass

    def write_header_attr(self):
        """This method writes the global attributes of the simulation as HDF5
        attributes of the root group. """

        # Write the attributes
        with h5py.File(self.filename + ".hdf5", "a") as f:
            for key, value in self.__dict__.items():
                if key.isupper():
                    f.attrs[key] = value

    def write_results(self, repetition, metrics):
        """Once the simulation is over, the results are written to a HDF5 file.
        The name of the file is made with the attributes EV_DEN#TF_DEN#ST_LAYOUT that
        identify a simulation. For each repetition a group is made with the index of
        the repetition and the data is saved into datasets."""

        with h5py.File(self.filename+".hdf5", "a") as f:
            metrics.write_results(
                f, "/"+str(repetition)+"/", self.simulator.seeking_history, self.simulator.queueing_history)

    def run(self, total_time, measure_period, repetitions, visual=None):
        """ Method to execute the simulation. The attributes are given
        in the SI, time (hours) and measure_period (minutes).

        :param time: total time to simulate in hours
        :param measure_period: time between two consecutive snapshots of the system.
        :param repetitions: number of times the simulation is run.
         """
        # Convert the time to simulation steps
        total_tsteps = int(self.units.minutes_to_steps(total_time*60))
        delta_tsteps = int(self.units.minutes_to_steps(measure_period))

        if delta_tsteps == 0:
            delta_tsteps = 1

        self.TOTAL_TSTEPS = total_tsteps
        self.DELTA_TSTEPS = delta_tsteps
        self.REPETITIONS = repetitions
        
        # Set the list of tsetps for displaying a message
        self.set_progress_message(10)

        # Prepare the HDF5 file
        self.prepare_results_file()

        self.print_summary()

        elapsed = time.time()
        for i in range(repetitions):
            self.repetition = i

            # Restart the cells, vehicles, stations and the simulator
            for cell in self.city_map.values():
                cell.occupied = False
            for v in self.vehicles:
                v.restart()

            for st in self.stations:
                st.restart()

            self.simulator.restart()

            # Create a metrics object and initilize it
            metrics = SimulationMetric(
                self.city_map, self.stations, 3, total_tsteps, delta_tsteps, self.SIZE)
            metrics.initialize(
                self.vehicles, self.ev_vehicles, self.stations)

            # Run the simulation using the simulator object
            if visual == None:
                self.run_simulator(metrics)
            else:
                self.run_simulator_visual(metrics, visual)
                
                

            # Store the data into an HDF5 file.
            self.write_results(i, metrics)

        self.ELAPSED = round((time.time() - elapsed) / repetitions, 3)

        self.write_header_attr()

    def run_simulator(self, metrics):
        """This method controls the flow of the simulator, saving the
        data and displaying a progress message. """

        previous_snapshot = SimulationSnapshot(self.vehicles)

        for current_tstep in range(1, self.TOTAL_TSTEPS+1):
            
            # Compute next step of the simulation
            self.simulator.next_step()

            # Check if we have to update the data collection
            if current_tstep % self.DELTA_TSTEPS == 0:
                current_snapshot = SimulationSnapshot(self.vehicles)
                metrics.update_data(self.vehicles, self.ev_vehicles, self.stations,
                                    current_snapshot, previous_snapshot, current_tstep)
                previous_snapshot = current_snapshot

            # Check if we have to display a progress message
            if current_tstep in self.progress_tsteps:
                self.print_progress(current_tstep)

    def run_simulator_visual(self, metrics, visual):
        """This method controls the flow of the simulator, saving the
        data and displaying a progress message. """

        
        self.previous_snapshot = SimulationSnapshot(self.vehicles)
        def next_frame():

            # Compute next step of the simulation
            self.simulator.next_step()
              
            visual.update()
            current_tstep = visual.current_tstep
            # Check if we have to update the data collection
            if current_tstep % self.DELTA_TSTEPS == 0:
                current_snapshot = SimulationSnapshot(self.vehicles)
                metrics.update_data(self.vehicles, self.ev_vehicles, self.stations, current_snapshot, self.previous_snapshot, current_tstep)
                self.previous_snapshot = current_snapshot

            # Check if we have to display a progress message
            if current_tstep in self.progress_tsteps:
                self.print_progress(current_tstep)

            # Increment the steps counter
            visual.current_tstep += 1

            # Check for termination
            if visual.current_tstep == self.TOTAL_TSTEPS:
                "Terminar la visualizacion"
                pass


            
        visual.beginRepresentation(next_frame)
            

    def prepare_simulation(self, total_time, measure_period, repetitions):
        """First function to call when we want to run a new simulation from the application. """
        # Convert the time to simulation steps
        total_tsteps = int(self.units.minutes_to_steps(total_time*60))
        delta_tsteps = int(self.units.minutes_to_steps(measure_period))

        if delta_tsteps == 0:
            delta_tsteps = 1

        self.TOTAL_TSTEPS = total_tsteps
        self.DELTA_TSTEPS = delta_tsteps
        self.REPETITIONS = repetitions
        
        # Set the list of tsetps for displaying a message
        self.set_progress_message(10)

        # Prepare the HDF5 file
        self.prepare_results_file()

        self.print_summary()



    def end_simulation(self):
        """Last function to call when we want to run a simulation from the application. """
        self.ELAPSED = 0
        self.write_header_attr()

    def run_simulator_application(self, current_repetition, current_tstep, current_metrics, previous_snapshot):
        """Function that updates the simulation from the application. """

        if current_tstep == 0:
            print("Starting")
            # Restart the cells, vehicles, stations and the simulator
            for cell in self.city_map.values():
                cell.occupied = False
            for v in self.vehicles:
                v.restart()

            for st in self.stations:
                st.restart()

            self.simulator.restart()
            self.repetition = current_repetition

            # Create a metrics object and initilize it
            current_metrics = SimulationMetric(self.city_map, self.stations, 3, self.TOTAL_TSTEPS, self.DELTA_TSTEPS, self.SIZE)
            current_metrics.initialize(self.vehicles, self.ev_vehicles, self.stations)
            self.metrics = current_metrics

            # Create the first snapshot
            previous_snapshot = SimulationSnapshot(self.vehicles)
            
            current_tstep += 1

        elif current_tstep > self.TOTAL_TSTEPS:
            print("End of repetition")
            # Store the data into an HDF5 file.
            self.write_results(current_repetition, current_metrics)

            # test whether there are more simulations left
            current_repetition += 1
            
            if current_repetition < self.REPETITIONS:
                # There are more simulations left to do, set the time step to zero.
                print("There are more repetitions left")
                current_tstep = 0
            else:
                # There are no more simulations left to, terminate.
                print("No more repetitions left")
                self.end_simulation()
                return None

        else:
            
            self.simulator.next_step()
            
            # Check if we have to update the data collection
            if current_tstep % self.DELTA_TSTEPS == 0:
                current_snapshot = SimulationSnapshot(self.vehicles)
                current_metrics.update_data(self.vehicles, self.ev_vehicles, self.stations, current_snapshot, previous_snapshot, current_tstep)
                previous_snapshot = current_snapshot

            # Check if we have to display a progress message
            if current_tstep in self.progress_tsteps:
                self.print_progress(current_tstep)
            
            current_tstep += 1


        return current_repetition, current_tstep, current_metrics, previous_snapshot