diff --git a/.gitignore b/.gitignore index 1197af641..fab9f00a5 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,5 @@ data/ maro_venv/ pyvenv.cfg htmlcov/ -.coverage - -.coveragerc +.coverage +.coveragerc diff --git a/docs/source/examples/multi_agent_dqn_cim.rst b/docs/source/examples/multi_agent_dqn_cim.rst index 69e08e1ef..f85b3e2a9 100644 --- a/docs/source/examples/multi_agent_dqn_cim.rst +++ b/docs/source/examples/multi_agent_dqn_cim.rst @@ -69,7 +69,7 @@ in the roll-out loop. In this example, plan_action = percent * (scope.discharge + early_discharge) - early_discharge actual_action = round(plan_action) if plan_action > 0 else round(percent * scope.discharge) else: - actual_action, action_type = 0, None + actual_action, action_type = 0, ActionType.LOAD return {port: Action(vessel, port, actual_action, action_type)} diff --git a/docs/source/scenarios/container_inventory_management.rst b/docs/source/scenarios/container_inventory_management.rst index 54cce05d8..f044901b0 100644 --- a/docs/source/scenarios/container_inventory_management.rst +++ b/docs/source/scenarios/container_inventory_management.rst @@ -572,7 +572,7 @@ Once we get a ``DecisionEvent`` from the environment, we should respond with an * **vessel_idx** (int): The id of the vessel/operation object of the port/agent. * **port_idx** (int): The id of the port/agent that take this action. * **action_type** (ActionType): Whether to load or discharge empty containers in this action. - * **quantity** (int): The quantity of empty containers to be loaded/discharged. + * **quantity** (int): The (non-negative) quantity of empty containers to be loaded/discharged. Example ^^^^^^^ diff --git a/examples/cim/common.py b/examples/cim/common.py index 2af93eb74..d6cea7042 100644 --- a/examples/cim/common.py +++ b/examples/cim/common.py @@ -30,7 +30,7 @@ class CIMTrajectory(Trajectory): def __init__( self, env, *, port_attributes, vessel_attributes, action_space, look_back, max_ports_downstream, reward_time_window, fulfillment_factor, shortage_factor, time_decay, - finite_vessel_space=True, has_early_discharge=True + finite_vessel_space=True, has_early_discharge=True ): super().__init__(env) self.port_attributes = port_attributes @@ -72,7 +72,7 @@ def get_action(self, action_by_agent, event): plan_action = percent * (scope.discharge + early_discharge) - early_discharge actual_action = round(plan_action) if plan_action > 0 else round(percent * scope.discharge) else: - actual_action, action_type = 0, None + actual_action, action_type = 0, ActionType.LOAD return {port: Action(vessel, port, actual_action, action_type)} diff --git a/maro/data_lib/cim/cim_data_container.py b/maro/data_lib/cim/cim_data_container.py index 701e766ad..40971e009 100644 --- a/maro/data_lib/cim/cim_data_container.py +++ b/maro/data_lib/cim/cim_data_container.py @@ -10,7 +10,7 @@ from .entities import ( CimBaseDataCollection, CimRealDataCollection, CimSyntheticDataCollection, NoisedItem, Order, OrderGenerateMode, - PortSetting, VesselSetting + PortSetting, SyntheticPortSetting, VesselSetting ) from .port_buffer_tick_wrapper import PortBufferTickWrapper from .utils import BUFFER_TICK_RAND_KEY, ORDER_NUM_RAND_KEY, apply_noise, list_sum_normalize @@ -42,7 +42,7 @@ class CimBaseDataContainer(ABC): Args: data_collection (CimBaseDataCollection): Corresponding data collection. """ - def __init__(self, data_collection: CimBaseDataCollection): + def __init__(self, data_collection: CimBaseDataCollection) -> None: self._data_collection = data_collection # wrapper for interfaces, to make it easy to use @@ -151,7 +151,7 @@ def full_return_buffers(self) -> PortBufferTickWrapper: .. code-block:: python # Get full return buffer tick of port 0. - buffer_tick = data_cnr.full_return_buffers[0] + buffer_tick = data_cntr.full_return_buffers[0] """ return self._full_return_buffer_wrapper @@ -209,7 +209,7 @@ def reachable_stops(self) -> VesselReachableStopsWrapper: return self._reachable_stops_wrapper @property - def vessel_period(self) -> int: + def vessel_period(self) -> List[int]: """Wrapper to get vessel's planned sailing period (without noise to complete a whole route). Examples: @@ -241,7 +241,7 @@ def reset(self): self._is_need_reset_seed = True def _reset_seed(self): - """Reset internal seed for generate reproduceable data""" + """Reset internal seed for generate reproduce-able data""" random.reset_seed(BUFFER_TICK_RAND_KEY) @abstractmethod @@ -288,14 +288,14 @@ def get_orders(self, tick: int, total_empty_container: int) -> List[Order]: self._is_need_reset_seed = False - if tick >= self._data_collection.max_tick: + if tick >= self._data_collection.max_tick: # pragma: no cover warnings.warn(f"{tick} out of max tick {self._data_collection.max_tick}") return [] return self._gen_orders(tick, total_empty_container) def _reset_seed(self): - """Reset internal seed for generate reproduceable data""" + """Reset internal seed for generate reproduce-able data""" super()._reset_seed() random.reset_seed(ORDER_NUM_RAND_KEY) @@ -308,6 +308,7 @@ def _gen_orders(self, tick: int, total_empty_container: int) -> List[Order]: """ # result order_list: List[Order] = [] + assert isinstance(self._data_collection, CimSyntheticDataCollection) order_proportion = self._data_collection.order_proportion order_mode = self._data_collection.order_mode total_containers = self._data_collection.total_containers @@ -316,7 +317,7 @@ def _gen_orders(self, tick: int, total_empty_container: int) -> List[Order]: orders_to_gen = int(order_proportion[tick]) # if under unfixed mode, we will consider current empty container as factor - if order_mode == OrderGenerateMode.UNFIXED: + if order_mode == OrderGenerateMode.UNFIXED: # pragma: no cover. TODO: remove this mark later delta = total_containers - total_empty_container if orders_to_gen <= delta: @@ -331,7 +332,9 @@ def _gen_orders(self, tick: int, total_empty_container: int) -> List[Order]: # calculate orders distribution for each port as source for port_idx in range(self.port_number): - source_dist: NoisedItem = self.ports[port_idx].source_proportion + port = self.ports[port_idx] + assert isinstance(port, SyntheticPortSetting) + source_dist: NoisedItem = port.source_proportion noised_source_order_number = apply_noise(source_dist.base, source_dist.noise, random[ORDER_NUM_RAND_KEY]) @@ -346,7 +349,9 @@ def _gen_orders(self, tick: int, total_empty_container: int) -> List[Order]: if remaining_orders == 0: break - targets_dist: List[NoisedItem] = self.ports[port_idx].target_proportions + port = self.ports[port_idx] + assert isinstance(port, SyntheticPortSetting) + targets_dist: List[NoisedItem] = port.target_proportions # apply noise and normalize noised_targets_dist = list_sum_normalize( @@ -403,6 +408,7 @@ def __init__(self, data_collection: CimRealDataCollection): super().__init__(data_collection) # orders + assert isinstance(self._data_collection, CimRealDataCollection) self._orders: Dict[int, List[Order]] = self._data_collection.orders def get_orders(self, tick: int, total_empty_container: int) -> List[Order]: @@ -422,11 +428,8 @@ def get_orders(self, tick: int, total_empty_container: int) -> List[Order]: self._is_need_reset_seed = False - if tick >= self._data_collection.max_tick: + if tick >= self._data_collection.max_tick: # pragma: no cover warnings.warn(f"{tick} out of max tick {self._data_collection.max_tick}") return [] - if tick not in self._orders: - return [] - - return self._orders[tick] + return self._orders[tick] if tick in self._orders else [] diff --git a/maro/data_lib/cim/cim_data_container_helpers.py b/maro/data_lib/cim/cim_data_container_helpers.py index 2be9ee671..e2b2dcfec 100644 --- a/maro/data_lib/cim/cim_data_container_helpers.py +++ b/maro/data_lib/cim/cim_data_container_helpers.py @@ -3,12 +3,13 @@ import os import urllib.parse +from typing import Optional from maro.cli.data_pipeline.utils import StaticParameter from maro.simulator.utils import random, seed from .cim_data_container import CimBaseDataContainer, CimRealDataContainer, CimSyntheticDataContainer -from .cim_data_generator import CimDataGenerator +from .cim_data_generator import gen_cim_data from .cim_data_loader import load_from_folder, load_real_data_from_folder from .utils import DATA_CONTAINER_INIT_SEED_LIMIT, ROUTE_INIT_RAND_KEY @@ -16,7 +17,7 @@ class CimDataContainerWrapper: def __init__(self, config_path: str, max_tick: int, topology: str): - self._data_cntr: CimBaseDataContainer = None + self._data_cntr: Optional[CimBaseDataContainer] = None self._max_tick = max_tick self._config_path = config_path self._start_tick = 0 @@ -39,11 +40,13 @@ def _init_data_container(self, topology_seed: int = None): config_path=config_path, max_tick=self._max_tick, start_tick=self._start_tick, topology_seed=topology_seed ) + elif os.path.exists(os.path.join(self._config_path, "order_proportion.csv")): + self._data_cntr = data_from_dumps(dumps_folder=self._config_path) else: # Real Data Mode: read data from input data files, no need for any config.yml. self._data_cntr = data_from_files(data_folder=self._config_path) - def reset(self, keep_seed): + def reset(self, keep_seed: bool): """Reset data container internal state""" if not keep_seed: self._init_data_container(random[ROUTE_INIT_RAND_KEY].randint(0, DATA_CONTAINER_INIT_SEED_LIMIT - 1)) @@ -87,9 +90,8 @@ def data_from_generator(config_path: str, max_tick: int, start_tick: int = 0, Returns: CimSyntheticDataContainer: Data container used to provide cim data related interfaces. """ - edg = CimDataGenerator() - - data_collection = edg.gen_data(config_path, start_tick=start_tick, max_tick=max_tick, topology_seed=topology_seed) + data_collection = gen_cim_data( + config_path, start_tick=start_tick, max_tick=max_tick, topology_seed=topology_seed) return CimSyntheticDataContainer(data_collection) diff --git a/maro/data_lib/cim/cim_data_dump.py b/maro/data_lib/cim/cim_data_dump.py index f99a0a335..a53770555 100644 --- a/maro/data_lib/cim/cim_data_dump.py +++ b/maro/data_lib/cim/cim_data_dump.py @@ -8,8 +8,25 @@ import numpy as np from yaml import safe_dump -from .cim_data_generator import CimDataGenerator -from .entities import CimSyntheticDataCollection +from .cim_data_generator import gen_cim_data +from .entities import CimSyntheticDataCollection, SyntheticPortSetting + + +def _dump_csv_file(file_path: str, headers: List[str], line_generator: callable): + """helper method to dump csv file + + Args: + file_path(str): path of output csv file + headers(List[str]): list of header + line_generator(callable): generator function to generate line to write + """ + with open(file_path, "wt+", newline="") as fp: + writer = csv.writer(fp) + + writer.writerow(headers) + + for line in line_generator(): + writer.writerow(line) class CimDataDumpUtil: @@ -70,7 +87,7 @@ def stop_generator(): stop.leave_tick ] - self._dump_csv_file(stops_file_path, headers, stop_generator) + _dump_csv_file(stops_file_path, headers, stop_generator) def _dump_ports(self, output_folder: str): """ @@ -86,6 +103,7 @@ def _dump_ports(self, output_folder: str): def port_generator(): for port in self._data_collection.port_settings: + assert isinstance(port, SyntheticPortSetting) yield [ port.index, port.name, @@ -99,7 +117,7 @@ def port_generator(): port.full_return_buffer.noise ] - self._dump_csv_file(ports_file_path, headers, port_generator) + _dump_csv_file(ports_file_path, headers, port_generator) def _dump_vessels(self, output_folder: str): """ @@ -137,7 +155,7 @@ def vessel_generator(): vessel.empty ] - self._dump_csv_file(vessels_file_path, headers, vessel_generator) + _dump_csv_file(vessels_file_path, headers, vessel_generator) def _dump_routes(self, output_folder: str, route_idx2name_dict: dict): """ @@ -161,7 +179,7 @@ def route_generator(): point.distance_to_next_port ] - self._dump_csv_file(routes_file_path, headers, route_generator) + _dump_csv_file(routes_file_path, headers, route_generator) def _dump_order_proportions(self, output_folder: str, port_idx2name_dict: dict): """ @@ -179,6 +197,7 @@ def _dump_order_proportions(self, output_folder: str, port_idx2name_dict: dict): def order_prop_generator(): for port in ports: + assert isinstance(port, SyntheticPortSetting) for prop in port.target_proportions: yield [ port.name, @@ -189,7 +208,7 @@ def order_prop_generator(): prop.noise ] - self._dump_csv_file(proportion_file_path, headers, order_prop_generator) + _dump_csv_file(proportion_file_path, headers, order_prop_generator) def _dump_misc(self, output_folder: str): """ @@ -213,22 +232,6 @@ def _dump_misc(self, output_folder: str): with open(misc_file_path, "wt+") as fp: safe_dump(misc_items, fp) - def _dump_csv_file(self, file_path: str, headers: List[str], line_generator: callable): - """helper method to dump csv file - - Args: - file_path(str): path of output csv file - headers(List[str]): list of header - line_generator(callable): generator function to generate line to write - """ - with open(file_path, "wt+", newline="") as fp: - writer = csv.writer(fp) - - writer.writerow(headers) - - for line in line_generator(): - writer.writerow(line) - def dump_from_config(config_file: str, output_folder: str, max_tick: int): """Dump cim data from config, this will call data generator to generate data , and dump it. @@ -245,9 +248,7 @@ def dump_from_config(config_file: str, output_folder: str, max_tick: int): assert output_folder is not None and os.path.exists(output_folder), f"Got output folder path: {output_folder}" assert max_tick is not None and max_tick > 0, f"Got max tick: {max_tick}" - generator = CimDataGenerator() - - data_collection = generator.gen_data(config_file, max_tick=max_tick, start_tick=0, topology_seed=None) + data_collection = gen_cim_data(config_file, max_tick=max_tick, start_tick=0, topology_seed=None) dump_util = CimDataDumpUtil(data_collection) diff --git a/maro/data_lib/cim/cim_data_generator.py b/maro/data_lib/cim/cim_data_generator.py index 056a5eca9..1a1ce43d1 100644 --- a/maro/data_lib/cim/cim_data_generator.py +++ b/maro/data_lib/cim/cim_data_generator.py @@ -7,192 +7,179 @@ from yaml import safe_load from maro.simulator.utils import random, seed -from maro.utils.exception.data_lib_exception import CimGeneratorInvalidParkingDuration from .entities import CimSyntheticDataCollection, OrderGenerateMode, Stop -from .global_order_proportion import GlobalOrderProportion -from .port_parser import PortsParser -from .route_parser import RoutesParser +from .parsers import parse_global_order_proportion, parse_ports, parse_routes, parse_vessels from .utils import ROUTE_INIT_RAND_KEY, apply_noise -from .vessel_parser import VesselsParser CIM_GENERATOR_VERSION = 0x000001 -class CimDataGenerator: - """Utility to generate cim data from configuration file.""" - - def __init__(self): - # parsers - self._ports_parser = PortsParser() - self._vessels_parser = VesselsParser() - self._routes_parser = RoutesParser() - self._global_order_proportion = GlobalOrderProportion() - - def gen_data( - self, config_file: str, max_tick: int, - start_tick: int = 0, - topology_seed: int = None - ) -> CimSyntheticDataCollection: - """Generate data with specified configurations. - - Args: - config_file(str): File of configuration (yaml). - max_tick(int): Max tick to generate. - start_tick(int): Start tick to generate. - topology_seed(int): Random seed of the business engine. \ - 'None' means using the seed in the configuration file. - - Returns: - CimSyntheticDataCollection: Data collection contains all cim data. - """ - - # read config - with open(config_file, "r") as fp: - conf: dict = safe_load(fp) - - if topology_seed is None: - topology_seed = conf["seed"] - - # set seed to generate data - seed(topology_seed) - - # misc configurations - total_containers = conf["total_containers"] - past_stop_number, future_stop_number = conf["stop_number"] - container_volumes = conf["container_volumes"] - - # parse configurations - vessel_mapping, vessels_setting = self._vessels_parser.parse(conf["vessels"]) - port_mapping, ports_setting = self._ports_parser.parse(conf["ports"], total_containers) - route_mapping, routes = self._routes_parser.parse(conf["routes"]) - global_order_proportion = self._global_order_proportion.parse( - conf["container_usage_proportion"], - total_containers, start_tick=start_tick, max_tick=max_tick) - - # extend routes with specified tick range - vessel_stops, vessel_period_without_noise = self._extend_route( - future_stop_number, max_tick, vessels_setting, ports_setting, port_mapping, routes, route_mapping) - - return CimSyntheticDataCollection( - # Port - port_settings=ports_setting, - port_mapping=port_mapping, - # Vessel - vessel_settings=vessels_setting, - vessel_mapping=vessel_mapping, - # Stop - vessel_stops=vessel_stops, - # Route - routes=routes, - route_mapping=route_mapping, - # Vessel Period - vessel_period_without_noise=vessel_period_without_noise, - # Volume/Container - container_volume=container_volumes[0], - # Cost Factors - load_cost_factor=conf["load_cost_factor"], - dsch_cost_factor=conf["dsch_cost_factor"], - # Visible Voyage Window - past_stop_number=past_stop_number, - future_stop_number=future_stop_number, - # Time Length of the Data Collection - max_tick=max_tick, - # Random Seed for Data Generation - seed=topology_seed, - # For Order Generation - total_containers=total_containers, - order_mode=OrderGenerateMode(conf["order_generate_mode"]), - order_proportion=global_order_proportion, - # Data Generator Version - version=CIM_GENERATOR_VERSION - ) - - def _extend_route( - self, future_stop_number: int, max_tick: int, - vessels_setting, ports_setting, port_mapping, routes, route_mapping - ) -> (List[List[Stop]], List[int]): - """Extend route with specified tick range.""" - - vessel_stops: List[List[Stop]] = [] - vessel_period_without_noise: List[int] = [] - - # fill the result stops with empty list - # NOTE: not using [[]] * N - for _ in range(len(vessels_setting)): - vessel_stops.append([]) - - # extend for each vessel - for vessel_setting in vessels_setting: - route_name = vessel_setting.route_name - - # route definition points from configuration - route_points = routes[route_mapping[route_name]] - route_length = len(route_points) - - loc_idx_in_route = 0 - - # find the start point - while route_points[loc_idx_in_route].port_name != vessel_setting.start_port_name: - loc_idx_in_route += 1 - - # update initial value fields - speed = vessel_setting.sailing_speed - speed_noise = vessel_setting.sailing_noise - duration = vessel_setting.parking_duration - duration_noise = vessel_setting.parking_noise - - tick = 0 - period_no_noise = 0 - extra_stop_counter = 0 - stop_index = 0 - - # unpack the route by max tick and future stop number - while extra_stop_counter <= future_stop_number: - cur_route_point = route_points[loc_idx_in_route] - port_idx = port_mapping[cur_route_point.port_name] - - # apply noise to parking duration - parking_duration = ceil(apply_noise(duration, duration_noise, random[ROUTE_INIT_RAND_KEY])) - - if parking_duration <= 0: - raise CimGeneratorInvalidParkingDuration() - - # a new stop - stop = Stop(stop_index, - tick, - tick + parking_duration, - port_idx, - vessel_setting.index) - - # append to current vessels stops list - vessel_stops[vessel_setting.index].append(stop) - - # use distance_to_next_port and speed (all with noise) to calculate tick of arrival and departure - distance_to_next_port = cur_route_point.distance_to_next_port - - # apply noise to speed - noised_speed = apply_noise(speed, speed_noise, random[ROUTE_INIT_RAND_KEY]) - sailing_duration = ceil(distance_to_next_port / noised_speed) - - # next tick - tick += parking_duration + sailing_duration - - # sailing durations without noise - whole_duration_no_noise = duration + ceil(distance_to_next_port / speed) - - # only add period at 1st route circle - period_no_noise += (whole_duration_no_noise if len( - vessel_stops[vessel_setting.index]) <= route_length else 0) - - # next location index - loc_idx_in_route = (loc_idx_in_route + 1) % route_length - - # counter to append extra stops which after max tick for future predict - extra_stop_counter += (1 if tick > max_tick else 0) - - stop_index += 1 - - vessel_period_without_noise.append(period_no_noise) - - return vessel_stops, vessel_period_without_noise +def _extend_route( + future_stop_number: int, max_tick: int, + vessels_setting, port_mapping, routes, route_mapping +) -> (List[List[Stop]], List[int]): + """Extend route with specified tick range.""" + + vessel_stops: List[List[Stop]] = [] + vessel_period_without_noise: List[int] = [] + + # fill the result stops with empty list + # NOTE: not using [[]] * N + for _ in range(len(vessels_setting)): + vessel_stops.append([]) + + # extend for each vessel + for vessel_setting in vessels_setting: + route_name = vessel_setting.route_name + + # route definition points from configuration + route_points = routes[route_mapping[route_name]] + route_length = len(route_points) + + loc_idx_in_route = 0 + + # find the start point + while route_points[loc_idx_in_route].port_name != vessel_setting.start_port_name: + loc_idx_in_route += 1 + + # update initial value fields + speed = vessel_setting.sailing_speed + speed_noise = vessel_setting.sailing_noise + duration = vessel_setting.parking_duration + duration_noise = vessel_setting.parking_noise + + tick = 0 + period_no_noise = 0 + extra_stop_counter = 0 + stop_index = 0 + + # unpack the route by max tick and future stop number + while extra_stop_counter <= future_stop_number: + cur_route_point = route_points[loc_idx_in_route] + port_idx = port_mapping[cur_route_point.port_name] + + # apply noise to parking duration + parking_duration = ceil(apply_noise(duration, duration_noise, random[ROUTE_INIT_RAND_KEY])) + assert parking_duration > 0 + + # a new stop + stop = Stop( + stop_index, + tick, + tick + parking_duration, + port_idx, + vessel_setting.index + ) + + # append to current vessels stops list + vessel_stops[vessel_setting.index].append(stop) + + # use distance_to_next_port and speed (all with noise) to calculate tick of arrival and departure + distance_to_next_port = cur_route_point.distance_to_next_port + + # apply noise to speed + noised_speed = apply_noise(speed, speed_noise, random[ROUTE_INIT_RAND_KEY]) + sailing_duration = ceil(distance_to_next_port / noised_speed) + + # next tick + tick += parking_duration + sailing_duration + + # sailing durations without noise + whole_duration_no_noise = duration + ceil(distance_to_next_port / speed) + + # only add period at 1st route circle + period_no_noise += (whole_duration_no_noise if len( + vessel_stops[vessel_setting.index]) <= route_length else 0) + + # next location index + loc_idx_in_route = (loc_idx_in_route + 1) % route_length + + # counter to append extra stops which after max tick for future predict + extra_stop_counter += (1 if tick > max_tick else 0) + + stop_index += 1 + + vessel_period_without_noise.append(period_no_noise) + + return vessel_stops, vessel_period_without_noise + + +def gen_cim_data( + config_file: str, max_tick: int, + start_tick: int = 0, + topology_seed: int = None +) -> CimSyntheticDataCollection: + """Generate data with specified configurations. + + Args: + config_file(str): File of configuration (yaml). + max_tick(int): Max tick to generate. + start_tick(int): Start tick to generate. + topology_seed(int): Random seed of the business engine. \ + 'None' means using the seed in the configuration file. + + Returns: + CimSyntheticDataCollection: Data collection contains all cim data. + """ + + # read config + with open(config_file, "r") as fp: + conf: dict = safe_load(fp) + + if topology_seed is None: + topology_seed = conf["seed"] + + # set seed to generate data + seed(topology_seed) + + # misc configurations + total_containers = conf["total_containers"] + past_stop_number, future_stop_number = conf["stop_number"] + container_volumes = conf["container_volumes"] + + # parse configurations + vessel_mapping, vessels_setting = parse_vessels(conf["vessels"]) + port_mapping, ports_setting = parse_ports(conf["ports"], total_containers) + route_mapping, routes = parse_routes(conf["routes"]) + global_order_proportion = parse_global_order_proportion( + conf["container_usage_proportion"], + total_containers, start_tick=start_tick, max_tick=max_tick) + + # extend routes with specified tick range + vessel_stops, vessel_period_without_noise = _extend_route( + future_stop_number, max_tick, vessels_setting, port_mapping, routes, route_mapping) + + return CimSyntheticDataCollection( + # Port + port_settings=ports_setting, + port_mapping=port_mapping, + # Vessel + vessel_settings=vessels_setting, + vessel_mapping=vessel_mapping, + # Stop + vessel_stops=vessel_stops, + # Route + routes=routes, + route_mapping=route_mapping, + # Vessel Period + vessel_period_without_noise=vessel_period_without_noise, + # Volume/Container + container_volume=container_volumes[0], + # Cost Factors + load_cost_factor=conf["load_cost_factor"], + dsch_cost_factor=conf["dsch_cost_factor"], + # Visible Voyage Window + past_stop_number=past_stop_number, + future_stop_number=future_stop_number, + # Time Length of the Data Collection + max_tick=max_tick, + # Random Seed for Data Generation + seed=topology_seed, + # For Order Generation + total_containers=total_containers, + order_mode=OrderGenerateMode(conf["order_generate_mode"]), + order_proportion=global_order_proportion, + # Data Generator Version + version=str(CIM_GENERATOR_VERSION) + ) diff --git a/maro/data_lib/cim/cim_data_loader.py b/maro/data_lib/cim/cim_data_loader.py index fae1e8c6d..45ed1a722 100644 --- a/maro/data_lib/cim/cim_data_loader.py +++ b/maro/data_lib/cim/cim_data_loader.py @@ -5,9 +5,8 @@ import math import os import time -from abc import ABC, abstractmethod from collections import defaultdict -from typing import Dict, List +from typing import Dict, List, Tuple import numpy as np from yaml import safe_load @@ -15,422 +14,328 @@ from maro.data_lib import BinaryReader from .entities import ( - CimBaseDataCollection, CimRealDataCollection, CimSyntheticDataCollection, NoisedItem, Order, OrderGenerateMode, - PortSetting, RoutePoint, Stop, SyntheticPortSetting, VesselSetting + CimRealDataCollection, CimSyntheticDataCollection, NoisedItem, Order, OrderGenerateMode, PortSetting, RoutePoint, + Stop, SyntheticPortSetting, VesselSetting ) -class CimBaseDataLoader(ABC): - """Utility to load data from dump folder and real input data folder.""" - - @abstractmethod - def load(self, data_folder: str) -> CimBaseDataCollection: - pass - - def _load_misc(self, data_folder: str) -> dict: - """Load misc items from yaml""" - misc_file_path = os.path.join(data_folder, "misc.yml") - for _ in range(3): - if not os.path.exists(misc_file_path): - time.sleep(10) - with open(misc_file_path, "rt") as fp: - return safe_load(fp) - - def _read_csv_lines(self, file_path: str): - """Helper to read and yield line from csv file""" - for _ in range(3): - if not os.path.exists(file_path): - time.sleep(10) - with open(file_path, "rt") as fp: - reader = csv.DictReader(fp) - - for line in reader: - yield line - - def _load_vessels(self, data_folder: str) -> (Dict[str, int], List[VesselSetting]): - vessel_mapping: Dict[str, int] = {} - vessels: List[VesselSetting] = [] - - vessels_file_path = os.path.join(data_folder, "vessels.csv") - - for line in self._read_csv_lines(vessels_file_path): - vessel_name = line["name"] - vessel_index = int(line["index"]) - - vessel_mapping[vessel_name] = vessel_index - - vessel = VesselSetting( - vessel_index, - vessel_name, - int(line["capacity"]), - line["route_name"], - line["start_port_name"], - float(line["sailing_speed"]), - float(line["sailing_speed_noise"]), - int(line["parking_duration"]), - float(line["parking_noise"]), - int(line["empty"]) - ) +def _load_misc(data_folder: str) -> dict: + """Load misc items from yaml""" + misc_file_path = os.path.join(data_folder, "misc.yml") + for _ in range(3): # pragma: no cover + if not os.path.exists(misc_file_path): + time.sleep(10) + with open(misc_file_path, "rt") as fp: + return safe_load(fp) - vessels.append(vessel) - return vessel_mapping, vessels +def _read_csv_lines(file_path: str): + """Helper to read and yield line from csv file""" + for _ in range(3): # pragma: no cover + if not os.path.exists(file_path): + time.sleep(10) + with open(file_path, "rt") as fp: + reader = csv.DictReader(fp) - def _load_vessel_period(self, data_folder: str) -> List[int]: - vessels_file_path = os.path.join(data_folder, "vessels.csv") + for line in reader: + yield line - periods_without_noise: List[int] = [] - for line in self._read_csv_lines(vessels_file_path): - periods_without_noise.append(int(line["period"])) - return periods_without_noise +def _load_vessels(data_folder: str) -> (Dict[str, int], List[VesselSetting]): + vessel_mapping: Dict[str, int] = {} + vessels: List[VesselSetting] = [] - def _calculate_vessel_period( - self, vessels: List[VesselSetting], routes: List[List[RoutePoint]], route_mapping: Dict[str, int] - ) -> List[int]: - expected_periods: List[int] = [] - for vessel in vessels: - route_points = routes[route_mapping[vessel.route_name]] + vessels_file_path = os.path.join(data_folder, "vessels.csv") - expected_period = 0 - for route_point in route_points: - expected_period += ( - vessel.parking_duration - + math.ceil(route_point.distance_to_next_port / vessel.sailing_speed) - ) - expected_periods.append(expected_period) + for line in _read_csv_lines(vessels_file_path): + vessel_name = line["name"] + vessel_index = int(line["index"]) - return expected_periods + vessel_mapping[vessel_name] = vessel_index - def _load_routes(self, data_folder: str) -> (Dict[str, int], List[List[RoutePoint]]): - route_mapping: Dict[str, int] = {} - routes: List[List[RoutePoint]] = [] + vessel = VesselSetting( + vessel_index, + vessel_name, + int(line["capacity"]), + line["route_name"], + line["start_port_name"], + float(line["sailing_speed"]), + float(line["sailing_speed_noise"]), + int(line["parking_duration"]), + float(line["parking_noise"]), + int(line["empty"]) + ) - route_file_path = os.path.join(data_folder, "routes.csv") + vessels.append(vessel) - for line in self._read_csv_lines(route_file_path): - route_index = int(line["index"]) - route_name = line["name"] + return vessel_mapping, vessels - route_mapping[route_name] = route_index - if route_index >= len(routes): - routes.append([]) +def _load_vessel_period(data_folder: str) -> List[int]: + vessels_file_path = os.path.join(data_folder, "vessels.csv") - route_point = RoutePoint( - route_index, line["port_name"], float(line["distance_to_next_port"])) + periods_without_noise: List[int] = [] + for line in _read_csv_lines(vessels_file_path): + periods_without_noise.append(int(line["period"])) - routes[route_index].append(route_point) + return periods_without_noise - return route_mapping, routes - def _load_stops(self, data_folder: str, vessel_number: int) -> List[List[Stop]]: - bin_path = os.path.join(data_folder, "stops.bin") - if os.path.exists(bin_path): - return self._load_stops_from_bin(bin_path, vessel_number) - else: - print(f"No stops binary file in {data_folder}, read from csv file instead...") - csv_path = os.path.join(data_folder, "stops.csv") - return self._load_stops_from_csv(csv_path, vessel_number) +def _calculate_vessel_period( + vessels: List[VesselSetting], routes: List[List[RoutePoint]], route_mapping: Dict[str, int] +) -> List[int]: + expected_periods: List[int] = [] + for vessel in vessels: + route_points = routes[route_mapping[vessel.route_name]] - def _load_stops_from_csv(self, stops_file_path: str, vessel_number: int) -> List[List[Stop]]: - stops: List[List[Stop]] = [] + expected_period = 0 + for route_point in route_points: + expected_period += ( + vessel.parking_duration + + math.ceil(route_point.distance_to_next_port / vessel.sailing_speed) + ) + expected_periods.append(expected_period) - for _ in range(vessel_number): - stops.append([]) + return expected_periods - for line in self._read_csv_lines(stops_file_path): - vessel_stops: List[Stop] = stops[int(line["vessel_index"])] - stop = Stop( - len(vessel_stops), - int(line["arrival_tick"]), - int(line["departure_tick"]), - int(line["port_index"]), - int(line["vessel_index"]) - ) +def _load_routes(data_folder: str) -> (Dict[str, int], List[List[RoutePoint]]): + route_mapping: Dict[str, int] = {} + routes: List[List[RoutePoint]] = [] + + route_file_path = os.path.join(data_folder, "routes.csv") + + for line in _read_csv_lines(route_file_path): + route_index = int(line["index"]) + route_name = line["name"] + + route_mapping[route_name] = route_index + + if route_index >= len(routes): + routes.append([]) + + route_point = RoutePoint( + route_index, line["port_name"], int(line["distance_to_next_port"])) + + routes[route_index].append(route_point) + + return route_mapping, routes + + +def _load_stops_from_csv(stops_file_path: str, vessel_number: int) -> List[List[Stop]]: + stops: List[List[Stop]] = [] + + for _ in range(vessel_number): + stops.append([]) - vessel_stops.append(stop) - - return stops - - def _load_stops_from_bin(self, stops_file_path: str, vessel_number: int) -> List[List[Stop]]: - stops: List[List[Stop]] = [] - - for _ in range(vessel_number): - stops.append([]) - - reader = BinaryReader(stops_file_path) - - for stop_item in reader.items(): - vessel_stops: List[Stop] = stops[stop_item.vessel_index] - - stop = Stop(len(vessel_stops), - stop_item.timestamp, - stop_item.leave_tick, - stop_item.port_index, - stop_item.vessel_index) - - vessel_stops.append(stop) - - return stops - - -class CimDumpDataLoader(CimBaseDataLoader): - """Utility to load data from dump folder""" - - def load(self, data_folder: str) -> CimSyntheticDataCollection: - """Load data from dump folder - - NOTE: - dumps folder should contains following files. - ports.csv, vessels.csv, routes.csv, order_proportion.csv, - global_order_proportion.txt, misc.yml, stops.bin - - Args: - data_folders(str): folder that contains dumped files - - Returns: - CimSyntheticDataCollection: data collection for data container - """ - # load from files - misc_items = self._load_misc(data_folder) - order_target_proportion = self._load_order_proportions(data_folder) - port_mapping, ports = self._load_ports(data_folder, order_target_proportion) - route_mapping, routes = self._load_routes(data_folder) - vessel_mapping, vessels = self._load_vessels(data_folder) - periods_without_noise = self._load_vessel_period(data_folder) - stops = self._load_stops(data_folder, len(vessels)) - global_order_proportions = self._load_global_order_proportions(data_folder) - - # construct data collection - # NOTE: this is a namedtuple, so out-side cannot change it - data_collection = CimSyntheticDataCollection( - port_settings=ports, - port_mapping=port_mapping, - vessel_settings=vessels, - vessel_mapping=vessel_mapping, - vessel_stops=stops, - routes=routes, - route_mapping=route_mapping, - vessel_period_without_noise=periods_without_noise, - container_volume=misc_items["container_volume"], - load_cost_factor=misc_items["load_cost_factor"], - dsch_cost_factor=misc_items["dsch_cost_factor"], - past_stop_number=misc_items["past_stop_number"], - future_stop_number=misc_items["future_stop_number"], - max_tick=misc_items["max_tick"], - seed=misc_items["seed"], - total_containers=misc_items["total_container"], - order_mode=OrderGenerateMode(misc_items["order_mode"]), - order_proportion=global_order_proportions, - version=misc_items["version"] + for line in _read_csv_lines(stops_file_path): + vessel_stops: List[Stop] = stops[int(line["vessel_index"])] + + stop = Stop( + len(vessel_stops), + int(line["arrival_tick"]), + int(line["departure_tick"]), + int(line["port_index"]), + int(line["vessel_index"]) ) - return data_collection + vessel_stops.append(stop) - def _load_global_order_proportions(self, data_folder: str) -> np.ndarray: - """load global order proportions from txt file""" - global_order_prop_file = os.path.join( - data_folder, "global_order_proportion.txt") + return stops - return np.loadtxt(global_order_prop_file) - def _load_order_proportions(self, data_folder: str) -> Dict[int, List[NoisedItem]]: - """Load target order proportions from file""" - target_proportions: Dict[int, List[NoisedItem]] = defaultdict(list) +def _load_stops_from_bin(stops_file_path: str, vessel_number: int) -> List[List[Stop]]: + stops: List[List[Stop]] = [] - proportion_file_path = os.path.join(data_folder, "order_proportion.csv") + for _ in range(vessel_number): + stops.append([]) - for line in self._read_csv_lines(proportion_file_path): - source_port_index = int(line["source_port_index"]) + reader = BinaryReader(stops_file_path) - target_prop = NoisedItem( - int(line["dest_port_index"]), - float(line["proportion"]), - float(line["proportion_noise"]) - ) + for stop_item in reader.items(): + vessel_stops: List[Stop] = stops[stop_item.vessel_index] - target_proportions[source_port_index].append(target_prop) + stop = Stop(len(vessel_stops), + stop_item.timestamp, + stop_item.leave_tick, + stop_item.port_index, + stop_item.vessel_index) - return target_proportions + vessel_stops.append(stop) - def _load_ports(self, data_folder: str, order_target_proportion: dict) -> dict: - ports_file_path = os.path.join(data_folder, "ports.csv") + return stops - port_mapping: Dict[str, int] = {} - ports: List[SyntheticPortSetting] = [] - for line in self._read_csv_lines(ports_file_path): - port_name = line["name"] - port_index = int(line["index"]) +def _load_stops(data_folder: str, vessel_number: int) -> List[List[Stop]]: + bin_path = os.path.join(data_folder, "stops.bin") + if os.path.exists(bin_path): + return _load_stops_from_bin(bin_path, vessel_number) + else: + print(f"No stops binary file in {data_folder}, read from csv file instead...") + csv_path = os.path.join(data_folder, "stops.csv") + return _load_stops_from_csv(csv_path, vessel_number) - port_mapping[port_name] = port_index - full_rtn_buffer = NoisedItem( - port_index, - int(line["full_return_buffer"]), - int(line["full_return_buffer_noise"])) +def _load_global_order_proportions(data_folder: str) -> np.ndarray: + """load global order proportions from txt file""" + global_order_prop_file = os.path.join( + data_folder, "global_order_proportion.txt") - empty_rtn_buffer = NoisedItem( - port_index, - int(line["empty_return_buffer"]), - int(line["empty_return_buffer_noise"])) + return np.loadtxt(global_order_prop_file) - source_order_proportion = NoisedItem( - port_index, - float(line["order_proportion"]), - float(line["order_proportion_noise"]) - ) - port = SyntheticPortSetting( - port_index, - port_name, - int(line["capacity"]), - int(line["empty"]), - empty_rtn_buffer, - full_rtn_buffer, - source_order_proportion, - order_target_proportion[port_index] - ) +def _load_order_proportions(data_folder: str) -> Dict[int, List[NoisedItem]]: + """Load target order proportions from file""" + target_proportions: Dict[int, List[NoisedItem]] = defaultdict(list) + + proportion_file_path = os.path.join(data_folder, "order_proportion.csv") + + for line in _read_csv_lines(proportion_file_path): + source_port_index = int(line["source_port_index"]) - ports.append(port) - - return port_mapping, ports - - -class CimRealDataLoader(CimBaseDataLoader): - """Utility to load data from data folder""" - - def load(self, data_folder: str) -> CimRealDataCollection: - """Load data from data folder - - NOTE: - data folder should contains following files. - ports.csv, vessels.csv, routes.csv, order.csv, - misc.yml, stops.csv - - Args: - data_folders(str): folder that contains data files. - - Returns: - CimDataCollection: data collection for data container - """ - # load from files - misc_items = self._load_misc(data_folder) - port_mapping, ports = self._load_ports(data_folder) - route_mapping, routes = self._load_routes(data_folder) - vessel_mapping, vessels = self._load_vessels(data_folder) - periods_without_noise = self._calculate_vessel_period(vessels, routes, route_mapping) - stops = self._load_stops(data_folder, len(vessels)) - orders = self._load_orders(data_folder) - - # construct data collection - # NOTE: this is a namedtuple, so out-side cannot change it - data_collection = CimRealDataCollection( - port_settings=ports, - port_mapping=port_mapping, - vessel_settings=vessels, - vessel_mapping=vessel_mapping, - vessel_stops=stops, - routes=routes, - route_mapping=route_mapping, - vessel_period_without_noise=periods_without_noise, - container_volume=misc_items["container_volume"], - load_cost_factor=misc_items["load_cost_factor"], - dsch_cost_factor=misc_items["dsch_cost_factor"], - past_stop_number=misc_items["past_stop_number"], - future_stop_number=misc_items["future_stop_number"], - max_tick=misc_items["max_tick"], - seed=misc_items["seed"], - orders=orders + target_prop = NoisedItem( + int(line["dest_port_index"]), + float(line["proportion"]), + float(line["proportion_noise"]) ) - return data_collection + target_proportions[source_port_index].append(target_prop) - def _load_ports(self, data_folder: str) -> dict: - ports_file_path = os.path.join(data_folder, "ports.csv") + return target_proportions - port_mapping: Dict[str, int] = {} - ports: List[PortSetting] = [] - for line in self._read_csv_lines(ports_file_path): - port_index = int(line["index"]) - port_name = line["name"] +def _load_ports_dump( + data_folder: str, order_target_proportion: Dict[int, List[NoisedItem]] +) -> Tuple[dict, List[SyntheticPortSetting]]: + ports_file_path = os.path.join(data_folder, "ports.csv") - port_mapping[port_name] = port_index + port_mapping: Dict[str, int] = {} + ports: List[SyntheticPortSetting] = [] - full_rtn_buffer = NoisedItem( - port_index, - int(line["full_return_buffer"]), - int(line["full_return_buffer_noise"]) - ) + for line in _read_csv_lines(ports_file_path): + port_name = line["name"] + port_index = int(line["index"]) - empty_rtn_buffer = NoisedItem( - port_index, - int(line["empty_return_buffer"]), - int(line["empty_return_buffer_noise"]) - ) + port_mapping[port_name] = port_index - port = PortSetting( - port_index, - port_name, - int(line["capacity"]), - int(line["empty"]), - empty_rtn_buffer, - full_rtn_buffer - ) + full_rtn_buffer = NoisedItem( + port_index, + int(line["full_return_buffer"]), + int(line["full_return_buffer_noise"])) + + empty_rtn_buffer = NoisedItem( + port_index, + int(line["empty_return_buffer"]), + int(line["empty_return_buffer_noise"])) + + source_order_proportion = NoisedItem( + port_index, + float(line["order_proportion"]), + float(line["order_proportion_noise"]) + ) - ports.append(port) - - return port_mapping, ports - - def _load_orders(self, data_folder: str) -> Dict[int, List[Order]]: - bin_path = os.path.join(data_folder, "orders.bin") - if os.path.exists(bin_path): - return self._load_orders_from_bin(bin_path) - else: - print(f"No orders binary file in {data_folder}, read from csv file instead...") - csv_path = os.path.join(data_folder, "orders.csv") - return self._load_orders_from_csv(csv_path) - - def _load_orders_from_csv(self, order_file_path: str) -> Dict[int, List[Order]]: - orders: Dict[int, List[Order]] = {} - - for line in self._read_csv_lines(order_file_path): - tick = int(line["tick"]) - if tick not in orders: - orders[tick] = [] - orders[tick].append( - Order( - int(line["tick"]), - int(line["source_port_index"]), - int(line["dest_port_index"]), - int(line["quantity"]) - ) + port = SyntheticPortSetting( + port_index, + port_name, + int(line["capacity"]), + int(line["empty"]), + empty_rtn_buffer, + full_rtn_buffer, + source_order_proportion, + order_target_proportion[port_index] + ) + + ports.append(port) + + return port_mapping, ports + + +def _load_ports_real(data_folder: str) -> Tuple[dict, List[PortSetting]]: + ports_file_path = os.path.join(data_folder, "ports.csv") + + port_mapping: Dict[str, int] = {} + ports: List[PortSetting] = [] + + for line in _read_csv_lines(ports_file_path): + port_index = int(line["index"]) + port_name = line["name"] + + port_mapping[port_name] = port_index + + full_rtn_buffer = NoisedItem( + port_index, + int(line["full_return_buffer"]), + int(line["full_return_buffer_noise"]) + ) + + empty_rtn_buffer = NoisedItem( + port_index, + int(line["empty_return_buffer"]), + int(line["empty_return_buffer_noise"]) + ) + + port = PortSetting( + port_index, + port_name, + int(line["capacity"]), + int(line["empty"]), + empty_rtn_buffer, + full_rtn_buffer + ) + + ports.append(port) + + return port_mapping, ports + + +def _load_orders_from_csv(order_file_path: str) -> Dict[int, List[Order]]: + orders: Dict[int, List[Order]] = {} + + for line in _read_csv_lines(order_file_path): + tick = int(line["tick"]) + if tick not in orders: + orders[tick] = [] + orders[tick].append( + Order( + int(line["tick"]), + int(line["source_port_index"]), + int(line["dest_port_index"]), + int(line["quantity"]) ) + ) - return orders + return orders - def _load_orders_from_bin(self, order_file_path: str) -> Dict[int, List[Order]]: - orders: Dict[int, List[Order]] = {} - reader = BinaryReader(order_file_path) +def _load_orders_from_bin(order_file_path: str) -> Dict[int, List[Order]]: + orders: Dict[int, List[Order]] = {} - for order in reader.items(): - tick = order.timestamp - if tick not in orders: - orders[tick] = [] - orders[tick].append( - Order( - order.timestamp, - order.src_port_index, - order.dest_port_index, - order.quantity - ) + reader = BinaryReader(order_file_path) + + for order in reader.items(): + tick = order.timestamp + if tick not in orders: + orders[tick] = [] + orders[tick].append( + Order( + order.timestamp, + order.src_port_index, + order.dest_port_index, + order.quantity ) + ) + + return orders - return orders + +def _load_orders(data_folder: str) -> Dict[int, List[Order]]: + bin_path = os.path.join(data_folder, "orders.bin") + if os.path.exists(bin_path): + return _load_orders_from_bin(bin_path) + else: + print(f"No orders binary file in {data_folder}, read from csv file instead...") + csv_path = os.path.join(data_folder, "orders.csv") + return _load_orders_from_csv(csv_path) def load_from_folder(source_folder: str) -> CimSyntheticDataCollection: @@ -447,9 +352,39 @@ def load_from_folder(source_folder: str) -> CimSyntheticDataCollection: Returns: CimSyntheticDataCollection: Data collection for cim data container. """ - loader = CimDumpDataLoader() - - return loader.load(source_folder) + # load from files + misc_items = _load_misc(source_folder) + order_target_proportion = _load_order_proportions(source_folder) + port_mapping, ports = _load_ports_dump(source_folder, order_target_proportion) + route_mapping, routes = _load_routes(source_folder) + vessel_mapping, vessels = _load_vessels(source_folder) + periods_without_noise = _load_vessel_period(source_folder) + stops = _load_stops(source_folder, len(vessels)) + global_order_proportions = _load_global_order_proportions(source_folder) + + # construct data collection + # NOTE: this is a namedtuple, so out-side cannot change it + return CimSyntheticDataCollection( + port_settings=ports, + port_mapping=port_mapping, + vessel_settings=vessels, + vessel_mapping=vessel_mapping, + vessel_stops=stops, + routes=routes, + route_mapping=route_mapping, + vessel_period_without_noise=periods_without_noise, + container_volume=misc_items["container_volume"], + load_cost_factor=misc_items["load_cost_factor"], + dsch_cost_factor=misc_items["dsch_cost_factor"], + past_stop_number=misc_items["past_stop_number"], + future_stop_number=misc_items["future_stop_number"], + max_tick=misc_items["max_tick"], + seed=misc_items["seed"], + total_containers=misc_items["total_container"], + order_mode=OrderGenerateMode(misc_items["order_mode"]), + order_proportion=global_order_proportions, + version=misc_items["version"] + ) def load_real_data_from_folder(source_folder: str) -> CimRealDataCollection: @@ -460,11 +395,37 @@ def load_real_data_from_folder(source_folder: str) -> CimRealDataCollection: ports.csv, vessels.csv, routes.csv, misc.yml, stops.csv, orders.csv. Args: - source_folder(str): Source folder contains data files. + source_folder (str): Source folder contains data files. Returns: CimRealDataCollection: Data collection for cim data container. """ - loader = CimRealDataLoader() - - return loader.load(source_folder) + # load from files + misc_items = _load_misc(source_folder) + port_mapping, ports = _load_ports_real(source_folder) + route_mapping, routes = _load_routes(source_folder) + vessel_mapping, vessels = _load_vessels(source_folder) + periods_without_noise = _calculate_vessel_period(vessels, routes, route_mapping) + stops = _load_stops(source_folder, len(vessels)) + orders = _load_orders(source_folder) + + # construct data collection + # NOTE: this is a namedtuple, so out-side cannot change it + return CimRealDataCollection( + port_settings=ports, + port_mapping=port_mapping, + vessel_settings=vessels, + vessel_mapping=vessel_mapping, + vessel_stops=stops, + routes=routes, + route_mapping=route_mapping, + vessel_period_without_noise=periods_without_noise, + container_volume=misc_items["container_volume"], + load_cost_factor=misc_items["load_cost_factor"], + dsch_cost_factor=misc_items["dsch_cost_factor"], + past_stop_number=misc_items["past_stop_number"], + future_stop_number=misc_items["future_stop_number"], + max_tick=misc_items["max_tick"], + seed=misc_items["seed"], + orders=orders + ) diff --git a/maro/data_lib/cim/entities.py b/maro/data_lib/cim/entities.py index 7ae9e3a9d..ffd929b01 100644 --- a/maro/data_lib/cim/entities.py +++ b/maro/data_lib/cim/entities.py @@ -3,7 +3,7 @@ from dataclasses import dataclass from enum import Enum -from typing import Dict, List +from typing import Dict, List, Optional import numpy as np @@ -33,14 +33,14 @@ class PortSetting: name: str capacity: int empty: int - empty_return_buffer: int - full_return_buffer: int + empty_return_buffer: Optional[NoisedItem] + full_return_buffer: Optional[NoisedItem] @dataclass(frozen=True) class SyntheticPortSetting(PortSetting): - source_proportion: float - target_proportions: float + source_proportion: Optional[NoisedItem] + target_proportions: Optional[List[NoisedItem]] # settings for vessel @@ -50,11 +50,11 @@ class VesselSetting: name: str capacity: int route_name: str - start_port_name: str - sailing_speed: float - sailing_noise: float - parking_duration: int - parking_noise: float + start_port_name: Optional[str] + sailing_speed: Optional[float] + sailing_noise: Optional[float] + parking_duration: Optional[int] + parking_noise: Optional[float] empty: int @@ -114,7 +114,7 @@ class CimBaseDataCollection: vessel_settings: List[VesselSetting] vessel_mapping: Dict[str, int] # Stop - vessel_stops: List[List[Stop]] + vessel_stops: List[List[Optional[Stop]]] # Route routes: List[List[RoutePoint]] route_mapping: Dict[str, int] diff --git a/maro/data_lib/cim/global_order_proportion.py b/maro/data_lib/cim/global_order_proportion.py deleted file mode 100644 index 63de50483..000000000 --- a/maro/data_lib/cim/global_order_proportion.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT license. - -from math import floor -from typing import Union - -import numpy as np - -from maro.simulator.utils import random - -from .utils import ORDER_INIT_RAND_KEY, apply_noise, clip - - -class GlobalOrderProportion: - """Helper used to generate order proportion at specified tick range. - """ - - def parse(self, conf: dict, total_container: int, max_tick: int, start_tick: int = 0) -> np.ndarray: - """Parse specified configuration, and generate order proportion. - - Args: - conf (dict): Configuration to parse. - total_container (int): Total containers in this environment. - max_tick (int): Max tick to generate. - start_tick (int): Start tick to generate. - - Returns: - np.ndarray: 1-dim numpy array for specified range. - """ - durations: int = max_tick - start_tick - - order_proportion = np.zeros(durations, dtype="i") - - # read configurations - period: int = conf["period"] - noise: Union[float, int] = conf["sample_noise"] - sample_nodes: list = [(x, y) for x, y in conf["sample_nodes"]] - - # step 1: interpolate with configured sample nodes to generate proportion in period - - # check if there is 0 and max_tick - 1 node exist ,insert if not exist - if sample_nodes[0][0] != 0: - sample_nodes.insert(0, (0, 0)) - - if sample_nodes[-1][0] != period - 1: - sample_nodes.append((period - 1, 0)) - - # our xp is period - xp = [node[0] for node in sample_nodes] - yp = [node[1] for node in sample_nodes] - - # distribution per period - order_period_distribution = np.interp( - [t for t in range(period)], xp, yp) - - # step 2: extend to specified range - - for t in range(start_tick, max_tick): - orders = order_period_distribution[t % period] # ratio - - # apply noise if the distribution not zero - if orders != 0: - if noise != 0: - orders = apply_noise(orders, noise, random[ORDER_INIT_RAND_KEY]) - - # clip and gen order - orders = floor(clip(0, 1, orders) * total_container) - order_proportion[t - start_tick] = orders - - return order_proportion diff --git a/maro/data_lib/cim/parsers.py b/maro/data_lib/cim/parsers.py new file mode 100644 index 000000000..393a6dceb --- /dev/null +++ b/maro/data_lib/cim/parsers.py @@ -0,0 +1,210 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +from math import floor +from typing import Dict, List, Union + +import numpy as np + +from maro.data_lib.cim.entities import NoisedItem, RoutePoint, SyntheticPortSetting, VesselSetting +from maro.data_lib.cim.utils import ORDER_INIT_RAND_KEY, apply_noise, clip +from maro.simulator.utils import random + + +def parse_vessels(conf: dict) -> (Dict[str, int], List[VesselSetting]): + """Parse specified vessel configurations. + + Args: + conf(dict): Configurations to parse. + + Returns: + (Dict[str, int], List[VesselSetting]): Vessel mappings (name to index), and settings list for all vessels. + + """ + mapping: Dict[str, int] = {} + vessels: List[VesselSetting] = [] + + index = 0 + + for vessel_name, vessel_node in conf.items(): + mapping[vessel_name] = index + + sailing = vessel_node["sailing"] + parking = vessel_node["parking"] + route = vessel_node["route"] + + vessels.append( + VesselSetting( + index, + vessel_name, + vessel_node["capacity"], + route["route_name"], + route["initial_port_name"], + sailing["speed"], + sailing["noise"], + parking["duration"], + parking["noise"], + # default no empty + vessel_node.get("empty", 0) + ) + ) + + index += 1 + + return mapping, vessels + + +def parse_global_order_proportion(conf: dict, total_container: int, max_tick: int, start_tick: int = 0) -> np.ndarray: + """Parse specified configuration, and generate order proportion. + + Args: + conf (dict): Configuration to parse. + total_container (int): Total containers in this environment. + max_tick (int): Max tick to generate. + start_tick (int): Start tick to generate. + + Returns: + np.ndarray: 1-dim numpy array for specified range. + """ + durations: int = max_tick - start_tick + + order_proportion = np.zeros(durations, dtype="i") + + # read configurations + period: int = conf["period"] + noise: Union[float, int] = conf["sample_noise"] + sample_nodes: list = [(x, y) for x, y in conf["sample_nodes"]] + + # step 1: interpolate with configured sample nodes to generate proportion in period + + # check if there is 0 and max_tick - 1 node exist ,insert if not exist + if sample_nodes[0][0] != 0: + sample_nodes.insert(0, (0, 0)) + if sample_nodes[-1][0] != period - 1: + sample_nodes.append((period - 1, 0)) + + # our xp is period + xp = [node[0] for node in sample_nodes] + yp = [node[1] for node in sample_nodes] + + # distribution per period + order_period_distribution = np.interp(list(range(period)), xp, yp) + + # step 2: extend to specified range + for t in range(start_tick, max_tick): + orders = order_period_distribution[t % period] # ratio + + # apply noise if the distribution not zero + if orders != 0: + if noise != 0: + orders = apply_noise(orders, noise, random[ORDER_INIT_RAND_KEY]) + + # clip and gen order + orders = floor(clip(0, 1, orders) * total_container) + order_proportion[t - start_tick] = orders + + return order_proportion + + +def parse_routes(conf: dict) -> (Dict[str, int], List[List[RoutePoint]]): + """Parse specified route configuration. + + Args: + conf (dict): Configuration to parse. + + Returns: + (Dict[str, int], List[List[RoutePoint]]): Route mapping (name to index), + and list of route point list (index is route index). + """ + routes: List[List[RoutePoint]] = [] + route_mapping: Dict[str, int] = {} # name->idx + + idx = 0 + + for name, node in conf.items(): + route_mapping[name] = idx + + routes.append([RoutePoint(idx, n["port_name"], n["distance_to_next_port"]) for n in node]) + + idx += 1 + + return route_mapping, routes + + +def parse_ports(conf: dict, total_container: int) -> (Dict[str, int], List[SyntheticPortSetting]): + """Parse specified port configurations. + + Args: + conf (dict): Configuration to parse. + total_container (int): Total container in current environment, used to calculate initial empty. + + Returns: + (Dict[str, int], List[SyntheticPortSetting]): Port mapping (name to index), list of port settings. + """ + # sum of ratio cannot large than 1 + total_ratio = sum([p["initial_container_proportion"] for p in conf.values()]) + + assert round(total_ratio, 7) == 1 + + ports_mapping: Dict[str, int] = {} + + index = 0 + + # step 1: create mapping + for port_name, port_info in conf.items(): + ports_mapping[port_name] = index + index += 1 + + port_settings: List[SyntheticPortSetting] = [] + + for port_idx, port in enumerate(conf.items()): + port_name, port_info = port + + # step 2: update initial values + empty_ratio = port_info["initial_container_proportion"] + + # full return buffer configurations + full_return_conf = port_info["full_return"] + empty_return_conf = port_info["empty_return"] + + dist_conf = port_info["order_distribution"] + source_dist_conf = dist_conf["source"] + + targets_dist = [] + + # orders distribution to destination + if "targets" in dist_conf: + for target_port_name, target_conf in dist_conf["targets"].items(): + dist = NoisedItem( + ports_mapping[target_port_name], + target_conf["proportion"], + target_conf["noise"]) + + targets_dist.append(dist) + + port_setting = SyntheticPortSetting( + port_idx, + port_name, + port_info["capacity"], + int(empty_ratio * total_container), + NoisedItem( + port_idx, + empty_return_conf["buffer_ticks"], + empty_return_conf["noise"] + ), + NoisedItem( + port_idx, + full_return_conf["buffer_ticks"], + full_return_conf["noise"] + ), + NoisedItem( + port_idx, + source_dist_conf["proportion"], + source_dist_conf["noise"] + ), + targets_dist + ) + + port_settings.append(port_setting) + + return ports_mapping, port_settings diff --git a/maro/data_lib/cim/port_buffer_tick_wrapper.py b/maro/data_lib/cim/port_buffer_tick_wrapper.py index 12a2a3863..d1336a720 100644 --- a/maro/data_lib/cim/port_buffer_tick_wrapper.py +++ b/maro/data_lib/cim/port_buffer_tick_wrapper.py @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT license. - from math import ceil +from typing import Callable from maro.simulator.utils import random @@ -21,7 +21,7 @@ class PortBufferTickWrapper: attribute_func (callable): Function to get attribute, used to switch between empty and full. """ - def __init__(self, data: CimBaseDataCollection, attribute_func: callable): + def __init__(self, data: CimBaseDataCollection, attribute_func: Callable[[PortSetting], NoisedItem]) -> None: self._ports = data.port_settings self._attribute_func = attribute_func diff --git a/maro/data_lib/cim/port_parser.py b/maro/data_lib/cim/port_parser.py deleted file mode 100644 index 75e525f30..000000000 --- a/maro/data_lib/cim/port_parser.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT license. - -from typing import Dict, List - -from .entities import NoisedItem, SyntheticPortSetting - - -class PortsParser: - """Parser used to parse port information from configurations - """ - - def parse(self, conf: dict, total_container: int) -> (Dict[str, int], List[SyntheticPortSetting]): - """Parse specified port configurations. - - Args: - conf (dict): Configuration to parse. - total_container (int): Total container in current environment, used to calculate initial empty. - - Returns: - (Dict[str, int], List[SyntheticPortSetting]): Port mapping (name to index), list of port settings. - """ - # sum of ratio cannot large than 1 - total_ratio = sum([p["initial_container_proportion"] for p in conf.values()]) - - assert round(total_ratio, 7) == 1 - - ports_mapping: Dict[str, int] = {} - - index = 0 - - # step 1: create mapping - for port_name, port_info in conf.items(): - ports_mapping[port_name] = index - index += 1 - - port_settings: List[SyntheticPortSetting] = [] - - for port_idx, port in enumerate(conf.items()): - port_name, port_info = port - - # step 2: update initial values - empty_ratio = port_info["initial_container_proportion"] - - # full return buffer configurations - full_return_conf = port_info["full_return"] - empty_return_conf = port_info["empty_return"] - - dist_conf = port_info["order_distribution"] - source_dist_conf = dist_conf["source"] - - targets_dist = [] - - # orders distribution to destination - if "targets" in dist_conf: - for target_port_name, target_conf in dist_conf["targets"].items(): - dist = NoisedItem( - ports_mapping[target_port_name], - target_conf["proportion"], - target_conf["noise"]) - - targets_dist.append(dist) - - port_setting = SyntheticPortSetting( - port_idx, - port_name, - port_info["capacity"], - int(empty_ratio * total_container), - NoisedItem( - port_idx, - empty_return_conf["buffer_ticks"], - empty_return_conf["noise"]), - NoisedItem( - port_idx, - full_return_conf["buffer_ticks"], - full_return_conf["noise"]), - NoisedItem( - port_idx, - source_dist_conf["proportion"], - source_dist_conf["noise"]), - targets_dist) - - port_settings.append(port_setting) - - return ports_mapping, port_settings diff --git a/maro/data_lib/cim/route_parser.py b/maro/data_lib/cim/route_parser.py deleted file mode 100644 index c942cac17..000000000 --- a/maro/data_lib/cim/route_parser.py +++ /dev/null @@ -1,36 +0,0 @@ - -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT license. - -from typing import Dict, List - -from .entities import RoutePoint - - -class RoutesParser: - """Parser used to parse route information from configurations. - """ - - def parse(self, conf: dict) -> (Dict[str, int], List[List[RoutePoint]]): - """Parse specified route configuration. - - Args: - conf (dict): Configuration to parse. - - Returns: - (Dict[str, int], List[List[RoutePoint]]): Route mapping (name to index), - and list of route point list (index is route index). - """ - routes: List[List[RoutePoint]] = [] - route_mapping: Dict[str, int] = {} # name->idx - - idx = 0 - - for name, node in conf.items(): - route_mapping[name] = idx - - routes.append([RoutePoint(idx, n["port_name"], n["distance_to_next_port"]) for n in node]) - - idx += 1 - - return route_mapping, routes diff --git a/maro/data_lib/cim/utils.py b/maro/data_lib/cim/utils.py index 7b28d6100..579b939c8 100644 --- a/maro/data_lib/cim/utils.py +++ b/maro/data_lib/cim/utils.py @@ -4,14 +4,21 @@ from random import Random from typing import List, Union -# we keep 4 random generator to make the result is reproduceable with same seed(s), no matter if agent passed actions -ROUTE_INIT_RAND_KEY = "route_init" +# we keep 4 random generator to make the result is reproduce-able with same seed(s), no matter if agent passed actions +from maro.simulator.utils import random + ORDER_INIT_RAND_KEY = "order_init" -BUFFER_TICK_RAND_KEY = "buffer_time" +ROUTE_INIT_RAND_KEY = "route_init" ORDER_NUM_RAND_KEY = "order_number" +BUFFER_TICK_RAND_KEY = "buffer_time" DATA_CONTAINER_INIT_SEED_LIMIT = 4096 +random.create_instance(ORDER_INIT_RAND_KEY) +random.create_instance(ROUTE_INIT_RAND_KEY) +random.create_instance(ORDER_NUM_RAND_KEY) +random.create_instance(BUFFER_TICK_RAND_KEY) + def clip(min_val: Union[int, float], max_val: Union[int, float], value: Union[int, float]) -> Union[int, float]: """Clip value between specified range @@ -53,7 +60,11 @@ def list_sum_normalize(num_list: List[Union[int, float]]) -> List[float]: t = sum(num_list) # avoid dive zero exception - if t == 0: - return 0 + return num_list if t == 0 else [d / t for d in num_list] + + +def extract_key_of_three_ints(key) -> (int, int, int): + assert type(key) == tuple or type(key) == list + assert len(key) == 3 - return [d / t for d in num_list] + return int(key[0]), int(key[1]), int(key[2]) diff --git a/maro/data_lib/cim/vessel_future_stops_prediction.py b/maro/data_lib/cim/vessel_future_stops_prediction.py index db37ed828..3b2be4ca5 100644 --- a/maro/data_lib/cim/vessel_future_stops_prediction.py +++ b/maro/data_lib/cim/vessel_future_stops_prediction.py @@ -5,6 +5,7 @@ from typing import List from .entities import CimBaseDataCollection, Stop +from .utils import extract_key_of_three_ints class VesselFutureStopsPrediction: @@ -18,30 +19,25 @@ class VesselFutureStopsPrediction: stops = data_cntr.vessel_future_stops[0] """ - def __init__(self, data: CimBaseDataCollection): + def __init__(self, data: CimBaseDataCollection) -> None: self._vessels = data.vessel_settings self._stops = data.vessel_stops self._routes = data.routes self._route_mapping = data.route_mapping self._port_mapping = data.port_mapping self._stop_number = data.future_stop_number - self._vessel_start_port_offsets = self._make_vessel_start_port_offset() + self._vessel_start_port_offsets = self._make_vessel_start_port_offsets() def __getitem__(self, key): """Used to support querying future stops by vessel index, last location index, next location index.""" - assert type(key) == tuple or type(key) == list - assert len(key) == 3 - - vessel_idx = key[0] - last_loc_idx = key[1] - loc_idx = key[2] + vessel_idx, last_loc_idx, loc_idx = extract_key_of_three_ints(key) # ignore current port if parking last_stop_idx = loc_idx + (0 if last_loc_idx == loc_idx else -1) return self._predict_future_stops(vessel_idx, last_stop_idx, self._stop_number) - def _make_vessel_start_port_offset(self) -> List[int]: + def _make_vessel_start_port_offsets(self) -> List[int]: vessel_start_port_offsets = [] for vessel in self._vessels: route_points = self._routes[self._route_mapping[vessel.route_name]] @@ -50,7 +46,7 @@ def _make_vessel_start_port_offset(self) -> List[int]: vessel_start_port_offsets.append(vessel_start_port_offset) return vessel_start_port_offsets - def _predict_future_stops(self, vessel_idx: int, last_stop_idx: int, stop_number: int): + def _predict_future_stops(self, vessel_idx: int, last_stop_idx: int, stop_number: int) -> List[Stop]: """Do predict future stops. """ vessel = self._vessels[vessel_idx] diff --git a/maro/data_lib/cim/vessel_parser.py b/maro/data_lib/cim/vessel_parser.py deleted file mode 100644 index 304af2d2e..000000000 --- a/maro/data_lib/cim/vessel_parser.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT license. - -from typing import Dict, List - -from .entities import VesselSetting - - -class VesselsParser: - """Parser used to parse vessel configurations. - """ - - def parse(self, conf: dict) -> (Dict[str, int], List[VesselSetting]): - """Parse specified vessel configurations. - - Args: - conf(dict): Configurations to parse. - - Returns: - (Dict[str, int], List[VesselSetting]): Vessel mappings (name to index), and settings list for all vessels. - - """ - mapping: Dict[str, int] = {} - vessels: List[VesselSetting] = [] - - index = 0 - - for vessel_name, vessel_node in conf.items(): - mapping[vessel_name] = index - - sailing = vessel_node["sailing"] - parking = vessel_node["parking"] - route = vessel_node["route"] - - vessels.append(VesselSetting( - index, - vessel_name, - vessel_node["capacity"], - route["route_name"], - route["initial_port_name"], - sailing["speed"], - sailing["noise"], - parking["duration"], - parking["noise"], - # default no empty - vessel_node.get("empty", 0))) - - index += 1 - - return mapping, vessels diff --git a/maro/data_lib/cim/vessel_past_stops_wrapper.py b/maro/data_lib/cim/vessel_past_stops_wrapper.py index 216e5f90f..02bd1c7aa 100644 --- a/maro/data_lib/cim/vessel_past_stops_wrapper.py +++ b/maro/data_lib/cim/vessel_past_stops_wrapper.py @@ -2,6 +2,7 @@ # Licensed under the MIT license. from .entities import CimBaseDataCollection +from .utils import extract_key_of_three_ints class VesselPastStopsWrapper: @@ -15,17 +16,12 @@ class VesselPastStopsWrapper: stops = data_cntr.vessel_past_stops[0] """ - def __init__(self, data: CimBaseDataCollection): + def __init__(self, data: CimBaseDataCollection) -> None: self._stop_number = data.past_stop_number self._stops = data.vessel_stops def __getitem__(self, key): - assert type(key) == tuple or type(key) == list - assert len(key) == 3 - - vessel_idx = key[0] - last_loc_idx = key[1] - loc_idx = key[2] + vessel_idx, last_loc_idx, loc_idx = extract_key_of_three_ints(key) # ignore current port if parking last_stop_idx = loc_idx + (0 if last_loc_idx == loc_idx else -1) diff --git a/maro/data_lib/cim/vessel_reachable_stops_wrapper.py b/maro/data_lib/cim/vessel_reachable_stops_wrapper.py index ebc2fe097..e094e39fa 100644 --- a/maro/data_lib/cim/vessel_reachable_stops_wrapper.py +++ b/maro/data_lib/cim/vessel_reachable_stops_wrapper.py @@ -2,6 +2,7 @@ # Licensed under the MIT license. from .entities import CimBaseDataCollection +from .utils import extract_key_of_three_ints class VesselReachableStopsWrapper: @@ -15,20 +16,14 @@ class VesselReachableStopsWrapper: stop_list = data_cntr.reachable_stops[0] """ - def __init__(self, data: CimBaseDataCollection): + def __init__(self, data: CimBaseDataCollection) -> None: self._routes = data.routes self._stops = data.vessel_stops def __getitem__(self, key): - assert type(key) == tuple or type(key) == list - assert len(key) == 3 - - vessel_idx = key[0] - route_idx = key[1] - next_loc_idx = key[2] + vessel_idx, route_idx, next_loc_idx = extract_key_of_three_ints(key) route_length = len(self._routes[route_idx]) - stops = self._stops[vessel_idx][ - next_loc_idx + 1: next_loc_idx + 1 + route_length] + stops = self._stops[vessel_idx][next_loc_idx + 1: next_loc_idx + 1 + route_length] return [(stop.port_idx, stop.arrival_tick) for stop in stops] diff --git a/maro/data_lib/cim/vessel_sailing_plan_wrapper.py b/maro/data_lib/cim/vessel_sailing_plan_wrapper.py index 145b4d781..ffbd50a3e 100644 --- a/maro/data_lib/cim/vessel_sailing_plan_wrapper.py +++ b/maro/data_lib/cim/vessel_sailing_plan_wrapper.py @@ -2,6 +2,7 @@ # Licensed under the MIT license. from .entities import CimBaseDataCollection +from .utils import extract_key_of_three_ints from .vessel_future_stops_prediction import VesselFutureStopsPrediction @@ -17,19 +18,11 @@ class VesselSailingPlanWrapper(VesselFutureStopsPrediction): stops = data_cntr.vessel_planned_stops[0] """ - def __init__(self, data: CimBaseDataCollection): + def __init__(self, data: CimBaseDataCollection) -> None: super().__init__(data) def __getitem__(self, key): - assert type(key) == tuple or type(key) == list - assert len(key) == 3 - - vessel_idx = key[0] - route_idx = key[1] - next_loc_idx = key[2] - + vessel_idx, route_idx, next_loc_idx = extract_key_of_three_ints(key) route_length = len(self._routes[route_idx]) - stops = self._predict_future_stops(vessel_idx, next_loc_idx, route_length) - return [(stop.port_idx, stop.arrival_tick) for stop in stops] diff --git a/maro/data_lib/cim/vessel_stop_wrapper.py b/maro/data_lib/cim/vessel_stop_wrapper.py index fb25641a4..14bbfcb9f 100644 --- a/maro/data_lib/cim/vessel_stop_wrapper.py +++ b/maro/data_lib/cim/vessel_stop_wrapper.py @@ -21,7 +21,7 @@ class VesselStopsWrapper: stops = data_cntr.vessel_stops[:] """ - def __init__(self, data: CimBaseDataCollection): + def __init__(self, data: CimBaseDataCollection) -> None: self._stops = data.vessel_stops def __getitem__(self, key): @@ -36,7 +36,7 @@ def __getitem__(self, key): loc_idx = key[1] return self._stops[vessel_idx][loc_idx] - elif key_type == slice and key.start is None and key.step is None and key.stop is None: + elif key_type == slice and all([key.start is None, key.step is None, key.stop is None]): return self._stops return None diff --git a/maro/simulator/scenarios/abs_business_engine.py b/maro/simulator/scenarios/abs_business_engine.py index ed72365f9..382e8b506 100644 --- a/maro/simulator/scenarios/abs_business_engine.py +++ b/maro/simulator/scenarios/abs_business_engine.py @@ -36,8 +36,8 @@ class AbsBusinessEngine(ABC): """ def __init__( - self, scenario_name: str, event_buffer: EventBuffer, topology: str, - start_tick: int, max_tick: int, snapshot_resolution: int, max_snapshots: int, + self, scenario_name: str, event_buffer: EventBuffer, topology: Optional[str], + start_tick: int, max_tick: int, snapshot_resolution: int, max_snapshots: Optional[int], additional_options: dict = None ): self._scenario_name: str = scenario_name diff --git a/maro/simulator/scenarios/cim/business_engine.py b/maro/simulator/scenarios/cim/business_engine.py index d3e9ed364..ccee78268 100644 --- a/maro/simulator/scenarios/cim/business_engine.py +++ b/maro/simulator/scenarios/cim/business_engine.py @@ -4,6 +4,7 @@ import os from math import ceil, floor +from typing import Optional import numpy as np from yaml import safe_load @@ -37,8 +38,8 @@ class CimBusinessEngine(AbsBusinessEngine): """Cim business engine, used simulate CIM related problem.""" def __init__( - self, event_buffer: EventBuffer, topology: str, start_tick: int, max_tick: int, - snapshot_resolution: int, max_snapshots: int, additional_options: dict = None + self, event_buffer: EventBuffer, topology: Optional[str], start_tick: int, max_tick: int, + snapshot_resolution: int, max_snapshots: Optional[int], additional_options: dict = None ): super().__init__( "cim", event_buffer, topology, start_tick, max_tick, @@ -62,10 +63,10 @@ def __init__( self._vessels = [] self._ports = [] - self._frame = None - self._full_on_ports: MatrixAttributeAccessor = None - self._full_on_vessels: MatrixAttributeAccessor = None - self._vessel_plans: MatrixAttributeAccessor = None + self._frame: Optional[FrameBase] = None + self._full_on_ports: Optional[MatrixAttributeAccessor] = None + self._full_on_vessels: Optional[MatrixAttributeAccessor] = None + self._vessel_plans: Optional[MatrixAttributeAccessor] = None self._port_orders_exporter = PortOrderExporter("enable-dump-snapshot" in additional_options) self._load_cost_factor: float = self._data_cntr.load_cost_factor @@ -239,19 +240,17 @@ def early_discharge(self, vessel_idx: int) -> int: def get_metrics(self) -> DocableDict: """Get metrics information for cim scenario. - - Args: - dict: A dict that contains "perf", "total_shortage" and "total_cost", - and can use help method to show help docs. """ total_shortage = sum([p.acc_shortage for p in self._ports]) total_booking = sum([p.acc_booking for p in self._ports]) return DocableDict( metrics_desc, - order_requirements=total_booking, - container_shortage=total_shortage, - operation_number=self._total_operate_num + { + 'order_requirements': total_booking, + 'container_shortage': total_shortage, + 'operation_number': self._total_operate_num + } ) def get_node_mapping(self) -> dict: @@ -417,6 +416,7 @@ def _on_order_generated(self, event: CascadeEvent): Args: event (CascadeEvent): Order event object. """ + assert isinstance(event.payload, Order) order: Order = event.payload src_port = self._ports[order.src_port_idx] @@ -449,10 +449,8 @@ def _on_order_generated(self, event: CascadeEvent): ) # If buffer_tick is 0, we should execute it as this tick. - if buffer_ticks == 0: - event.add_immediate_event(laden_return_evt) - else: - self._event_buffer.insert_event(laden_return_evt) + event.add_immediate_event(laden_return_evt) if buffer_ticks == 0 \ + else self._event_buffer.insert_event(laden_return_evt) def _on_full_return(self, event: AtomEvent): """Handler for processing the event that full containers are returned from shipper. @@ -461,6 +459,7 @@ def _on_full_return(self, event: AtomEvent): 1. First move the container from on_shipper to full (update state: on_shipper -> full). 2. Then append the container to the port pending list. """ + assert isinstance(event.payload, LadenReturnPayload) payload: LadenReturnPayload = event.payload src_port = self._ports[payload.src_port_idx] @@ -487,7 +486,7 @@ def _on_full_load(self, event: AtomEvent): Args: event (AtomEvent): Arrival event object. """ - + assert isinstance(event.payload, VesselStatePayload) arrival_obj: VesselStatePayload = event.payload vessel_idx: int = arrival_obj.vessel_idx port_idx: int = arrival_obj.port_idx @@ -556,7 +555,7 @@ def _on_arrival(self, event: AtomEvent): Args: event (AtomEvent): Arrival event object. """ - + assert isinstance(event.payload, VesselStatePayload) arrival_payload: VesselStatePayload = event.payload vessel_idx = arrival_payload.vessel_idx vessel = self._vessels[vessel_idx] @@ -587,7 +586,7 @@ def _on_departure(self, event: AtomEvent): Args: event (AtomEvent): Departure event object. """ - + assert isinstance(event.payload, VesselStatePayload) departure_payload: VesselStatePayload = event.payload vessel_idx = departure_payload.vessel_idx vessel = self._vessels[vessel_idx] @@ -613,6 +612,7 @@ def _on_discharge(self, event: CascadeEvent): Args: event (AtomEvent): Discharge event object. """ + assert isinstance(event.payload, VesselDischargePayload) discharge_payload: VesselDischargePayload = event.payload vessel_idx = discharge_payload.vessel_idx port_idx = discharge_payload.port_idx @@ -631,10 +631,8 @@ def _on_discharge(self, event: CascadeEvent): tick=event.tick + buffer_ticks, event_type=Events.RETURN_EMPTY, payload=payload ) - if buffer_ticks == 0: - event.add_immediate_event(mt_return_evt) - else: - self._event_buffer.insert_event(mt_return_evt) + event.add_immediate_event(mt_return_evt) if buffer_ticks == 0 \ + else self._event_buffer.insert_event(mt_return_evt) def _on_empty_return(self, event: AtomEvent): """Handler for processing event when there are some empty container return to port. @@ -642,6 +640,7 @@ def _on_empty_return(self, event: AtomEvent): Args: event (AtomEvent): Empty-return event object. """ + assert isinstance(event.payload, EmptyReturnPayload) payload: EmptyReturnPayload = event.payload port = self._ports[payload.port_idx] @@ -655,11 +654,9 @@ def _on_action_received(self, event: CascadeEvent): event (CascadeEvent): Action event object with expected payload: {vessel_id: empty_number_to_move}}. """ actions = event.payload + assert isinstance(actions, list) if actions: - if type(actions) is not list: - actions = [actions] - for action in actions: vessel_idx = action.vessel_idx port_idx = action.port_idx @@ -669,14 +666,8 @@ def _on_action_received(self, event: CascadeEvent): port_empty = port.empty vessel_empty = vessel.empty - action_type: ActionType = getattr(action, "action_type", None) - - # Make it compatible with previous action. - if action_type is None: - action_type = ActionType.DISCHARGE if move_num > 0 else ActionType.LOAD - - # Make sure the move number is positive, as we have the action type. - move_num = abs(move_num) + assert isinstance(action, Action) + action_type = action.action_type if action_type == ActionType.DISCHARGE: assert(move_num <= vessel_empty) diff --git a/maro/simulator/scenarios/cim/common.py b/maro/simulator/scenarios/cim/common.py index 5901aed76..9add975bc 100644 --- a/maro/simulator/scenarios/cim/common.py +++ b/maro/simulator/scenarios/cim/common.py @@ -32,10 +32,13 @@ class Action: summary_key = ["port_idx", "vessel_idx", "action_type", "quantity"] def __init__(self, vessel_idx: int, port_idx: int, quantity: int, action_type: ActionType): - self.vessel_idx = vessel_idx - self.port_idx = port_idx - self.quantity = quantity - self.action_type = action_type + assert action_type is not None + assert quantity >= 0 + + self.vessel_idx: int = vessel_idx + self.port_idx: int = port_idx + self.quantity: int = quantity + self.action_type: ActionType = action_type def __repr__(self): return "%s {action_type: %r, port_idx: %r, vessel_idx: %r, quantity: %r}" % \ @@ -66,7 +69,7 @@ class DecisionEvent: tick (int): On which tick we need an action. port_idx (int): Which port will take action. vessel_idx (int): Which vessel will take action. - snapshot_list (int): Snapshots of the environment to input into the decision model. + snapshot_list (SnapshotList): Snapshots of the environment to input into the decision model. action_scope_func (Function): Function to calculate action scope, we use function here to make it getting the value as late as possible. early_discharge_func (Function): Function to fetch early discharge number of specified vessel, we @@ -108,7 +111,7 @@ def early_discharge(self) -> int: return int(self._early_discharge) def __getstate__(self): - """Return pickleable dictionary. + """Return pickle-able dictionary. NOTE: this class do not support unpickle""" return { diff --git a/maro/simulator/scenarios/cim/frame_builder.py b/maro/simulator/scenarios/cim/frame_builder.py index 3a7a05903..77d10a931 100644 --- a/maro/simulator/scenarios/cim/frame_builder.py +++ b/maro/simulator/scenarios/cim/frame_builder.py @@ -15,6 +15,7 @@ def gen_cim_frame(port_num: int, vessel_num: int, stop_nums: tuple, snapshots_nu port_num (int): Number of ports. vessel_num (int): Number of vessels. stop_nums (tuple): Past stops number and future stop number. + snapshots_num (int): Number of snapshots. """ vessel_cls = gen_vessel_definition(stop_nums) matrix_cls = gen_matrix(port_num, vessel_num) diff --git a/maro/simulator/scenarios/cim/matrix.py b/maro/simulator/scenarios/cim/matrix.py index 69844ee9b..1c90e89d9 100644 --- a/maro/simulator/scenarios/cim/matrix.py +++ b/maro/simulator/scenarios/cim/matrix.py @@ -15,6 +15,7 @@ def gen_matrix(port_num: int, vessel_num: int): Return: type: Matrix class definition. """ + @node("matrices") class GeneralInfoMatrix(NodeBase): """Used to save matrix, and provide matrix accessor.""" @@ -28,15 +29,12 @@ class GeneralInfoMatrix(NodeBase): def __init__(self): # we cannot create matrix accessor here, since the attributes will be bind after frame setup, - self._acc_dict = {} - self._acc_dict["full_on_ports"] = MatrixAttributeAccessor(self, "full_on_ports", port_num, port_num) - self._acc_dict["full_on_vessels"] = MatrixAttributeAccessor(self, "full_on_vessels", vessel_num, port_num) - self._acc_dict["vessel_plans"] = MatrixAttributeAccessor(self, "vessel_plans", vessel_num, port_num) + self._acc_dict = { + "full_on_ports": MatrixAttributeAccessor(self, "full_on_ports", port_num, port_num), + "full_on_vessels": MatrixAttributeAccessor(self, "full_on_vessels", vessel_num, port_num), + "vessel_plans": MatrixAttributeAccessor(self, "vessel_plans", vessel_num, port_num)} def __getitem__(self, key): - if key in self._acc_dict: - return self._acc_dict[key] - - return None + return self._acc_dict.get(key, None) return GeneralInfoMatrix diff --git a/maro/simulator/scenarios/citi_bike/business_engine.py b/maro/simulator/scenarios/citi_bike/business_engine.py index cfd8dbf2d..cff8170e1 100644 --- a/maro/simulator/scenarios/citi_bike/business_engine.py +++ b/maro/simulator/scenarios/citi_bike/business_engine.py @@ -3,7 +3,7 @@ import datetime import os -from typing import List +from typing import List, Optional import holidays import numpy as np @@ -46,8 +46,8 @@ class CitibikeBusinessEngine(AbsBusinessEngine): def __init__( - self, event_buffer: EventBuffer, topology: str, start_tick: int, - max_tick: int, snapshot_resolution: int, max_snapshots: int, additional_options: dict = {} + self, event_buffer: EventBuffer, topology: Optional[str], start_tick: int, + max_tick: int, snapshot_resolution: int, max_snapshots: Optional[int], additional_options: dict = {} ): super().__init__( "citi_bike", event_buffer, topology, start_tick, max_tick, @@ -178,7 +178,7 @@ def get_agent_idx_list(self) -> List[int]: """ return [station.index for station in self._stations] - def get_metrics(self) -> dict: + def get_metrics(self) -> DocableDict: """Get current metrics information. Note: @@ -189,9 +189,11 @@ def get_metrics(self) -> dict: """ return DocableDict( metrics_desc, - trip_requirements=self._total_trips, - bike_shortage=self._total_shortages, - operation_number=self._total_operate_num + { + 'trip_requirements': self._total_trips, + 'bike_shortage': self._total_shortages, + 'operation_number': self._total_operate_num + } ) def __del__(self): diff --git a/maro/simulator/scenarios/helpers.py b/maro/simulator/scenarios/helpers.py index b1b15c12d..2adad47f3 100644 --- a/maro/simulator/scenarios/helpers.py +++ b/maro/simulator/scenarios/helpers.py @@ -30,11 +30,11 @@ class DocableDict: Args: doc (str): Customized doc of the dict. - kwargs (dict): Dictionary items to store. + origin_dict (dict): Dictionary items to store. """ - def __init__(self, doc: str, **kwargs): - self._original_dict = kwargs + def __init__(self, doc: str, origin_dict: dict): + self._original_dict = origin_dict DocableDict.__doc__ = doc def __getattr__(self, name): diff --git a/maro/simulator/scenarios/vm_scheduling/business_engine.py b/maro/simulator/scenarios/vm_scheduling/business_engine.py index 807ea8985..d13a49f6a 100644 --- a/maro/simulator/scenarios/vm_scheduling/business_engine.py +++ b/maro/simulator/scenarios/vm_scheduling/business_engine.py @@ -4,7 +4,7 @@ import os import shutil import tarfile -from typing import Dict, List +from typing import Dict, List, Optional from yaml import safe_load @@ -51,11 +51,11 @@ class VmSchedulingBusinessEngine(AbsBusinessEngine): def __init__( self, event_buffer: EventBuffer, - topology: str, + topology: Optional[str], start_tick: int, max_tick: int, snapshot_resolution: int, - max_snapshots: int, + max_snapshots: Optional[int], additional_options: dict = {} ): super().__init__( @@ -565,19 +565,21 @@ def get_metrics(self) -> DocableDict: return DocableDict( metrics_desc, - total_vm_requests=self._total_vm_requests, - total_incomes=self._total_incomes, - energy_consumption_cost=self._energy_consumption_cost, - total_profit=self._total_profit, - total_energy_consumption=self._total_energy_consumption, - successful_allocation=self._successful_allocation, - successful_completion=self._successful_completion, - failed_allocation=self._failed_allocation, - failed_completion=self._failed_completion, - total_latency=self._total_latency, - total_oversubscriptions=self._total_oversubscriptions, - total_overload_pms=self._total_overload_pms, - total_overload_vms=self._total_overload_vms + { + 'total_vm_requests': self._total_vm_requests, + 'total_incomes': self._total_incomes, + 'energy_consumption_cost': self._energy_consumption_cost, + 'total_profit': self._total_profit, + 'total_energy_consumption': self._total_energy_consumption, + 'successful_allocation': self._successful_allocation, + 'successful_completion': self._successful_completion, + 'failed_allocation': self._failed_allocation, + 'failed_completion': self._failed_completion, + 'total_latency': self._total_latency, + 'total_oversubscriptions': self._total_oversubscriptions, + 'total_overload_pms': self._total_overload_pms, + 'total_overload_vms': self._total_overload_vms + } ) def _register_events(self): diff --git a/maro/simulator/utils/sim_random.py b/maro/simulator/utils/sim_random.py index 40e6b0206..59eb70d27 100644 --- a/maro/simulator/utils/sim_random.py +++ b/maro/simulator/utils/sim_random.py @@ -53,7 +53,7 @@ def seed(self, seed_num: int): self._seed_dict[key] = seed - def _create_instance(self, key: str) -> None: + def create_instance(self, key: str) -> None: assert type(key) is str if key not in self._rand_instances: @@ -66,7 +66,7 @@ def __getitem__(self, key): assert type(key) is str if key not in self._rand_instances: - self._create_instance(key) + self.create_instance(key) return self._rand_instances[key] @@ -82,7 +82,7 @@ def reset_seed(self, key: str) -> None: assert type(key) is str if key not in self._seed_dict: - self._create_instance(key) + self.create_instance(key) rand = self._rand_instances[key] rand.seed(self._seed_dict[key]) diff --git a/notebooks/container_inventory_management/interact_with_environment.ipynb b/notebooks/container_inventory_management/interact_with_environment.ipynb index ae3ea336d..c9c354aa7 100644 --- a/notebooks/container_inventory_management/interact_with_environment.ipynb +++ b/notebooks/container_inventory_management/interact_with_environment.ipynb @@ -311,7 +311,7 @@ " - **vessel_idx**: (int) The id of the vessel/operation object of the port/agent;\n", " - **port_idx**: (int) The id of the port/agent that take this action;\n", " - **action_type**: (ActionType) Whether to load or discharge empty containers in this action;\n", - " - **quantity**: (int) The quantity of empty containers to be loaded/discharged.\n", + " - **quantity**: (int) The (non-negative) quantity of empty containers to be loaded/discharged.\n", "\n", "## Generate random actions based on the DecisionEvent\n", "\n", diff --git a/notebooks/container_inventory_management/rl_formulation.ipynb b/notebooks/container_inventory_management/rl_formulation.ipynb index 18b84392e..c4360d11a 100644 --- a/notebooks/container_inventory_management/rl_formulation.ipynb +++ b/notebooks/container_inventory_management/rl_formulation.ipynb @@ -101,7 +101,7 @@ " plan_action = percent * (scope.discharge + early_discharge) - early_discharge\n", " actual_action = round(plan_action) if plan_action > 0 else round(percent * scope.discharge)\n", " else:\n", - " actual_action, action_type = 0, None\n", + " actual_action, action_type = 0, ActionType.LOAD\n", "\n", " return {port: Action(vessel, port, actual_action, action_type)}\n", "\n", diff --git a/tests/cim/data_generator/test_data_collection_dump.py b/tests/cim/data_generator/test_data_collection_dump.py index 6e88ce552..b5d1f0dd8 100644 --- a/tests/cim/data_generator/test_data_collection_dump.py +++ b/tests/cim/data_generator/test_data_collection_dump.py @@ -5,12 +5,11 @@ import tempfile import unittest -import numpy as np - from maro.data_lib.cim.cim_data_dump import dump_from_config MAX_TICK = 20 + class TestDataCollectionDump(unittest.TestCase): def test_dump(self): output_folder = tempfile.mkdtemp() @@ -19,7 +18,9 @@ def test_dump(self): dump_from_config(config_path, output_folder, 20) # check output folder - for fname in ["ports.csv", "vessels.csv", "routes.csv", "global_order_proportion.txt", "order_proportion.csv", "misc.yml"]: + for fname in [ + "ports.csv", "vessels.csv", "routes.csv", "global_order_proportion.txt", "order_proportion.csv", "misc.yml" + ]: self.assertTrue(os.path.exists(os.path.join(output_folder, fname)), fname) # TODO: check content? @@ -37,5 +38,6 @@ def test_dump_with_invalid_parameters(self): with self.assertRaises(AssertionError): dump_from_config(config_path, None, 20) -if __name__=="__main__": + +if __name__ == "__main__": unittest.main() diff --git a/tests/cim/data_generator/test_data_collection_load.py b/tests/cim/data_generator/test_data_collection_load.py index 4f838698c..b9d4366b7 100644 --- a/tests/cim/data_generator/test_data_collection_load.py +++ b/tests/cim/data_generator/test_data_collection_load.py @@ -4,25 +4,28 @@ import os import tempfile import unittest -from typing import Dict, List +from typing import List from maro.data_lib import BinaryConverter -from maro.data_lib.cim import dump_from_config, load_from_folder +from maro.data_lib.cim import load_from_folder from maro.data_lib.cim.cim_data_dump import CimDataDumpUtil -from maro.data_lib.cim.cim_data_generator import CimDataGenerator -from maro.data_lib.cim.entities import CimSyntheticDataCollection, NoisedItem, SyntheticPortSetting, RoutePoint, Stop, VesselSetting +from maro.data_lib.cim.cim_data_generator import gen_cim_data +from maro.data_lib.cim.entities import ( + CimSyntheticDataCollection, NoisedItem, RoutePoint, Stop, SyntheticPortSetting, VesselSetting +) MAX_TICK = 20 + class TestDumpsLoad(unittest.TestCase): def test_load_correct(self): config_path = os.path.join("tests", "data", "cim", "data_generator", "dumps", "config.yml") - stops_meta_path = os.path.join("tests", "data", "cim", "data_generator" ,"dumps", "cim.stops.meta.yml") + stops_meta_path = os.path.join("tests", "data", "cim", "data_generator", "dumps", "cim.stops.meta.yml") output_folder = tempfile.mkdtemp() # here we need to use CimDataDumpUtil manually to compare the result - dc: CimSyntheticDataCollection = CimDataGenerator().gen_data(config_path, 20) + dc: CimSyntheticDataCollection = gen_cim_data(config_path, 20) dumper = CimDataDumpUtil(dc) @@ -80,8 +83,10 @@ def _compare_ports(self, dc1: CimSyntheticDataCollection, dc2: CimSyntheticDataC self.assertTrue(len(ports_1) == len(ports_2)) for port_index in range(len(ports_1)): - port1: SyntheticPortSetting = ports_1[port_index] - port2: SyntheticPortSetting = ports_2[port_index] + port1 = ports_1[port_index] + port2 = ports_2[port_index] + assert isinstance(port1, SyntheticPortSetting) + assert isinstance(port2, SyntheticPortSetting) self.assertTrue(port1.index == port2.index, f"{port1.index}, {port2.index}") self.assertTrue(port1.name == port2.name, f"{port1.name}, {port2.name}") @@ -102,7 +107,6 @@ def _compare_ports(self, dc1: CimSyntheticDataCollection, dc2: CimSyntheticDataC self.assertTrue(tprop1.base == tprop2.base) self.assertTrue(tprop1.noise == tprop2.noise) - def _compare_vessels(self, dc1: CimSyntheticDataCollection, dc2: CimSyntheticDataCollection): vessels_1: List[VesselSetting] = dc1.vessel_settings vessels_2: List[VesselSetting] = dc2.vessel_settings @@ -123,7 +127,6 @@ def _compare_vessels(self, dc1: CimSyntheticDataCollection, dc2: CimSyntheticDat self.assertTrue(vessel1.parking_duration == vessel2.parking_duration) self.assertTrue(vessel1.parking_noise == vessel2.parking_noise) - def _compare_stops(self, dc1: CimSyntheticDataCollection, dc2: CimSyntheticDataCollection): stops_1: List[List[Stop]] = dc1.vessel_stops stops_2: List[List[Stop]] = dc2.vessel_stops @@ -147,5 +150,6 @@ def _compare_stops(self, dc1: CimSyntheticDataCollection, dc2: CimSyntheticDataC self.assertTrue(stop1.port_idx == stop2.port_idx) self.assertTrue(stop1.vessel_idx == stop2.vessel_idx) -if __name__=="__main__": + +if __name__ == "__main__": unittest.main() diff --git a/tests/cim/data_generator/test_data_generator.py b/tests/cim/data_generator/test_data_generator.py index 4ff36ac0e..bf58ad890 100644 --- a/tests/cim/data_generator/test_data_generator.py +++ b/tests/cim/data_generator/test_data_generator.py @@ -4,20 +4,17 @@ import os import unittest -import yaml - -from maro.data_lib.cim.cim_data_generator import CimDataGenerator +from maro.data_lib.cim.cim_data_generator import gen_cim_data from maro.data_lib.cim.entities import CimSyntheticDataCollection MAX_TICK = 20 + class TestDataGenerator(unittest.TestCase): def test_data_generator_without_noise(self): config_path = os.path.join("tests", "data", "cim", "data_generator", "dumps", "config.yml") - ge = CimDataGenerator() - - dc: CimSyntheticDataCollection = ge.gen_data(config_path, max_tick=MAX_TICK) + dc: CimSyntheticDataCollection = gen_cim_data(config_path, max_tick=MAX_TICK) self.assertEqual(MAX_TICK, len(dc.order_proportion)) self.assertEqual(100000, dc.total_containers) @@ -42,10 +39,11 @@ def test_data_generator_without_noise(self): self.assertListEqual([0] * 5, [v.empty for v in dc.vessel_settings]) # port - self.assertListEqual([100000,100000, 1000000, 100000], [p.capacity for p in dc.port_settings]) + self.assertListEqual([100000, 100000, 1000000, 100000], [p.capacity for p in dc.port_settings]) self.assertListEqual([0.25 * dc.total_containers] * 4, [p.empty for p in dc.port_settings]) # TODO: more -if __name__=="__main__": + +if __name__ == "__main__": unittest.main() diff --git a/tests/cim/data_generator/test_order_global_proportion.py b/tests/cim/data_generator/test_order_global_proportion.py index 798e00fa6..e0837a327 100644 --- a/tests/cim/data_generator/test_order_global_proportion.py +++ b/tests/cim/data_generator/test_order_global_proportion.py @@ -4,78 +4,73 @@ import unittest from math import floor -from maro.data_lib.cim.global_order_proportion import GlobalOrderProportion +from maro.data_lib.cim.parsers import parse_global_order_proportion class TestOrderGlobalProportion(unittest.TestCase): def test_orders_without_noise(self): - total_cntrs = 100 + total_cnt = 100 max_tick = 50 period = 10 ratio = 0.02 - conf ={ + conf = { "period": period, "sample_nodes": [ (0, ratio), - (period-1, ratio) + (period - 1, ratio) ], "sample_noise": 0 } - op = GlobalOrderProportion() - - prop = op.parse(conf, total_container=total_cntrs, max_tick=max_tick) + prop = parse_global_order_proportion(conf, total_container=total_cnt, max_tick=max_tick) # check the order number - self.assertEqual(floor(total_cntrs * ratio) * max_tick, prop.sum()) + self.assertEqual(floor(total_cnt * ratio) * max_tick, prop.sum()) # check proportion, it should be a line - self.assertListEqual([floor(total_cntrs * ratio)] * max_tick, list(prop)) + self.assertListEqual([floor(total_cnt * ratio)] * max_tick, list(prop)) def test_orders_with_noise(self): - total_cntrs = 100 + total_cnt = 100 max_tick = 50 period = 10 ratio = 0.02 - conf ={ + conf = { "period": period, "sample_nodes": [ (0, ratio), - (period-1, ratio) + (period - 1, ratio) ], "sample_noise": 0.1 } - op = GlobalOrderProportion() - - prop = op.parse(conf, total_container=total_cntrs, max_tick=max_tick) + prop = parse_global_order_proportion(conf, total_container=total_cnt, max_tick=max_tick) self.assertTrue(prop.sum() > 0) def test_orders_with_start_tick(self): - total_cntrs = 100 + total_cnt = 100 start_tick = 10 max_tick = 50 period = 10 ratio = 0.02 - conf ={ + conf = { "period": period, "sample_nodes": [ (0, ratio), - (period-1, ratio) + (period - 1, ratio) ], "sample_noise": 0 } - op = GlobalOrderProportion() + prop = parse_global_order_proportion(conf, total_container=total_cnt, start_tick=start_tick, max_tick=max_tick) - prop = op.parse(conf, total_container=total_cntrs, start_tick=start_tick, max_tick=max_tick) + self.assertEqual(floor(total_cnt * ratio) * (max_tick - start_tick), prop.sum()) - self.assertEqual(floor(total_cntrs * ratio) * (max_tick - start_tick), prop.sum()) if __name__ == "__main__": unittest.main() diff --git a/tests/cim/data_generator/test_ports_parser.py b/tests/cim/data_generator/test_ports_parser.py index 04c75e33d..7d9c7d20e 100644 --- a/tests/cim/data_generator/test_ports_parser.py +++ b/tests/cim/data_generator/test_ports_parser.py @@ -5,7 +5,7 @@ from yaml import safe_load -from maro.data_lib.cim.port_parser import PortsParser +from maro.data_lib.cim.parsers import parse_ports default_conf = """ ports: @@ -41,6 +41,7 @@ proportion: 0.67 """ + class TestPortParser(unittest.TestCase): def test_port_parser(self): @@ -48,14 +49,12 @@ def test_port_parser(self): conf = safe_load(default_conf) - ppr = PortsParser() - - port_mapping, ports = ppr.parse(conf["ports"], total_cntr) + port_mapping, ports = parse_ports(conf["ports"], total_cntr) # port number should be same with config self.assertEqual(2, len(ports)) - # all empty sould be used + # all empty should be used self.assertEqual(total_cntr, sum([p.empty for p in ports])) # capacity should same with config @@ -71,5 +70,6 @@ def test_port_parser(self): # self.assertEqual(len(port_mapping), len(ports)) + if __name__ == "__main__": unittest.main() diff --git a/tests/cim/data_generator/test_route_parser.py b/tests/cim/data_generator/test_route_parser.py index 21c09e923..259282cd1 100644 --- a/tests/cim/data_generator/test_route_parser.py +++ b/tests/cim/data_generator/test_route_parser.py @@ -5,8 +5,7 @@ import yaml -from maro.data_lib.cim.entities import RoutePoint -from maro.data_lib.cim.route_parser import RoutesParser +from maro.data_lib.cim.parsers import parse_routes conf_str = """ routes: @@ -24,13 +23,12 @@ port_name: demand_port_002 """ + class TestRoutePoint(unittest.TestCase): def test_route_parser(self): conf = yaml.safe_load(conf_str) - parser = RoutesParser() - - route_mapping, routes = parser.parse(conf["routes"]) + route_mapping, routes = parse_routes(conf["routes"]) # there should be 2 routes self.assertEqual(2, len(route_mapping)) @@ -44,5 +42,5 @@ def test_route_parser(self): self.assertListEqual([60, 70, 80], [r.distance_to_next_port for r in routes[1]]) -if __name__=="__main__": +if __name__ == "__main__": unittest.main() diff --git a/tests/cim/data_generator/test_vessel_parser.py b/tests/cim/data_generator/test_vessel_parser.py index 0dc43f8b2..2e8d2eb29 100644 --- a/tests/cim/data_generator/test_vessel_parser.py +++ b/tests/cim/data_generator/test_vessel_parser.py @@ -5,8 +5,7 @@ import yaml -from maro.data_lib.cim.entities import VesselSetting -from maro.data_lib.cim.vessel_parser import VesselsParser +from maro.data_lib.cim.parsers import parse_vessels conf_str = """ vessels: @@ -34,13 +33,12 @@ speed: 10 """ + class TestVesselParser(unittest.TestCase): def test_vessel_parse(self): conf = yaml.safe_load(conf_str) - parser = VesselsParser() - - vessel_mapping, vessels = parser.parse(conf["vessels"]) + vessel_mapping, vessels = parse_vessels(conf["vessels"]) self.assertEqual(2, len(vessel_mapping)) self.assertEqual(2, len(vessels)) @@ -56,5 +54,6 @@ def test_vessel_parse(self): self.assertListEqual([10, 10], [v.sailing_speed for v in vessels]) self.assertListEqual([0, 0], [v.sailing_noise for v in vessels]) -if __name__=="__main__": + +if __name__ == "__main__": unittest.main() diff --git a/tests/cim/mock_data_container.py b/tests/cim/mock_data_container.py deleted file mode 100644 index 144a5aa51..000000000 --- a/tests/cim/mock_data_container.py +++ /dev/null @@ -1,276 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT license. - -import csv -import os -from collections import defaultdict - -from maro.data_lib.cim.entities import Order, SyntheticPortSetting, Stop, VesselSetting - -PORT_NUM = 3 -VESSEL_NUM = 2 -PAST_STOP_NUM = 2 -FUTURE_STOP_NUM = 3 -TOTAL_CONTAINER_NUM = 100 -CONTAINER_VOLUME = 1 - - -class MockVesselStopsWrapper: - def __init__(self, stops): - self._stops = stops - - def __getitem__(self, key): - key_type = type(key) - - if key_type == int: - # get stops for vessel - vessel_idx = key - return self._stops[vessel_idx] - elif key_type == tuple: - vessel_idx = key[0] - loc_idx = key[1] - - return self._stops[vessel_idx][loc_idx] - - else: - ret = [] - - for i in range(VESSEL_NUM): - ret.append(self._stops[i]) - - return ret - -class MockEmptyReturnBufferWrapper: - def __getitem__(self, key): - return 1 - - -class MockFullReturnBufferWrapper: - def __getitem__(self, key): - return 0 - - -class MockVesselSailingPlanWrapper: - def __getitem__(self, key): - return [ - (0, 11), - (2, 13) - ] - - -class MockVeselPastStopWapper: - def __getitem__(self, key): - return [None, Stop(-1, 0, 2, 0, 0)] - - -class MockVesselFutureStopWrapper: - def __getitem__(self, key): - return [Stop(-1, 4, 6, 2, 0), Stop(-1, 10, 12, 3, 0), Stop(-1, 20, 22, 4, 0)] - - -class MockReachableStopsWrapper: - def __init__(self, stops, route_length): - self._stops = stops - self._route_length = route_length - - def __getitem__(self, key): - vessel_idx = key[0] - route_idx = key[1] - next_loc_idx = key[2] - - return [(stop.port_idx, stop.arrival_tick) for stop in - self._stops[vessel_idx][next_loc_idx + 1:next_loc_idx + 1 + self._route_length[route_idx]]] - -class MockDataContainer: - def __init__(self, case_folder: str): - self.route_dict = defaultdict(list) - self.order_dict = {} - self.ports_dict = {} - self.vessels_dict = defaultdict(float) - self._ports = [] - self._vessels = [] - - route_csv = os.path.join(case_folder, "route.csv") - - self._read_route(route_csv) - - order_csv = os.path.join(case_folder, "order.csv") - - self._read_order(order_csv) - - ports_csv = os.path.join(case_folder, "ports.csv") - - self._read_ports(ports_csv) - - vessel_csv = os.path.join(case_folder, "vessels.csv") - - self._read_vessels(vessel_csv) - - self.route_length = defaultdict(lambda: 3) - - self.port_name_to_id = {i: i for i in range(PORT_NUM)} - self.vessel_name_to_id = {i: i for i in range(VESSEL_NUM)} - - - - self._vessel_stops_wrapper = MockVesselStopsWrapper(self.route_dict) - self._reachable_stops_wrapper = MockReachableStopsWrapper(self.route_dict, self.route_length) - - @property - def past_stop_number(self) -> int: - return PAST_STOP_NUM - - @property - def future_stop_number(self) -> int: - return FUTURE_STOP_NUM - - @property - def ports(self): - return self._ports - - @property - def port_number(self) -> int: - return PORT_NUM - - @property - def vessels(self): - return self._vessels - - @property - def vessel_number(self) -> int: - return VESSEL_NUM - - @property - def container_volume(self): - return CONTAINER_VOLUME - - @property - def vessel_stops(self) : - return self._vessel_stops_wrapper - - @property - def empty_return_buffers(self): - return MockEmptyReturnBufferWrapper() - - @property - def full_return_buffers(self): - return MockFullReturnBufferWrapper() - - @property - def vessel_past_stops(self): - return MockVeselPastStopWapper() - - @property - def vessel_future_stops(self): - return MockVesselFutureStopWrapper() - - @property - def vessel_planned_stops(self): - return MockVesselSailingPlanWrapper() - - @property - def reachable_stops(self): - return self._reachable_stops_wrapper - - @property - def vessel_peroid(self): - pass - - @property - def route_mapping(self): - return {"r1": 0} - - @property - def vessel_mapping(self): - return {v.name: v.index for v in self._vessels} - - @property - def port_mapping(self): - return {p.name: p.index for p in self._ports} - - def get_orders(self, tick: int, total_empty_container: int): - result = [] - - if tick in self.order_dict: - - for src, _ in self.order_dict[tick].items(): - for dest, qty in self.order_dict[tick][src].items(): - result.append(Order(tick, src, dest, qty)) - - return result - - def _read_ports(self, path: str): - if os.path.exists(path): - with open(path, "r") as fp: - reader = csv.reader(fp) - - next(reader) - - for l in reader: - port_id = int(l[0]) - cap = float(l[1]) - cntr = int(l[2]) - - if port_id not in self.ports_dict: - self.ports_dict[port_id] = {} - - port = SyntheticPortSetting(port_id, f"p{port_id}", cap, cntr, None, None, None, None) - - self._ports.append(port) - - def _read_vessels(self, path: str): - if os.path.exists(path): - with open(path, "r") as fp: - reader = csv.reader(fp) - next(reader) - - for l in reader: - vessel_id = int(l[0]) - cap = float(l[1]) - cntr = int(l[2]) - - if vessel_id not in self.vessels_dict: - self.vessels_dict[vessel_id] = {} - - self.vessels_dict[vessel_id]['cap'] = cap - self.vessels_dict[vessel_id]['cntr'] = cntr - - vessel = VesselSetting(vessel_id, f"v{vessel_id}", cap, "r1", None, None, None, None, None, cntr) - - self._vessels.append(vessel) - - def _read_route(self, path: str): - with open(path, "r") as fp: - reader = csv.reader(fp) - - next(reader) # skip header - - for l in reader: - self.route_dict[int(l[0])].append( - Stop(-1, int(l[1]), int(l[2]), int(l[3]), int(l[0]))) - - def _read_order(self, path: str): - with open(path, "r") as fp: - reader = csv.reader(fp) - - next(reader) # skip header - - for l in reader: - if l == "": - continue - - tick = int(l[0]) - src = int(l[1]) - dest = int(l[2]) - qty = int(l[3]) - - if tick not in self.order_dict: - self.order_dict[tick] = {} - - if src not in self.order_dict[tick]: - self.order_dict[tick][src] = {} - - if dest not in self.order_dict[tick][src]: - self.order_dict[tick][src][dest] = {} - - self.order_dict[tick][src][dest] = qty diff --git a/tests/cim/test_cim_scenario.py b/tests/cim/test_cim_scenario.py index f696355b2..d981fb381 100644 --- a/tests/cim/test_cim_scenario.py +++ b/tests/cim/test_cim_scenario.py @@ -1,500 +1,383 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT license. - - import csv -import unittest import os +import pickle import tempfile - +import unittest from collections import namedtuple -from maro.event_buffer import EventBuffer, EventState -from maro.simulator.scenarios.cim.business_engine import CimBusinessEngine, Events -from maro.simulator.scenarios.cim.ports_order_export import PortOrderExporter -from tests.utils import next_step -from .mock_data_container import MockDataContainer - -from tests.utils import next_step, backends_to_test - -MAX_TICK = 20 - - -def setup_case(case_name: str): - eb = EventBuffer() - case_folder = os.path.join("tests", "data", "cim", case_name) - - CimBusinessEngine.__init__ = mock_cim_init_func - - be = CimBusinessEngine(eb, case_folder, MAX_TICK) - - return eb, be - - -def mock_cim_init_func(self, event_buffer, topology_path, max_tick): - - self._start_tick = 0 - self._max_tick = max_tick - self._topology_path = topology_path - self._event_buffer = event_buffer - self._max_snapshots = None - self._snapshot_resolution = 1 - - self._data_cntr = MockDataContainer(topology_path) - - self._vessels = [] - self._ports = [] - self._frame = None - self._port_orders_exporter = PortOrderExporter(False) - self._init_frame() +from typing import List, Optional - self._snapshots = self._frame.snapshots +import yaml - self._register_events() +os.environ["MARO_STREAMIT_ENABLED"] = "true" +os.environ["MARO_STREAMIT_EXPERIMENT_NAME"] = "cim_testing" - self._load_departure_events() +from maro.data_lib.cim import dump_from_config +from maro.data_lib.cim.entities import PortSetting, Stop, SyntheticPortSetting, VesselSetting +from maro.data_lib.cim.vessel_stop_wrapper import VesselStopsWrapper +from maro.simulator import Env +from maro.simulator.scenarios.cim.business_engine import CimBusinessEngine +from maro.simulator.scenarios.cim.common import Action, ActionType, DecisionEvent +from maro.simulator.scenarios.cim.ports_order_export import PortOrderExporter +from tests.utils import backends_to_test, compare_dictionary - self._init_vessel_plans() +TOPOLOGY_PATH_CONFIG = "tests/data/cim/case_data/config_folder" +TOPOLOGY_PATH_DUMP = "tests/data/cim/case_data/dump_folder" +TOPOLOGY_PATH_REAL_BIN = "tests/data/cim/case_data/real_folder_bin" +TOPOLOGY_PATH_REAL_CSV = "tests/data/cim/case_data/real_folder_csv" class TestCimScenarios(unittest.TestCase): - def setUp(self): - pass - - def test_init_state(self): + def __init__(self, *args, **kwargs): + super(TestCimScenarios, self).__init__(*args, **kwargs) + + with open(os.path.join(TOPOLOGY_PATH_CONFIG, "config.yml"), "r") as input_stream: + self._raw_topology = yaml.safe_load(input_stream) + + self._env: Optional[Env] = None + self._reload_topology: str = TOPOLOGY_PATH_CONFIG + self._business_engine: Optional[CimBusinessEngine] = None + + def _init_env(self, backend_name: str) -> None: + os.environ["DEFAULT_BACKEND_NAME"] = backend_name + self._env = Env( + scenario="cim", + topology=self._reload_topology, + start_tick=0, + durations=200, + options={"enable-dump-snapshot": tempfile.gettempdir()} + ) + self._business_engine = self._env.business_engine + + def test_load_from_config(self) -> None: for backend_name in backends_to_test: - os.environ["DEFAULT_BACKEND_NAME"] = backend_name - - eb: EventBuffer = None - be: CimBusinessEngine = None - eb, be = setup_case("case_01") - - # check frame - self.assertEqual(3, len(be.frame.ports), "static node number should be same with port number after " - "initialization") - self.assertEqual(2, len(be.frame.vessels), "dynamic node number should be same with vessel number " - "after initialization") - - # check snapshot - self.assertEqual(0, len(be.snapshots), f"snapshots should be 0 after initialization") - - def test_vessel_moving_correct(self): - for backend_name in backends_to_test: - os.environ["DEFAULT_BACKEND_NAME"] = backend_name - eb, be = setup_case("case_01") - tick = 0 - - ##################################### - # STEP : beginning - v = be._vessels[0] - - self.assertEqual( - 0, v.next_loc_idx, "next_loc_idx of vessel 0 should be 0 at beginning") - self.assertEqual( - 0, v.last_loc_idx, "last_loc_idx of vessel 0 should be 0 at beginning") - - stop = be._data_cntr.vessel_stops[0, v.next_loc_idx] - - self.assertEqual(0, stop.port_idx, - "vessel 0 should parking at port 0 at beginning") - - v = be._vessels[1] - - self.assertEqual( - 0, v.next_loc_idx, "next_loc_idx of vessel 1 should be 0 at beginning") - self.assertEqual( - 0, v.last_loc_idx, "last_loc_idx of vessel 1 should be 0 at beginning") - - stop = be._data_cntr.vessel_stops[1, v.next_loc_idx] - - self.assertEqual(1, stop.port_idx, - "vessel 1 should parking at port 1 at beginning") - - ##################################### - # STEP : tick = 2 - for i in range(3): - next_step(eb, be, tick) - - tick += 1 - - v = be._vessels[0] - - # if these 2 idx not equal, then means at sailing state - self.assertEqual(1, v.next_loc_idx, - "next_loc_idx of vessel 0 should be 1 at tick 2") - self.assertEqual(0, v.last_loc_idx, - "last_loc_idx of vessel 0 should be 0 at tick 2") - - v = be._vessels[1] - - self.assertEqual(1, v.next_loc_idx, - "next_loc_idx of vessel 1 should be 1 at tick 2") - self.assertEqual(0, v.last_loc_idx, - "last_loc_idx of vessel 1 should be 0 at tick 2") - - v = be.snapshots["matrices"][2::"vessel_plans"].flatten() - - # since we already fixed the vessel plans, we just check the value - for i in range(2): - self.assertEqual(11, v[i*3+0]) - self.assertEqual(-1, v[i*3+1]) - self.assertEqual(13, v[i*3+2]) - - ##################################### - # STEP : tick = 8 - for i in range(6): - next_step(eb, be, tick) - - tick += 1 - - v = be._vessels[0] - - # vessel 0 parking - self.assertEqual(1, v.next_loc_idx, - "next_loc_idx of vessel 0 should be 1 at tick 8") - self.assertEqual(1, v.last_loc_idx, - "last_loc_idx of vessel 0 should be 1 at tick 8") - - stop = be._data_cntr.vessel_stops[0, v.next_loc_idx] - - self.assertEqual(1, stop.port_idx, - "vessel 0 should parking at port 1 at tick 8") - - v = be._vessels[1] - - # vessel 1 sailing - self.assertEqual(1, v.next_loc_idx, - "next_loc_idx of vessel 1 should be 1 at tick 8") - self.assertEqual(0, v.last_loc_idx, - "last_loc_idx of vessel 1 should be 0 at tick 8") - - ##################################### - # STEP : tick = 10 - for i in range(2): - next_step(eb, be, tick) - - tick += 1 - - v = be._vessels[0] - - # vessel 0 parking - self.assertEqual(1, v.next_loc_idx, - "next_loc_idx of vessel 0 should be 1 at tick 10") - self.assertEqual(1, v.last_loc_idx, - "last_loc_idx of vessel 0 should be 1 at tick 10") - - v = be._vessels[1] - - # vessel 1 parking - self.assertEqual(1, v.next_loc_idx, - "next_loc_idx of vessel 1 should be 1 at tick 10") - self.assertEqual(1, v.last_loc_idx, - "last_loc_idx of vessel 1 should be 1 at tick 10") - - ##################################### - # STEP : tick = 11 - for i in range(1): - next_step(eb, be, tick) - - tick += 1 - - v = be._vessels[0] - - # vessel 0 parking - self.assertEqual(2, v.next_loc_idx, - "next_loc_idx of vessel 0 should be 2 at tick 11") - self.assertEqual(1, v.last_loc_idx, - "last_loc_idx of vessel 0 should be 1 at tick 11") - - v = be._vessels[1] - - # vessel 1 parking - self.assertEqual(1, v.next_loc_idx, - "next_loc_idx of vessel 1 should be 1 at tick 11") - self.assertEqual(1, v.last_loc_idx, - "last_loc_idx of vessel 1 should be 1 at tick 11") - - # move the env to next step, so it will take snapshot for current tick 11 - next_step(eb, be, tick) - - # we have hard coded the future stops, here we just check if the value correct at each tick - for i in range(tick - 1): - # check if the future stop at tick 8 (vessel 0 arrive at port 1) - stop_list = be.snapshots["vessels"][i:0:[ - "past_stop_list", "past_stop_tick_list"]].flatten() - - self.assertEqual(-1, stop_list[0]) - self.assertEqual(-1, stop_list[2]) - - stop_list = be.snapshots["vessels"][i:0:[ - "future_stop_list", "future_stop_tick_list"]].flatten() - - self.assertEqual(2, stop_list[0]) - self.assertEqual(3, stop_list[1]) - self.assertEqual(4, stop_list[2]) - self.assertEqual(4, stop_list[3]) - self.assertEqual(10, stop_list[4]) - self.assertEqual(20, stop_list[5]) - - # check if statistics data correct - order_states = be.snapshots["ports"][i:0:[ - "shortage", "acc_shortage", "booking", "acc_booking"]].flatten() - - # all the value should be 0 for this case - self.assertEqual( - 0, order_states[0], f"shortage of port 0 should be 0 at tick {i}") - self.assertEqual( - 0, order_states[1], f"acc_shortage of port 0 should be 0 until tick {i}") - self.assertEqual( - 0, order_states[2], f"booking of port 0 should be 0 at tick {i}") - self.assertEqual( - 0, order_states[3], f"acc_booking of port 0 should be 0 until tick {i}") - - # check fulfillment - fulfill_states = be.snapshots["ports"][i:0:[ - "fulfillment", "acc_fulfillment"]].flatten() - - self.assertEqual( - 0, fulfill_states[0], f"fulfillment of port 0 should be 0 at tick {i}") - self.assertEqual( - 0, fulfill_states[1], f"acc_fulfillment of port 0 should be 0 until tick {i}") - - v = be.snapshots["matrices"][2:: "vessel_plans"].flatten() - - # since we already fixed the vessel plans, we just check the value - for i in range(2): - self.assertEqual(11, v[i*3+0]) - self.assertEqual(-1, v[i*3+1]) - self.assertEqual(13, v[i*3+2]) - - def test_order_state(self): + self._init_env(backend_name) + + ######################################################### + if len(self._business_engine.configs) > 0: # Env will not have `configs` if loaded from dump/real. + self.assertTrue(compare_dictionary(self._business_engine.configs, self._raw_topology)) + + self.assertEqual(len(getattr(self._business_engine.frame, "ports")), 22) + self.assertEqual(self._business_engine._data_cntr.port_number, 22) + self.assertEqual(len(getattr(self._business_engine.frame, "vessels")), 46) + self.assertEqual(self._business_engine._data_cntr.vessel_number, 46) + self.assertEqual(len(self._business_engine.snapshots), 0) + + ######################################################### + # Vessel + vessels: List[VesselSetting] = self._business_engine._data_cntr.vessels + for i, vessel in enumerate(vessels): + vessel_config = self._raw_topology["vessels"][vessel.name] + self.assertEqual(vessel.index, i) + self.assertEqual(vessel.capacity, vessel_config["capacity"]) + self.assertEqual(vessel.parking_duration, vessel_config["parking"]["duration"]) + self.assertEqual(vessel.parking_noise, vessel_config["parking"]["noise"]) + self.assertEqual(vessel.start_port_name, vessel_config["route"]["initial_port_name"]) + self.assertEqual(vessel.route_name, vessel_config["route"]["route_name"]) + self.assertEqual(vessel.sailing_noise, vessel_config["sailing"]["noise"]) + self.assertEqual(vessel.sailing_speed, vessel_config["sailing"]["speed"]) + + for name, idx in self._business_engine.get_node_mapping()["vessels"].items(): + self.assertEqual(vessels[idx].name, name) + + ######################################################### + # Port + ports: List[PortSetting] = self._business_engine._data_cntr.ports + port_names = [port.name for port in ports] + for i, port in enumerate(ports): + assert isinstance(port, SyntheticPortSetting) + port_config = self._raw_topology["ports"][port.name] + self.assertEqual(port.index, i) + self.assertEqual(port.capacity, port_config["capacity"]) + self.assertEqual(port.empty_return_buffer.noise, port_config["empty_return"]["noise"]) + self.assertEqual(port.full_return_buffer.noise, port_config["full_return"]["noise"]) + self.assertEqual(port.source_proportion.noise, port_config["order_distribution"]["source"]["noise"]) + for target in port.target_proportions: + self.assertEqual( + target.noise, + port_config["order_distribution"]["targets"][port_names[target.index]]["noise"] + ) + + for name, idx in self._business_engine.get_node_mapping()["ports"].items(): + self.assertEqual(ports[idx].name, name) + + def test_load_from_real(self) -> None: + for topology in [TOPOLOGY_PATH_REAL_BIN, TOPOLOGY_PATH_REAL_CSV]: + self._reload_topology = topology + for backend_name in backends_to_test: + self._init_env(backend_name) + + for i, port in enumerate(self._business_engine._ports): + self.assertEqual(port.booking, 0) + self.assertEqual(port.shortage, 0) + + hard_coded_truth = [556, 0, 20751], [1042, 0, 17320], [0, 0, 25000], [0, 0, 25000] + + self._env.step(action=None) + for i, port in enumerate(self._business_engine._ports): + self.assertEqual(port.booking, hard_coded_truth[i][0]) + self.assertEqual(port.shortage, hard_coded_truth[i][1]) + self.assertEqual(port.empty, hard_coded_truth[i][2]) + + self._env.reset(keep_seed=True) + self._env.step(action=None) + for i, port in enumerate(self._business_engine._ports): + self.assertEqual(port.booking, hard_coded_truth[i][0]) + self.assertEqual(port.shortage, hard_coded_truth[i][1]) + self.assertEqual(port.empty, hard_coded_truth[i][2]) + + self._reload_topology = TOPOLOGY_PATH_CONFIG + + def test_dump_and_load(self) -> None: + dump_from_config(os.path.join(TOPOLOGY_PATH_CONFIG, "config.yml"), TOPOLOGY_PATH_DUMP, 200) + + self._reload_topology = TOPOLOGY_PATH_DUMP + + # The reloaded Env should have same behaviors + self.test_load_from_config() + self.test_vessel_movement() + self.test_order_state() + self.test_order_export() + self.test_early_discharge() + + self._reload_topology = TOPOLOGY_PATH_CONFIG + + def test_vessel_movement(self) -> None: for backend_name in backends_to_test: - os.environ["DEFAULT_BACKEND_NAME"] = backend_name - - eb, be = setup_case("case_02") - tick = 0 - - p = be._ports[0] - - self.assertEqual( - 0, p.booking, "port 0 have no booking at beginning") - self.assertEqual( - 0, p.shortage, "port 0 have no shortage at beginning") - self.assertEqual( - 100, p.empty, "port 0 have 100 empty containers at beginning") - - ##################################### - # STEP : tick = 0 - for i in range(1): - next_step(eb, be, tick) - tick += 1 - - # there should be 10 order generated at tick 0 - self.assertEqual( - 10, p.booking, "port 0 should have 10 bookings at tick 0") - self.assertEqual( - 0, p.shortage, "port 0 have no shortage at tick 0") - self.assertEqual( - 90, p.empty, "port 0 have 90 empty containers at tick 0") - - ##################################### - # STEP : tick = 1 - for i in range(1): - next_step(eb, be, tick) - tick += 1 - - # we have 0 booking, so no shortage - self.assertEqual( - 0, p.booking, "port 0 should have 0 bookings at tick 1") - self.assertEqual( - 0, p.shortage, "port 0 have no shortage at tick 1") - self.assertEqual( - 90, p.empty, "port 0 have 90 empty containers at tick 1") - - ##################################### - # STEP : tick = 3 - for i in range(2): - next_step(eb, be, tick) - tick += 1 - - # there is an order that take 40 containers - self.assertEqual( - 40, p.booking, "port 0 should have 40 booking at tick 3") - self.assertEqual( - 0, p.shortage, "port 0 have no shortage at tick 3") - self.assertEqual( - 50, p.empty, "port 0 have 90 empty containers at tick 3") - - ##################################### - # STEP : tick = 7 - for i in range(4): - next_step(eb, be, tick) - tick += 1 - - # there is an order that take 51 containers - self.assertEqual( - 51, p.booking, "port 0 should have 51 booking at tick 7") - self.assertEqual(1, p.shortage, "port 0 have 1 shortage at tick 7") - self.assertEqual( - 0, p.empty, "port 0 have 0 empty containers at tick 7") - - # push the simulator to next tick to update snapshot - next_step(eb, be, tick) - - # check if there is any container missing - total_cntr_number = sum([port.empty for port in be._ports]) + \ - sum([vessel.empty for vessel in be._vessels]) + \ - sum([port.full for port in be._ports]) + \ - sum([vessel.full for vessel in be._vessels]) - - # NOTE: we flatten here, as raw backend query result has 4dim shape - # check if statistics data correct - order_states = be.snapshots["ports"][7:0:[ - "shortage", "acc_shortage", "booking", "acc_booking"]].flatten() - - # all the value should be 0 for this case - self.assertEqual( - 1, order_states[0], f"shortage of port 0 should be 0 at tick {i}") - self.assertEqual( - 1, order_states[1], f"acc_shortage of port 0 should be 0 until tick {i}") - self.assertEqual( - 51, order_states[2], f"booking of port 0 should be 0 at tick {i}") - self.assertEqual( - 101, order_states[3], f"acc_booking of port 0 should be 0 until tick {i}") - - # check fulfillment - fulfill_states = be.snapshots["ports"][7:0:[ - "fulfillment", "acc_fulfillment"]].flatten() - - self.assertEqual( - 50, fulfill_states[0], f"fulfillment of port 0 should be 50 at tick {i}") - self.assertEqual( - 100, fulfill_states[1], f"acc_fulfillment of port 0 should be 100 until tick {i}") - - def test_order_load_discharge_state(self): + self._init_env(backend_name) + + hard_coded_period = [ + 67, 75, 84, 67, 53, 58, 51, 58, 61, 49, 164, 182, 146, 164, 182, 146, 90, 98, 79, 95, 104, 84, 87, 97, + 78, 154, 169, 136, 154, 169, 94, 105, 117, 94, 189, 210, 167, 189, 210, 167, 141, 158, 125, 141, 158, + 125 + ] + self.assertListEqual(self._business_engine._data_cntr.vessel_period, hard_coded_period) + + ports: List[PortSetting] = self._business_engine._data_cntr.ports + port_names: List[str] = [port.name for port in ports] + vessel_stops: VesselStopsWrapper = self._business_engine._data_cntr.vessel_stops + vessels: List[VesselSetting] = self._business_engine._data_cntr.vessels + + # Test invalid argument + self.assertIsNone(vessel_stops[None]) + + ######################################################### + for i, vessel in enumerate(vessels): + start_port_index = port_names.index(vessel.start_port_name) + self.assertEqual(vessel_stops[i, 0].port_idx, start_port_index) + + ######################################################### + for i, vessel in enumerate(vessels): + stop_port_indices = [stop.port_idx for stop in vessel_stops[i]] + + raw_route = self._raw_topology["routes"][vessel.route_name] + route_stop_names = [stop["port_name"] for stop in raw_route] + route_stop_indices = [port_names.index(name) for name in route_stop_names] + start_offset = route_stop_indices.index(port_names.index(vessel.start_port_name)) + + for j, stop_port_index in enumerate(stop_port_indices): + self.assertEqual(stop_port_index, route_stop_indices[(j + start_offset) % len(route_stop_indices)]) + + ######################################################### + # STEP: beginning + for i, vessel in enumerate(self._business_engine._vessels): + self.assertEqual(vessel.idx, i) + self.assertEqual(vessel.next_loc_idx, 0) + self.assertEqual(vessel.last_loc_idx, 0) + + ######################################################### + self._env.step(action=None) + self.assertEqual(self._env.tick, 5) # Vessel 35 will trigger the first arrival event at tick 5 + for i, vessel in enumerate(self._business_engine._vessels): + if i == 35: + self.assertEqual(vessel.next_loc_idx, 1) + self.assertEqual(vessel.last_loc_idx, 1) + else: + self.assertEqual(vessel.next_loc_idx, 1) + self.assertEqual(vessel.last_loc_idx, 0) + + ######################################################### + self._env.step(action=None) + self.assertEqual(self._env.tick, 6) # Vessel 27 will trigger the second arrival event at tick 6 + for i, vessel in enumerate(self._business_engine._vessels): + if i == 27: # Vessel 27 just arrives + self.assertEqual(vessel.next_loc_idx, 1) + self.assertEqual(vessel.last_loc_idx, 1) + elif i == 35: # Vessel 35 has already departed + self.assertEqual(vessel.next_loc_idx, 2) + self.assertEqual(vessel.last_loc_idx, 1) + else: + self.assertEqual(vessel.next_loc_idx, 1) + self.assertEqual(vessel.last_loc_idx, 0) + + ######################################################### + while self._env.tick < 100: + self._env.step(action=None) + self.assertEqual(self._env.tick, 100) + for i, vessel in enumerate(self._business_engine._vessels): + expected_next_loc_idx = expected_last_loc_idx = -1 + for j, stop in enumerate(vessel_stops[i]): + if stop.arrival_tick == self._env.tick: + expected_next_loc_idx = expected_last_loc_idx = j + break + if stop.arrival_tick > self._env.tick: + expected_next_loc_idx = j + expected_last_loc_idx = j - 1 + break + + self.assertEqual(vessel.next_loc_idx, expected_next_loc_idx) + self.assertEqual(vessel.last_loc_idx, expected_last_loc_idx) + + def test_order_state(self) -> None: for backend_name in backends_to_test: - os.environ["DEFAULT_BACKEND_NAME"] = backend_name - - eb, be = setup_case("case_03") - tick = 0 - - ##################################### - # STEP : tick = 5 - for i in range(6): - next_step(eb, be, tick) - tick += 1 - - # check if we have load all 50 full container - p = be._ports[0] - v = be._vessels[0] - - self.assertEqual(0, p.full, "port 0 should have no full at tick 5") - self.assertEqual( - 50, v.full, "all 50 full container should be loaded on vessel 0") - self.assertEqual( - 50, p.empty, "remaining empty should be 50 after order generated at tick 5") - self.assertEqual(0, p.shortage, "no shortage at tick 5 for port 0") - self.assertEqual(0, p.booking, "no booking at tick 5 for pot 0") - - ##################################### - # STEP : tick = 10 - for i in range(5): - next_step(eb, be, tick) - tick += 1 - - # at tick 10 vessel 0 arrive at port 1, it should discharge all the full containers - p1 = be._ports[1] - - self.assertEqual( - 0, v.full, "all 0 full container on vessel 0 after arrive at port 1 at tick 10") - self.assertEqual(50, p1.on_consignee, - "there should be 50 full containers pending to be empty at tick 10 after discharge") - self.assertEqual(0, p1.empty, "no empty for port 1 at tick 10") - self.assertEqual(0, p1.full, "no full for port 1 at tick 10") - - ##################################### - # STEP : tick = 12 - for i in range(2): - next_step(eb, be, tick) - tick += 1 - - # we hard coded the buffer time to 2, so - self.assertEqual(0, p1.on_consignee, - "all the full become empty at tick 12 for port 1") - self.assertEqual( - 50, p1.empty, "there will be 50 empty at tick 12 for port 1") - - def test_early_discharge(self): + self._init_env(backend_name) + + for i, port in enumerate(self._business_engine._ports): + total_containers = self._raw_topology['total_containers'] + initial_container_proportion = self._raw_topology['ports'][port.name]['initial_container_proportion'] + + self.assertEqual(port.booking, 0) + self.assertEqual(port.shortage, 0) + self.assertEqual(port.empty, int(total_containers * initial_container_proportion)) + + ######################################################### + self._env.step(action=None) + self.assertEqual(self._env.tick, 5) + + hard_coded_truth = [ # Should get same results under default random seed + [223, 0, 14726], [16, 0, 916], [18, 0, 917], [89, 0, 5516], [84, 0, 4613], [72, 0, 4603], + [26, 0, 1374], [24, 0, 1378], [48, 0, 2756], [54, 0, 2760], [26, 0, 1379], [99, 0, 5534], + [137, 0, 7340], [19, 0, 912], [13, 0, 925], [107, 0, 6429], [136, 0, 9164], [64, 0, 3680], + [24, 0, 1377], [31, 0, 1840], [109, 0, 6454], [131, 0, 7351] + ] + for i, port in enumerate(self._business_engine._ports): + self.assertEqual(port.booking, hard_coded_truth[i][0]) + self.assertEqual(port.shortage, hard_coded_truth[i][1]) + self.assertEqual(port.empty, hard_coded_truth[i][2]) + + def test_keep_seed(self) -> None: for backend_name in backends_to_test: - os.environ["DEFAULT_BACKEND_NAME"] = backend_name - - eb, be = setup_case("case_04") - tick = 0 - - p0 = be._ports[0] - p1 = be._ports[1] - p2 = be._ports[2] - v = be._vessels[0] - - ##################################### - # STEP : tick = 10 - for i in range(11): - next_step(eb, be, tick) - tick += 1 - - # at tick 10, vessel 0 arrive port 2, it already loaded 50 full, it need to load 50 at port 2, so it will early dicharge 10 empty - self.assertEqual( - 0, v.empty, "vessel 0 should early discharge all the empty at tick 10") - self.assertEqual( - 100, v.full, "vessel 0 should have 100 full on-board at tick 10") - self.assertEqual( - 10, p2.empty, "port 2 have 10 more empty due to early discharge at tick 10") - self.assertEqual(0, p2.full, "no full at port 2 at tick 10") - - ##################################### - # STEP : tick = 18 - for i in range(8): - next_step(eb, be, tick) - tick += 1 - - # at tick 18, vessel 0 arrive at port 1, it will discharge all the full - self.assertEqual( - 0, v.empty, "vessel 0 should have no empty at tick 18") - self.assertEqual( - 0, v.full, "vessel 0 should discharge all full on-board at tick 18") - self.assertEqual( - 100, p1.on_consignee, "100 full pending to become empty at port 1 at tick 18") - self.assertEqual(0, p1.empty, "no empty for port 1 at tick 18") - - ##################################### - # STEP : tick = 20 - for i in range(2): - next_step(eb, be, tick) - tick += 1 - - self.assertEqual( - 100, p1.empty, "there should be 100 empty at tick 20 at port 1") - - def test_order_export(self): + self._init_env(backend_name) + + vessel_stops_1: List[List[Stop]] = self._business_engine._data_cntr.vessel_stops + self._env.step(action=None) + port_info_1 = [(port.booking, port.shortage, port.empty) for port in self._business_engine._ports] + + self._env.reset(keep_seed=True) + vessel_stops_2: List[List[Stop]] = self._business_engine._data_cntr.vessel_stops + self._env.step(action=None) + port_info_2 = [(port.booking, port.shortage, port.empty) for port in self._business_engine._ports] + + self._env.reset(keep_seed=False) + vessel_stops_3: List[List[Stop]] = self._business_engine._data_cntr.vessel_stops + self._env.step(action=None) + port_info_3 = [(port.booking, port.shortage, port.empty) for port in self._business_engine._ports] + + # Vessel + for i in range(self._business_engine._data_cntr.vessel_number): + # 1 and 2 should be totally equal + self.assertListEqual(vessel_stops_1[i], vessel_stops_2[i]) + + # 1 and 3 should have difference + flag = True + for stop1, stop3 in zip(vessel_stops_1[i], vessel_stops_3[i]): + self.assertListEqual( + [stop1.index, stop1.port_idx, stop1.vessel_idx], + [stop3.index, stop3.port_idx, stop3.vessel_idx] + ) + if (stop1.arrival_tick, stop1.leave_tick) != (stop3.arrival_tick, stop3.leave_tick): + flag = False + self.assertFalse(flag) + + # Port + self.assertListEqual(port_info_1, port_info_2) + self.assertFalse(all(port1 == port3 for port1, port3 in zip(port_info_1, port_info_3))) + + def test_order_export(self) -> None: """order.tick, order.src_port_idx, order.dest_port_idx, order.quantity""" Order = namedtuple("Order", ["tick", "src_port_idx", "dest_port_idx", "quantity"]) - exportor = PortOrderExporter(True) + # + for enabled in [False, True]: + exporter = PortOrderExporter(enabled) - for i in range(5): - exportor.add(Order(0, 0, 1, i + 1)) + for i in range(5): + exporter.add(Order(0, 0, 1, i + 1)) - out_folder = tempfile.gettempdir() + out_folder = tempfile.gettempdir() + if os.path.exists(f"{out_folder}/orders.csv"): + os.remove(f"{out_folder}/orders.csv") - exportor.dump(out_folder) + exporter.dump(out_folder) - with open(f"{out_folder}/orders.csv") as fp: - reader = csv.DictReader(fp) + if enabled: + with open(f"{out_folder}/orders.csv") as fp: + reader = csv.DictReader(fp) + row = 0 + for line in reader: + self.assertEqual(row + 1, int(line["quantity"])) + row += 1 + else: # Should done nothing + self.assertFalse(os.path.exists(f"{out_folder}/orders.csv")) - row = 0 - for line in reader: - self.assertEqual(row+1, int(line["quantity"])) + def test_early_discharge(self) -> None: + for backend_name in backends_to_test: + self._init_env(backend_name) + + metric, decision_event, is_done = self._env.step(None) + assert isinstance(decision_event, DecisionEvent) + + self.assertEqual(decision_event.action_scope.load, 1240) + self.assertEqual(decision_event.action_scope.discharge, 0) + self.assertEqual(decision_event.early_discharge, 0) + + decision_event = pickle.loads(pickle.dumps(decision_event)) # Test serialization + + load_action = Action( + vessel_idx=decision_event.vessel_idx, + port_idx=decision_event.port_idx, + quantity=1201, + action_type=ActionType.LOAD + ) + discharge_action = Action( + vessel_idx=decision_event.vessel_idx, + port_idx=decision_event.port_idx, + quantity=1, + action_type=ActionType.DISCHARGE + ) + metric, decision_event, is_done = self._env.step([load_action, discharge_action]) + + history = [] + while not is_done: + metric, decision_event, is_done = self._env.step(None) + assert decision_event is None or isinstance(decision_event, DecisionEvent) + if decision_event is not None and decision_event.vessel_idx == 35: + v = self._business_engine._vessels[35] + history.append((v.full, v.empty, v.early_discharge)) + + hard_coded_benchmark = [ + (465, 838, 362), (756, 547, 291), (1261, 42, 505), (1303, 0, 42), (1303, 0, 0), (1303, 0, 0), + (803, 0, 0) + ] + self.assertListEqual(history, hard_coded_benchmark) + + # + payload_detail_benchmark = { + 'ORDER': ['tick', 'src_port_idx', 'dest_port_idx', 'quantity'], + 'RETURN_FULL': ['src_port_idx', 'dest_port_idx', 'quantity'], + 'VESSEL_ARRIVAL': ['port_idx', 'vessel_idx'], + 'LOAD_FULL': ['port_idx', 'vessel_idx'], + 'DISCHARGE_FULL': ['vessel_idx', 'port_idx', 'from_port_idx', 'quantity'], + 'PENDING_DECISION': [ + 'tick', 'port_idx', 'vessel_idx', 'snapshot_list', 'action_scope', 'early_discharge'], + 'LOAD_EMPTY': ['port_idx', 'vessel_idx', 'action_type', 'quantity'], + 'DISCHARGE_EMPTY': ['port_idx', 'vessel_idx', 'action_type', 'quantity'], + 'VESSEL_DEPARTURE': ['port_idx', 'vessel_idx'], 'RETURN_EMPTY': ['port_idx', 'quantity'] + } + self.assertTrue( + compare_dictionary(self._business_engine.get_event_payload_detail(), payload_detail_benchmark)) + port_number = self._business_engine._data_cntr.port_number + self.assertListEqual(self._business_engine.get_agent_idx_list(), list(range(port_number))) - row += 1 if __name__ == "__main__": unittest.main() diff --git a/tests/data/cim/case_data/config_folder/config.yml b/tests/data/cim/case_data/config_folder/config.yml new file mode 100644 index 000000000..eb300f6f7 --- /dev/null +++ b/tests/data/cim/case_data/config_folder/config.yml @@ -0,0 +1,1671 @@ +seed: 4096 +load_cost_factor: 0.05 +dsch_cost_factor: 0.05 +container_usage_proportion: + period: 112 + sample_nodes: + - - 1 + - 0.015012045981107782 + - - 2 + - 0.015044493615969741 + - - 3 + - 0.015086758870970732 + - - 4 + - 0.01512277494153067 + - - 5 + - 0.01513318262505517 + - - 6 + - 0.01509807371913584 + - - 7 + - 0.015 + - - 8 + - 0.015173074266317171 + - - 9 + - 0.015425203006617148 + - - 10 + - 0.015750572812854108 + - - 11 + - 0.016132546658976815 + - - 12 + - 0.01654380252377108 + - - 13 + - 0.016947634709925108 + - - 14 + - 0.017300377961276522 + - - 15 + - 0.017554806245962416 + - - 16 + - 0.01766425880641032 + - - 17 + - 0.01758716473640939 + - - 18 + - 0.017291581573131123 + - - 19 + - 0.016759338850452894 + - - 20 + - 0.015989387422188653 + - - 21 + - 0.015000000000000001 + - - 22 + - 0.016170454760379226 + - - 23 + - 0.017464338416109185 + - - 24 + - 0.01880721970850913 + - - 25 + - 0.020111919797577836 + - - 26 + - 0.021283910705811104 + - - 27 + - 0.02222773080624969 + - - 28 + - 0.022853981633974483 + - - 29 + - 0.023086401027200568 + - - 30 + - 0.022868475176765664 + - - 31 + - 0.022169060410756062 + - - 32 + - 0.02098653519702863 + - - 33 + - 0.01935109142050977 + - - 34 + - 0.017324895896556256 + - - 35 + - 0.015 + - - 36 + - 0.017505963234746832 + - - 37 + - 0.02005609098616606 + - - 38 + - 0.022502173332406644 + - - 39 + - 0.024693815471924507 + - - 40 + - 0.02648812707616644 + - - 41 + - 0.027759325587487844 + - - 42 + - 0.028407585306672437 + - - 43 + - 0.028366497123525156 + - - 44 + - 0.027608583358805686 + - - 45 + - 0.026148433549357086 + - - 46 + - 0.024043182092683656 + - - 47 + - 0.02139022683000181 + - - 48 + - 0.018322276390618314 + - - 49 + - 0.015000000000000003 + - - 50 + - 0.01839727693779964 + - - 51 + - 0.021682247211563782 + - - 52 + - 0.024670979964007092 + - - 53 + - 0.027194221337363163 + - - 54 + - 0.02910789226660702 + - - 55 + - 0.030302085852342477 + - - 56 + - 0.030707963267948966 + - - 57 + - 0.03030208585234248 + - - 58 + - 0.029107892266607024 + - - 59 + - 0.027194221337363163 + - - 60 + - 0.0246709799640071 + - - 61 + - 0.021682247211563786 + - - 62 + - 0.018397276937799648 + - - 63 + - 0.015000000000000001 + - - 64 + - 0.01832227639061831 + - - 65 + - 0.021390226830001805 + - - 66 + - 0.024043182092683656 + - - 67 + - 0.026148433549357086 + - - 68 + - 0.02760858335880569 + - - 69 + - 0.02836649712352516 + - - 70 + - 0.028407585306672443 + - - 71 + - 0.027759325587487844 + - - 72 + - 0.026488127076166445 + - - 73 + - 0.024693815471924514 + - - 74 + - 0.022502173332406648 + - - 75 + - 0.020056090986166064 + - - 76 + - 0.017505963234746836 + - - 77 + - 0.015000000000000001 + - - 78 + - 0.017324895896556256 + - - 79 + - 0.019351091420509767 + - - 80 + - 0.020986535197028634 + - - 81 + - 0.022169060410756065 + - - 82 + - 0.02286847517676566 + - - 83 + - 0.02308640102720056 + - - 84 + - 0.022853981633974483 + - - 85 + - 0.022227730806249697 + - - 86 + - 0.021283910705811104 + - - 87 + - 0.020111919797577836 + - - 88 + - 0.018807219708509137 + - - 89 + - 0.017464338416109188 + - - 90 + - 0.01617045476037923 + - - 91 + - 0.015 + - - 92 + - 0.01598938742218865 + - - 93 + - 0.01675933885045289 + - - 94 + - 0.017291581573131126 + - - 95 + - 0.017587164736409394 + - - 96 + - 0.01766425880641032 + - - 97 + - 0.017554806245962416 + - - 98 + - 0.01730037796127653 + - - 99 + - 0.016947634709925108 + - - 100 + - 0.01654380252377108 + - - 101 + - 0.01613254665897681 + - - 102 + - 0.01575057281285411 + - - 103 + - 0.015425203006617148 + - - 104 + - 0.015173074266317171 + - - 105 + - 0.015 + - - 106 + - 0.01509807371913584 + - - 107 + - 0.01513318262505517 + - - 108 + - 0.01512277494153067 + - - 109 + - 0.015086758870970732 + - - 110 + - 0.015044493615969741 + sample_noise: 0.002 +container_volumes: +- 1 +order_generate_mode: fixed +ports: + bremerhaven_ger: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.16 + order_distribution: + source: + noise: 0.025134751494652507 + proportion: 0.16 + targets: + leHavre_fra: + noise: 0.025432733187591285 + proportion: 0.18755329471737509 + montreal_can: + noise: 0.014981815264884504 + proportion: 0.0756906048807678 + newYork_usa: + noise: 0.07860790872534353 + proportion: 0.47933778254893905 + pusan_kor: + noise: 0.002902145091237068 + proportion: 0.07667664751459422 + qingdao_chn: + noise: 0.0016703057083900894 + proportion: 0.0512157380304634 + shanghai_chn: + noise: 0.004329384963154748 + proportion: 0.04763588234031061 + singapore_sgp: + noise: 0.00014700838968303593 + proportion: 0.005882857005910926 + yantian_chn: + noise: 0.014277274600949947 + proportion: 0.07600719296163895 + durban_sau: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.01 + order_distribution: + source: + noise: 0.0003905851406890507 + proportion: 0.01 + targets: + qingdao_chn: + noise: 0.03386602700899302 + proportion: 0.2502879616953551 + shanghai_chn: + noise: 0.04099351916502508 + proportion: 0.2501170152675811 + singapore_sgp: + noise: 0.02983331512414685 + proportion: 0.24812321657873454 + yantian_chn: + noise: 0.0398838857119423 + proportion: 0.25147180645832923 + itagual_bra: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.01 + order_distribution: + source: + noise: 0.0017049012580563228 + proportion: 0.01 + targets: + qingdao_chn: + noise: 0.006704071585248814 + proportion: 0.19992517767067194 + santos_bra: + noise: 0.01065215665926791 + proportion: 0.20145578663243896 + shanghai_chn: + noise: 0.01889939427623029 + proportion: 0.19975353803822685 + singapore_sgp: + noise: 0.030425501405892584 + proportion: 0.19775168293424744 + yantian_chn: + noise: 0.0038049637706619867 + proportion: 0.20111381472441478 + leHavre_fra: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.06 + order_distribution: + source: + noise: 0.010023093144315015 + proportion: 0.06 + targets: + bremerhaven_ger: + noise: 0.046713884416130814 + proportion: 0.2613085269688243 + montreal_can: + noise: 0.007011738109808054 + proportion: 0.09474419644790176 + newYork_usa: + noise: 0.0075575968616494 + proportion: 0.20255309034522379 + pusan_kor: + noise: 0.0022204132834372364 + proportion: 0.09500755942526971 + qingdao_chn: + noise: 0.0025163323117031356 + proportion: 0.08820727646821312 + shanghai_chn: + noise: 0.012253843912920523 + proportion: 0.08725114792024982 + singapore_sgp: + noise: 0.014556815408633276 + proportion: 0.07609945087001035 + yantian_chn: + noise: 0.007509358525794673 + proportion: 0.09482875155430699 + losAngeles_usa: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.05 + order_distribution: + source: + noise: 0.007441531984981497 + proportion: 0.05 + targets: + oakland_usa: + noise: 0.04265188459544233 + proportion: 0.27616189497862537 + princeRupert_can: + noise: 0.00273897680516611 + proportion: 0.15754923155692402 + qingdao_chn: + noise: 0.01517255937598155 + proportion: 0.1623492157793647 + seattle_usa: + noise: 0.014299850500812094 + proportion: 0.2423747616801366 + shanghai_chn: + noise: 0.0051011672478115 + proportion: 0.16156489600494922 + manzanillo_mex: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.05 + order_distribution: + source: + noise: 0.005425915310841583 + proportion: 0.05 + targets: + manzanillo_mex: + noise: 0.0073296927183103605 + proportion: 0.1642819028757581 + pusan_kor: + noise: 0.018169915140256784 + proportion: 0.1269237319062987 + qingdao_chn: + noise: 0.02383787277319661 + proportion: 0.12072756120502863 + sanAntonio_par: + noise: 0.0025563544317216205 + proportion: 0.10518021746764428 + shanghai_chn: + noise: 0.02058384394808081 + proportion: 0.11985637174894495 + yantian_chn: + noise: 0.00989286132515691 + proportion: 0.12676080545008 + yokohama_jpn: + noise: 0.0007488162438248653 + proportion: 0.23626940934624543 + melbourne_aus: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.015 + order_distribution: + source: + noise: 0.0029979553756433923 + proportion: 0.015 + targets: + singapore_sgp: + noise: 0.0566906410775888 + proportion: 0.3293364398135728 + sydney_aus: + noise: 0.01865388192132147 + proportion: 0.3364188275147848 + yantian_chn: + noise: 0.014238633997660635 + proportion: 0.33424473267164256 + montreal_can: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.015 + order_distribution: + source: + noise: 0.0005621554647653919 + proportion: 0.015 + targets: + bremerhaven_ger: + noise: 0.08779643369212999 + proportion: 0.5172441914124826 + leHavre_fra: + noise: 0.07842477295179612 + proportion: 0.4827558085875176 + newYork_usa: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.03 + order_distribution: + source: + noise: 0.00016057717079772171 + proportion: 0.03 + targets: + bremerhaven_ger: + noise: 0.05981761122488196 + proportion: 0.5332513888590196 + leHavre_fra: + noise: 0.03990533991720246 + proportion: 0.46674861114098054 + oakland_usa: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.03 + order_distribution: + source: + noise: 0.004910915544061917 + proportion: 0.03 + targets: + losAngeles_usa: + noise: 0.048963812038470556 + proportion: 0.30175478642890274 + princeRupert_can: + noise: 0.0254961817963097 + proportion: 0.23086917432389525 + qingdao_chn: + noise: 0.03349874892050399 + proportion: 0.23393880851694335 + shanghai_chn: + noise: 0.012993114869682515 + proportion: 0.23343723073025868 + princeRupert_can: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.015 + order_distribution: + source: + noise: 0.0027667472862969076 + proportion: 0.015 + targets: + losAngeles_usa: + noise: 0.05091486367601389 + proportion: 0.2657898185062753 + oakland_usa: + noise: 0.028397329439457514 + proportion: 0.26818216862044597 + qingdao_chn: + noise: 0.0016646658939369785 + proportion: 0.23313476502804797 + shanghai_chn: + noise: 0.013742472852754001 + proportion: 0.23289324784523086 + pusan_kor: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.06 + order_distribution: + source: + noise: 0.009320427377742116 + proportion: 0.06 + targets: + bremerhaven_ger: + noise: 0.031991420231325084 + proportion: 0.21204226502906795 + leHavre_fra: + noise: 0.005105457599858164 + proportion: 0.08535909653471886 + manzanillo_mex: + noise: 0.005809147275802185 + proportion: 0.0959125358074426 + qingdao_chn: + noise: 0.0033945457967072133 + proportion: 0.051610240792657115 + sanAntonio_par: + noise: 0.001854192137165468 + proportion: 0.035795894860646466 + seattle_usa: + noise: 0.018330317944630015 + proportion: 0.14202580193983683 + shanghai_chn: + noise: 0.009459052846765289 + proportion: 0.05072408949335503 + singapore_sgp: + noise: 0.0016197423680226325 + proportion: 0.04038858042581788 + vancouver_can: + noise: 0.00469787131269142 + proportion: 0.059258114284493034 + yantian_chn: + noise: 0.003856257701007031 + proportion: 0.05774708854229363 + yokohama_jpn: + noise: 0.014803870326138143 + proportion: 0.1691362922896707 + qingdao_chn: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.08 + order_distribution: + source: + noise: 0.007181454765417904 + proportion: 0.08 + targets: + bremerhaven_ger: + noise: 0.02952708760272511 + proportion: 0.19166868266386958 + durban_sau: + noise: 0.0026860007722877676 + proportion: 0.025475188174797537 + itagual_bra: + noise: 0.0023058721270403397 + proportion: 0.020374390901285715 + leHavre_fra: + noise: 0.008262798336030506 + proportion: 0.04940051725539669 + losAngeles_usa: + noise: 0.014599621251693524 + proportion: 0.14605127409748705 + manzanillo_mex: + noise: 0.004578888771735588 + proportion: 0.061252276599797005 + oakland_usa: + noise: 0.0018968128853613635 + proportion: 0.15590868899027802 + princeRupert_can: + noise: 7.825075850514062e-05 + proportion: 0.005409393840032325 + pusan_kor: + noise: 0.0007151691535729856 + proportion: 0.018577689927305203 + santos_bra: + noise: 0.0036806154087330586 + proportion: 0.02037439104592543 + seattle_usa: + noise: 0.0016116464454415448 + proportion: 0.113038562331156 + shanghai_chn: + noise: 0.0011503058311188963 + proportion: 0.010504586083902057 + vancouver_can: + noise: 0.0031091344585237185 + proportion: 0.02008850220677704 + yantian_chn: + noise: 0.0017512950798533396 + proportion: 0.018391591436727996 + yokohama_jpn: + noise: 0.023837737047619264 + proportion: 0.14348426444526224 + sanAntonio_par: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.01 + order_distribution: + source: + noise: 0.0015229565945744795 + proportion: 0.01 + targets: + manzanillo_mex: + noise: 0.029864110340333205 + proportion: 0.1695702560614863 + pusan_kor: + noise: 0.030434021872288986 + proportion: 0.16240810942168046 + qingdao_chn: + noise: 0.031654173870326326 + proportion: 0.1612201993861513 + shanghai_chn: + noise: 0.022840905205567065 + proportion: 0.16105317774270345 + yantian_chn: + noise: 0.022058626605590343 + proportion: 0.16237686231768783 + yokohama_jpn: + noise: 0.020882193597463152 + proportion: 0.18337139507029068 + santos_bra: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.01 + order_distribution: + source: + noise: 0.0017591918390130468 + proportion: 0.01 + targets: + itagual_bra: + noise: 0.00015677478442405662 + proportion: 0.20145578737905992 + qingdao_chn: + noise: 0.007951708444894913 + proportion: 0.19992517738434895 + shanghai_chn: + noise: 0.008394529742993468 + proportion: 0.19975353831991421 + singapore_sgp: + noise: 0.01406059678653582 + proportion: 0.19775168256638195 + yantian_chn: + noise: 0.011310434190498734 + proportion: 0.20111381435029493 + seattle_usa: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.07 + order_distribution: + source: + noise: 0.009178972285261822 + proportion: 0.07 + targets: + losAngeles_usa: + noise: 0.023523817623575388 + proportion: 0.2912736754142012 + pusan_kor: + noise: 0.01876947062956955 + proportion: 0.12538821688519616 + qingdao_chn: + noise: 0.02029673306956859 + proportion: 0.11617748395503369 + shanghai_chn: + noise: 0.011329185762784782 + proportion: 0.11488243812354347 + singapore_sgp: + noise: 0.00024014816912197655 + proportion: 0.09977785851511455 + vancouver_can: + noise: 0.02057342705041732 + proportion: 0.1273542861551621 + yantian_chn: + noise: 0.020083358186626803 + proportion: 0.12514604095174878 + shanghai_chn: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.1 + order_distribution: + source: + noise: 0.017109626305467616 + proportion: 0.1 + targets: + bremerhaven_ger: + noise: 0.021959106592952006 + proportion: 0.21398371679064213 + durban_sau: + noise: 0.00010435314516980227 + proportion: 0.017462167174606275 + itagual_bra: + noise: 0.0020594969751376174 + proportion: 0.011430536063236027 + leHavre_fra: + noise: 0.0009873199353313093 + proportion: 0.04575353580273499 + losAngeles_usa: + noise: 0.014743978442771179 + proportion: 0.16004173887997492 + manzanillo_mex: + noise: 0.0012374290703743276 + proportion: 0.05976808154382374 + oakland_usa: + noise: 0.022426459611386496 + proportion: 0.1716979971992254 + pusan_kor: + noise: 0.0005070099495173756 + proportion: 0.00930596400725694 + qingdao_chn: + noise: 1.8783239577936313e-05 + proportion: 0.0009364036349727401 + santos_bra: + noise: 0.0012407696401487867 + proportion: 0.011430536346128936 + seattle_usa: + noise: 0.011299147787220907 + proportion: 0.12100465463531186 + vancouver_can: + noise: 0.002147321460772641 + proportion: 0.011092475826618629 + yantian_chn: + noise: 0.000883163479902347 + proportion: 0.009085896257062817 + yokohama_jpn: + noise: 0.012495339718107597 + proportion: 0.1570062958384047 + singapore_sgp: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.04 + order_distribution: + source: + noise: 0.0014198451145378962 + proportion: 0.04 + targets: + bremerhaven_ger: + noise: 0.02403294164090424 + proportion: 0.16163465526933787 + durban_sau: + noise: 0.007339857249890276 + proportion: 0.052947229587582224 + itagual_bra: + noise: 0.004826948332878951 + proportion: 0.049611409618781833 + leHavre_fra: + noise: 0.000697323478201817 + proportion: 0.06859394881607536 + melbourne_aus: + noise: 0.009748446374379698 + proportion: 0.054598902525719965 + pusan_kor: + noise: 0.0052864459821632135 + proportion: 0.04843640111672956 + qingdao_chn: + noise: 0.00020264478777542088 + proportion: 0.04380756610509279 + santos_bra: + noise: 0.0060662606703405855 + proportion: 0.0496114180206766 + seattle_usa: + noise: 0.002184489196293904 + proportion: 0.11021203091597927 + shanghai_chn: + noise: 0.00446890645579302 + proportion: 0.043156750350596414 + singapore_sgp: + noise: 0.0005079804300151535 + proportion: 0.03556597867096123 + sydney_aus: + noise: 0.0032814259751614335 + proportion: 0.053961624659387086 + vancouver_can: + noise: 0.0013725415556900408 + proportion: 0.04942443509903704 + yantian_chn: + noise: 0.007866049277209214 + proportion: 0.04831469461601993 + yokohama_jpn: + noise: 0.013995694253814077 + proportion: 0.13012295462802295 + sydney_aus: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.015 + order_distribution: + source: + noise: 5.318259543305715e-05 + proportion: 0.015 + targets: + melbourne_aus: + noise: 0.010959524006592607 + proportion: 0.33665441119009204 + singapore_sgp: + noise: 0.013378806626384264 + proportion: 0.3291642505650358 + yantian_chn: + noise: 0.043398566324619615 + proportion: 0.3341813382448723 + vancouver_can: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.02 + order_distribution: + source: + noise: 0.002260078974868654 + proportion: 0.02 + targets: + pusan_kor: + noise: 0.01022517907397126 + proportion: 0.16323476832180567 + qingdao_chn: + noise: 0.025111111836521687 + proportion: 0.16078294599848347 + seattle_usa: + noise: 0.025666860266381805 + proportion: 0.19595625809774195 + shanghai_chn: + noise: 0.00391575019491024 + proportion: 0.1604382196746799 + singapore_sgp: + noise: 0.009869415376296499 + proportion: 0.15641751563334233 + yantian_chn: + noise: 0.01646828787230074 + proportion: 0.1631702922739467 + yantian_chn: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.07 + order_distribution: + source: + noise: 0.005460106707641842 + proportion: 0.07 + targets: + bremerhaven_ger: + noise: 0.03183934815230198 + proportion: 0.23063506921338092 + durban_sau: + noise: 0.006955761417407662 + proportion: 0.036977112629701755 + itagual_bra: + noise: 0.003879986047538319 + proportion: 0.03103337062146673 + leHavre_fra: + noise: 0.0033746270986396783 + proportion: 0.06485623632186012 + manzanillo_mex: + noise: 0.014297914647911386 + proportion: 0.07866656356344162 + melbourne_aus: + noise: 0.0055998037357593185 + proportion: 0.03992004007199072 + pusan_kor: + noise: 0.004337884540903886 + proportion: 0.028939753926030695 + qingdao_chn: + noise: 0.0034845335834618927 + proportion: 0.020692152164438926 + santos_bra: + noise: 0.0023084677027446535 + proportion: 0.03103337095193521 + seattle_usa: + noise: 0.0043141199296154705 + proportion: 0.13901084519757625 + shanghai_chn: + noise: 0.0001996474876791818 + proportion: 0.01953252624072474 + singapore_sgp: + noise: 0.0011502086812016938 + proportion: 0.006007386215135132 + sydney_aus: + noise: 0.0019319056238978993 + proportion: 0.03878455068512264 + vancouver_can: + noise: 0.0007858696914021227 + proportion: 0.03070023678474206 + yantian_chn: + noise: 0.0016168667042640794 + proportion: 0.02872289950079146 + yokohama_jpn: + noise: 0.023246811699828965 + proportion: 0.17448788591166092 + yokohama_jpn: + capacity: 10000 + empty_return: + buffer_ticks: 1 + noise: 1 + full_return: + buffer_ticks: 1 + noise: 1 + initial_container_proportion: 0.08 + order_distribution: + source: + noise: 0.014254332625813097 + proportion: 0.08 + targets: + manzanillo_mex: + noise: 0.03244047063421112 + proportion: 0.224773870587767 + pusan_kor: + noise: 0.018126081484701975 + proportion: 0.1473122137604972 + qingdao_chn: + noise: 0.017152304680050526 + proportion: 0.1344645529933837 + sanAntonio_par: + noise: 0.0003086488347039955 + proportion: 0.10222734144856105 + shanghai_chn: + noise: 0.0056287128415065625 + proportion: 0.13265815413379464 + singapore_sgp: + noise: 0.019438990767313084 + proportion: 0.1115894561579222 + yantian_chn: + noise: 0.014838419143613479 + proportion: 0.14697441091807414 +routes: + a3s: + - distance_to_next_port: 320 + port_name: yantian_chn + - distance_to_next_port: 240 + port_name: sydney_aus + - distance_to_next_port: 80 + port_name: melbourne_aus + asa: + - distance_to_next_port: 320 + port_name: singapore_sgp + - distance_to_next_port: 260 + port_name: sydney_aus + - distance_to_next_port: 60 + port_name: melbourne_aus + ate1: + - distance_to_next_port: 160 + port_name: newYork_usa + - distance_to_next_port: 240 + port_name: bremerhaven_ger + - distance_to_next_port: 40 + port_name: leHavre_fra + gex1: + - distance_to_next_port: 220 + port_name: montreal_can + - distance_to_next_port: 220 + port_name: bremerhaven_ger + - distance_to_next_port: 40 + port_name: leHavre_fra + ktx5: + - distance_to_next_port: 240 + port_name: singapore_sgp + - distance_to_next_port: 80 + port_name: yantian_chn + - distance_to_next_port: 140 + port_name: yokohama_jpn + ll4: + - distance_to_next_port: 80 + port_name: shanghai_chn + - distance_to_next_port: 60 + port_name: yantian_chn + - distance_to_next_port: 80 + port_name: singapore_sgp + - distance_to_next_port: 380 + port_name: leHavre_fra + - distance_to_next_port: 80 + port_name: bremerhaven_ger + - distance_to_next_port: 420 + port_name: singapore_sgp + - distance_to_next_port: 200 + port_name: qingdao_chn + - distance_to_next_port: 80 + port_name: pusan_kor + pcc1: + - distance_to_next_port: 240 + port_name: qingdao_chn + - distance_to_next_port: 40 + port_name: shanghai_chn + - distance_to_next_port: 240 + port_name: princeRupert_can + - distance_to_next_port: 120 + port_name: losAngeles_usa + - distance_to_next_port: 100 + port_name: oakland_usa + pcc2: + - distance_to_next_port: 80 + port_name: shanghai_chn + - distance_to_next_port: 280 + port_name: losAngeles_usa + - distance_to_next_port: 160 + port_name: seattle_usa + - distance_to_next_port: 280 + port_name: qingdao_chn + pnw1: + - distance_to_next_port: 80 + port_name: yantian_chn + - distance_to_next_port: 260 + port_name: vancouver_can + - distance_to_next_port: 80 + port_name: seattle_usa + - distance_to_next_port: 320 + port_name: pusan_kor + pnw2: + - distance_to_next_port: 200 + port_name: singapore_sgp + - distance_to_next_port: 120 + port_name: yantian_chn + - distance_to_next_port: 80 + port_name: shanghai_chn + - distance_to_next_port: 60 + port_name: pusan_kor + - distance_to_next_port: 200 + port_name: seattle_usa + - distance_to_next_port: 40 + port_name: vancouver_can + - distance_to_next_port: 520 + port_name: qingdao_chn + - distance_to_next_port: 60 + port_name: shanghai_chn + saf3: + - distance_to_next_port: 80 + port_name: qingdao_chn + - distance_to_next_port: 40 + port_name: shanghai_chn + - distance_to_next_port: 60 + port_name: yantian_chn + - distance_to_next_port: 80 + port_name: singapore_sgp + - distance_to_next_port: 260 + port_name: durban_sau + - distance_to_next_port: 360 + port_name: yantian_chn + tla2: + - distance_to_next_port: 60 + port_name: qingdao_chn + - distance_to_next_port: 60 + port_name: shanghai_chn + - distance_to_next_port: 80 + port_name: yantian_chn + - distance_to_next_port: 100 + port_name: singapore_sgp + - distance_to_next_port: 460 + port_name: itagual_bra + - distance_to_next_port: 20 + port_name: santos_bra + - distance_to_next_port: 580 + port_name: singapore_sgp + - distance_to_next_port: 140 + port_name: yantian_chn + - distance_to_next_port: 80 + port_name: shanghai_chn + tlp1: + - distance_to_next_port: 100 + port_name: yantian_chn + - distance_to_next_port: 60 + port_name: shanghai_chn + - distance_to_next_port: 60 + port_name: qingdao_chn + - distance_to_next_port: 40 + port_name: pusan_kor + - distance_to_next_port: 260 + port_name: manzanillo_mex + - distance_to_next_port: 140 + port_name: sanAntonio_par + - distance_to_next_port: 180 + port_name: manzanillo_mex + - distance_to_next_port: 260 + port_name: yokohama_jpn + - distance_to_next_port: 60 + port_name: shanghai_chn +stop_number: +- 4 +- 3 +total_containers: 100000 +vessels: + a3s_vessel_000: + capacity: 871 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: yantian_chn + route_name: a3s + sailing: + noise: 2 + speed: 10 + a3s_vessel_001: + capacity: 967 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: sydney_aus + route_name: a3s + sailing: + noise: 2 + speed: 9 + asa_vessel_000: + capacity: 601 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: singapore_sgp + route_name: asa + sailing: + noise: 1 + speed: 8 + asa_vessel_001: + capacity: 493 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: melbourne_aus + route_name: asa + sailing: + noise: 2 + speed: 10 + ate1_vessel_000: + capacity: 5758 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: newYork_usa + route_name: ate1 + sailing: + noise: 1 + speed: 9 + ate1_vessel_001: + capacity: 6333 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: bremerhaven_ger + route_name: ate1 + sailing: + noise: 1 + speed: 8 + gex1_vessel_000: + capacity: 1033 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: bremerhaven_ger + route_name: gex1 + sailing: + noise: 2 + speed: 10 + gex1_vessel_001: + capacity: 1147 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: montreal_can + route_name: gex1 + sailing: + noise: 1 + speed: 9 + ktx5_vessel_000: + capacity: 1557 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: singapore_sgp + route_name: ktx5 + sailing: + noise: 1 + speed: 8 + ktx5_vessel_001: + capacity: 1275 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: yokohama_jpn + route_name: ktx5 + sailing: + noise: 1 + speed: 10 + ll4_vessel_000: + capacity: 6603 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: bremerhaven_ger + route_name: ll4 + sailing: + noise: 2 + speed: 9 + ll4_vessel_001: + capacity: 7263 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: yantian_chn + route_name: ll4 + sailing: + noise: 2 + speed: 8 + ll4_vessel_002: + capacity: 5943 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: singapore_sgp + route_name: ll4 + sailing: + noise: 1 + speed: 10 + ll4_vessel_003: + capacity: 6603 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: leHavre_fra + route_name: ll4 + sailing: + noise: 1 + speed: 9 + ll4_vessel_004: + capacity: 7263 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: pusan_kor + route_name: ll4 + sailing: + noise: 1 + speed: 8 + ll4_vessel_005: + capacity: 5943 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: qingdao_chn + route_name: ll4 + sailing: + noise: 1 + speed: 10 + pcc1_vessel_000: + capacity: 4922 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: oakland_usa + route_name: pcc1 + sailing: + noise: 1 + speed: 9 + pcc1_vessel_001: + capacity: 5414 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: qingdao_chn + route_name: pcc1 + sailing: + noise: 2 + speed: 8 + pcc1_vessel_002: + capacity: 4430 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: princeRupert_can + route_name: pcc1 + sailing: + noise: 1 + speed: 10 + pcc2_vessel_000: + capacity: 2443 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: seattle_usa + route_name: pcc2 + sailing: + noise: 2 + speed: 9 + pcc2_vessel_001: + capacity: 2687 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: losAngeles_usa + route_name: pcc2 + sailing: + noise: 2 + speed: 8 + pcc2_vessel_002: + capacity: 2199 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: shanghai_chn + route_name: pcc2 + sailing: + noise: 2 + speed: 10 + pnw1_vessel_000: + capacity: 2129 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: seattle_usa + route_name: pnw1 + sailing: + noise: 1 + speed: 9 + pnw1_vessel_001: + capacity: 2341 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: vancouver_can + route_name: pnw1 + sailing: + noise: 1 + speed: 8 + pnw1_vessel_002: + capacity: 1917 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: yantian_chn + route_name: pnw1 + sailing: + noise: 1 + speed: 10 + pnw2_vessel_000: + capacity: 897 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: pusan_kor + route_name: pnw2 + sailing: + noise: 2 + speed: 9 + pnw2_vessel_001: + capacity: 986 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: yantian_chn + route_name: pnw2 + sailing: + noise: 2 + speed: 8 + pnw2_vessel_002: + capacity: 808 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: vancouver_can + route_name: pnw2 + sailing: + noise: 1 + speed: 10 + pnw2_vessel_003: + capacity: 897 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: pusan_kor + route_name: pnw2 + sailing: + noise: 1 + speed: 9 + pnw2_vessel_004: + capacity: 986 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: qingdao_chn + route_name: pnw2 + sailing: + noise: 1 + speed: 8 + saf3_vessel_000: + capacity: 583 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: yantian_chn + route_name: saf3 + sailing: + noise: 1 + speed: 10 + saf3_vessel_001: + capacity: 647 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: qingdao_chn + route_name: saf3 + sailing: + noise: 1 + speed: 9 + saf3_vessel_002: + capacity: 711 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: singapore_sgp + route_name: saf3 + sailing: + noise: 2 + speed: 8 + saf3_vessel_003: + capacity: 583 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: durban_sau + route_name: saf3 + sailing: + noise: 2 + speed: 10 + tla2_vessel_000: + capacity: 1185 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: itagual_bra + route_name: tla2 + sailing: + noise: 1 + speed: 9 + tla2_vessel_001: + capacity: 1303 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: santos_bra + route_name: tla2 + sailing: + noise: 1 + speed: 8 + tla2_vessel_002: + capacity: 1067 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: singapore_sgp + route_name: tla2 + sailing: + noise: 2 + speed: 10 + tla2_vessel_003: + capacity: 1185 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: shanghai_chn + route_name: tla2 + sailing: + noise: 1 + speed: 9 + tla2_vessel_004: + capacity: 1303 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: qingdao_chn + route_name: tla2 + sailing: + noise: 2 + speed: 8 + tla2_vessel_005: + capacity: 1067 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: yantian_chn + route_name: tla2 + sailing: + noise: 1 + speed: 10 + tlp1_vessel_000: + capacity: 6332 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: yantian_chn + route_name: tlp1 + sailing: + noise: 2 + speed: 9 + tlp1_vessel_001: + capacity: 6965 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: manzanillo_mex + route_name: tlp1 + sailing: + noise: 1 + speed: 8 + tlp1_vessel_002: + capacity: 5699 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: pusan_kor + route_name: tlp1 + sailing: + noise: 1 + speed: 10 + tlp1_vessel_003: + capacity: 6332 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: manzanillo_mex + route_name: tlp1 + sailing: + noise: 2 + speed: 9 + tlp1_vessel_004: + capacity: 6965 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: sanAntonio_par + route_name: tlp1 + sailing: + noise: 1 + speed: 8 + tlp1_vessel_005: + capacity: 5699 + parking: + duration: 1 + noise: 1 + route: + initial_port_name: yokohama_jpn + route_name: tlp1 + sailing: + noise: 2 + speed: 10 diff --git a/tests/data/cim/case_data/dump_folder/global_order_proportion.txt b/tests/data/cim/case_data/dump_folder/global_order_proportion.txt new file mode 100644 index 000000000..a97fde48d --- /dev/null +++ b/tests/data/cim/case_data/dump_folder/global_order_proportion.txt @@ -0,0 +1,200 @@ +0.000000000000000000e+00 +1.567000000000000000e+03 +1.629000000000000000e+03 +1.698000000000000000e+03 +1.612000000000000000e+03 +1.550000000000000000e+03 +1.477000000000000000e+03 +1.641000000000000000e+03 +1.353000000000000000e+03 +1.479000000000000000e+03 +1.743000000000000000e+03 +1.754000000000000000e+03 +1.473000000000000000e+03 +1.609000000000000000e+03 +1.819000000000000000e+03 +1.815000000000000000e+03 +1.760000000000000000e+03 +1.925000000000000000e+03 +1.533000000000000000e+03 +1.665000000000000000e+03 +1.434000000000000000e+03 +1.658000000000000000e+03 +1.423000000000000000e+03 +1.707000000000000000e+03 +1.943000000000000000e+03 +2.151000000000000000e+03 +2.262000000000000000e+03 +2.282000000000000000e+03 +2.356000000000000000e+03 +2.256000000000000000e+03 +2.366000000000000000e+03 +2.168000000000000000e+03 +2.207000000000000000e+03 +1.891000000000000000e+03 +1.799000000000000000e+03 +1.682000000000000000e+03 +1.847000000000000000e+03 +2.130000000000000000e+03 +2.296000000000000000e+03 +2.309000000000000000e+03 +2.569000000000000000e+03 +2.930000000000000000e+03 +2.640000000000000000e+03 +2.708000000000000000e+03 +2.631000000000000000e+03 +2.532000000000000000e+03 +2.380000000000000000e+03 +2.231000000000000000e+03 +1.979000000000000000e+03 +1.381000000000000000e+03 +1.829000000000000000e+03 +1.987000000000000000e+03 +2.382000000000000000e+03 +2.837000000000000000e+03 +3.090000000000000000e+03 +2.943000000000000000e+03 +2.974000000000000000e+03 +3.070000000000000000e+03 +2.839000000000000000e+03 +2.647000000000000000e+03 +2.580000000000000000e+03 +1.984000000000000000e+03 +1.829000000000000000e+03 +1.476000000000000000e+03 +1.955000000000000000e+03 +2.228000000000000000e+03 +2.319000000000000000e+03 +2.740000000000000000e+03 +2.715000000000000000e+03 +2.736000000000000000e+03 +3.014000000000000000e+03 +2.945000000000000000e+03 +2.804000000000000000e+03 +2.400000000000000000e+03 +2.446000000000000000e+03 +1.845000000000000000e+03 +1.943000000000000000e+03 +1.302000000000000000e+03 +1.695000000000000000e+03 +1.833000000000000000e+03 +1.966000000000000000e+03 +2.118000000000000000e+03 +2.110000000000000000e+03 +2.437000000000000000e+03 +2.143000000000000000e+03 +2.087000000000000000e+03 +2.120000000000000000e+03 +1.967000000000000000e+03 +1.802000000000000000e+03 +1.599000000000000000e+03 +1.512000000000000000e+03 +1.456000000000000000e+03 +1.748000000000000000e+03 +1.702000000000000000e+03 +1.619000000000000000e+03 +1.882000000000000000e+03 +1.730000000000000000e+03 +1.629000000000000000e+03 +1.814000000000000000e+03 +1.655000000000000000e+03 +1.683000000000000000e+03 +1.560000000000000000e+03 +1.499000000000000000e+03 +1.728000000000000000e+03 +1.557000000000000000e+03 +1.582000000000000000e+03 +1.708000000000000000e+03 +1.432000000000000000e+03 +1.421000000000000000e+03 +1.493000000000000000e+03 +1.674000000000000000e+03 +0.000000000000000000e+00 +0.000000000000000000e+00 +1.403000000000000000e+03 +1.513000000000000000e+03 +1.366000000000000000e+03 +1.593000000000000000e+03 +1.479000000000000000e+03 +1.453000000000000000e+03 +1.378000000000000000e+03 +1.492000000000000000e+03 +1.564000000000000000e+03 +1.600000000000000000e+03 +1.706000000000000000e+03 +1.465000000000000000e+03 +1.877000000000000000e+03 +1.685000000000000000e+03 +1.924000000000000000e+03 +1.963000000000000000e+03 +1.705000000000000000e+03 +1.667000000000000000e+03 +1.571000000000000000e+03 +1.702000000000000000e+03 +1.589000000000000000e+03 +1.459000000000000000e+03 +1.654000000000000000e+03 +1.968000000000000000e+03 +1.837000000000000000e+03 +2.095000000000000000e+03 +2.270000000000000000e+03 +2.252000000000000000e+03 +2.265000000000000000e+03 +2.257000000000000000e+03 +2.364000000000000000e+03 +2.171000000000000000e+03 +1.808000000000000000e+03 +1.795000000000000000e+03 +1.568000000000000000e+03 +1.709000000000000000e+03 +2.176000000000000000e+03 +2.246000000000000000e+03 +2.299000000000000000e+03 +2.682000000000000000e+03 +2.959000000000000000e+03 +2.806000000000000000e+03 +2.871000000000000000e+03 +2.565000000000000000e+03 +2.707000000000000000e+03 +2.222000000000000000e+03 +1.953000000000000000e+03 +1.871000000000000000e+03 +1.435000000000000000e+03 +1.708000000000000000e+03 +2.056000000000000000e+03 +2.343000000000000000e+03 +2.784000000000000000e+03 +2.824000000000000000e+03 +2.940000000000000000e+03 +2.929000000000000000e+03 +3.218000000000000000e+03 +3.008000000000000000e+03 +2.870000000000000000e+03 +2.615000000000000000e+03 +2.247000000000000000e+03 +1.831000000000000000e+03 +1.516000000000000000e+03 +1.827000000000000000e+03 +2.125000000000000000e+03 +2.252000000000000000e+03 +2.612000000000000000e+03 +2.627000000000000000e+03 +2.636000000000000000e+03 +2.985000000000000000e+03 +2.895000000000000000e+03 +2.800000000000000000e+03 +2.429000000000000000e+03 +2.438000000000000000e+03 +2.037000000000000000e+03 +1.878000000000000000e+03 +1.595000000000000000e+03 +1.823000000000000000e+03 +2.094000000000000000e+03 +2.093000000000000000e+03 +2.178000000000000000e+03 +2.206000000000000000e+03 +2.313000000000000000e+03 +2.329000000000000000e+03 +2.056000000000000000e+03 +2.143000000000000000e+03 +1.842000000000000000e+03 diff --git a/tests/data/cim/case_data/dump_folder/misc.yml b/tests/data/cim/case_data/dump_folder/misc.yml new file mode 100644 index 000000000..0656014a5 --- /dev/null +++ b/tests/data/cim/case_data/dump_folder/misc.yml @@ -0,0 +1,10 @@ +container_volume: 1 +dsch_cost_factor: 0.05 +future_stop_number: 3 +load_cost_factor: 0.05 +max_tick: 200 +order_mode: fixed +past_stop_number: 4 +seed: 4096 +total_container: 100000 +version: '1' diff --git a/tests/data/cim/case_data/dump_folder/order_proportion.csv b/tests/data/cim/case_data/dump_folder/order_proportion.csv new file mode 100644 index 000000000..06b5b4b09 --- /dev/null +++ b/tests/data/cim/case_data/dump_folder/order_proportion.csv @@ -0,0 +1,158 @@ +source_port_name,source_port_index,dest_port_name,dest_port_index,proportion,proportion_noise +bremerhaven_ger,0,leHavre_fra,3,0.18755329471737509,0.025432733187591285 +bremerhaven_ger,0,montreal_can,7,0.0756906048807678,0.014981815264884504 +bremerhaven_ger,0,newYork_usa,8,0.47933778254893905,0.07860790872534353 +bremerhaven_ger,0,pusan_kor,11,0.07667664751459422,0.002902145091237068 +bremerhaven_ger,0,qingdao_chn,12,0.0512157380304634,0.0016703057083900894 +bremerhaven_ger,0,shanghai_chn,16,0.04763588234031061,0.004329384963154748 +bremerhaven_ger,0,singapore_sgp,17,0.005882857005910926,0.00014700838968303593 +bremerhaven_ger,0,yantian_chn,20,0.07600719296163895,0.014277274600949947 +durban_sau,1,qingdao_chn,12,0.2502879616953551,0.03386602700899302 +durban_sau,1,shanghai_chn,16,0.2501170152675811,0.04099351916502508 +durban_sau,1,singapore_sgp,17,0.24812321657873454,0.02983331512414685 +durban_sau,1,yantian_chn,20,0.25147180645832923,0.0398838857119423 +itagual_bra,2,qingdao_chn,12,0.19992517767067194,0.006704071585248814 +itagual_bra,2,santos_bra,14,0.20145578663243896,0.01065215665926791 +itagual_bra,2,shanghai_chn,16,0.19975353803822685,0.01889939427623029 +itagual_bra,2,singapore_sgp,17,0.19775168293424744,0.030425501405892584 +itagual_bra,2,yantian_chn,20,0.20111381472441478,0.0038049637706619867 +leHavre_fra,3,bremerhaven_ger,0,0.2613085269688243,0.046713884416130814 +leHavre_fra,3,montreal_can,7,0.09474419644790176,0.007011738109808054 +leHavre_fra,3,newYork_usa,8,0.20255309034522379,0.0075575968616494 +leHavre_fra,3,pusan_kor,11,0.09500755942526971,0.0022204132834372364 +leHavre_fra,3,qingdao_chn,12,0.08820727646821312,0.0025163323117031356 +leHavre_fra,3,shanghai_chn,16,0.08725114792024982,0.012253843912920523 +leHavre_fra,3,singapore_sgp,17,0.07609945087001035,0.014556815408633276 +leHavre_fra,3,yantian_chn,20,0.09482875155430699,0.007509358525794673 +losAngeles_usa,4,oakland_usa,9,0.27616189497862537,0.04265188459544233 +losAngeles_usa,4,princeRupert_can,10,0.15754923155692402,0.00273897680516611 +losAngeles_usa,4,qingdao_chn,12,0.1623492157793647,0.01517255937598155 +losAngeles_usa,4,seattle_usa,15,0.2423747616801366,0.014299850500812094 +losAngeles_usa,4,shanghai_chn,16,0.16156489600494922,0.0051011672478115 +manzanillo_mex,5,manzanillo_mex,5,0.1642819028757581,0.0073296927183103605 +manzanillo_mex,5,pusan_kor,11,0.1269237319062987,0.018169915140256784 +manzanillo_mex,5,qingdao_chn,12,0.12072756120502863,0.02383787277319661 +manzanillo_mex,5,sanAntonio_par,13,0.10518021746764428,0.0025563544317216205 +manzanillo_mex,5,shanghai_chn,16,0.11985637174894495,0.02058384394808081 +manzanillo_mex,5,yantian_chn,20,0.12676080545008,0.00989286132515691 +manzanillo_mex,5,yokohama_jpn,21,0.23626940934624543,0.0007488162438248653 +melbourne_aus,6,singapore_sgp,17,0.3293364398135728,0.0566906410775888 +melbourne_aus,6,sydney_aus,18,0.3364188275147848,0.01865388192132147 +melbourne_aus,6,yantian_chn,20,0.33424473267164256,0.014238633997660635 +montreal_can,7,bremerhaven_ger,0,0.5172441914124826,0.08779643369212999 +montreal_can,7,leHavre_fra,3,0.4827558085875176,0.07842477295179612 +newYork_usa,8,bremerhaven_ger,0,0.5332513888590196,0.05981761122488196 +newYork_usa,8,leHavre_fra,3,0.46674861114098054,0.03990533991720246 +oakland_usa,9,losAngeles_usa,4,0.30175478642890274,0.048963812038470556 +oakland_usa,9,princeRupert_can,10,0.23086917432389525,0.0254961817963097 +oakland_usa,9,qingdao_chn,12,0.23393880851694335,0.03349874892050399 +oakland_usa,9,shanghai_chn,16,0.23343723073025868,0.012993114869682515 +princeRupert_can,10,losAngeles_usa,4,0.2657898185062753,0.05091486367601389 +princeRupert_can,10,oakland_usa,9,0.26818216862044597,0.028397329439457514 +princeRupert_can,10,qingdao_chn,12,0.23313476502804797,0.0016646658939369785 +princeRupert_can,10,shanghai_chn,16,0.23289324784523086,0.013742472852754001 +pusan_kor,11,bremerhaven_ger,0,0.21204226502906795,0.031991420231325084 +pusan_kor,11,leHavre_fra,3,0.08535909653471886,0.005105457599858164 +pusan_kor,11,manzanillo_mex,5,0.0959125358074426,0.005809147275802185 +pusan_kor,11,qingdao_chn,12,0.051610240792657115,0.0033945457967072133 +pusan_kor,11,sanAntonio_par,13,0.035795894860646466,0.001854192137165468 +pusan_kor,11,seattle_usa,15,0.14202580193983683,0.018330317944630015 +pusan_kor,11,shanghai_chn,16,0.05072408949335503,0.009459052846765289 +pusan_kor,11,singapore_sgp,17,0.04038858042581788,0.0016197423680226325 +pusan_kor,11,vancouver_can,19,0.059258114284493034,0.00469787131269142 +pusan_kor,11,yantian_chn,20,0.05774708854229363,0.003856257701007031 +pusan_kor,11,yokohama_jpn,21,0.1691362922896707,0.014803870326138143 +qingdao_chn,12,bremerhaven_ger,0,0.19166868266386958,0.02952708760272511 +qingdao_chn,12,durban_sau,1,0.025475188174797537,0.0026860007722877676 +qingdao_chn,12,itagual_bra,2,0.020374390901285715,0.0023058721270403397 +qingdao_chn,12,leHavre_fra,3,0.04940051725539669,0.008262798336030506 +qingdao_chn,12,losAngeles_usa,4,0.14605127409748705,0.014599621251693524 +qingdao_chn,12,manzanillo_mex,5,0.061252276599797005,0.004578888771735588 +qingdao_chn,12,oakland_usa,9,0.15590868899027802,0.0018968128853613635 +qingdao_chn,12,princeRupert_can,10,0.005409393840032325,7.825075850514062e-05 +qingdao_chn,12,pusan_kor,11,0.018577689927305203,0.0007151691535729856 +qingdao_chn,12,santos_bra,14,0.02037439104592543,0.0036806154087330586 +qingdao_chn,12,seattle_usa,15,0.113038562331156,0.0016116464454415448 +qingdao_chn,12,shanghai_chn,16,0.010504586083902057,0.0011503058311188963 +qingdao_chn,12,vancouver_can,19,0.02008850220677704,0.0031091344585237185 +qingdao_chn,12,yantian_chn,20,0.018391591436727996,0.0017512950798533396 +qingdao_chn,12,yokohama_jpn,21,0.14348426444526224,0.023837737047619264 +sanAntonio_par,13,manzanillo_mex,5,0.1695702560614863,0.029864110340333205 +sanAntonio_par,13,pusan_kor,11,0.16240810942168046,0.030434021872288986 +sanAntonio_par,13,qingdao_chn,12,0.1612201993861513,0.031654173870326326 +sanAntonio_par,13,shanghai_chn,16,0.16105317774270345,0.022840905205567065 +sanAntonio_par,13,yantian_chn,20,0.16237686231768783,0.022058626605590343 +sanAntonio_par,13,yokohama_jpn,21,0.18337139507029068,0.020882193597463152 +santos_bra,14,itagual_bra,2,0.20145578737905992,0.00015677478442405662 +santos_bra,14,qingdao_chn,12,0.19992517738434895,0.007951708444894913 +santos_bra,14,shanghai_chn,16,0.19975353831991421,0.008394529742993468 +santos_bra,14,singapore_sgp,17,0.19775168256638195,0.01406059678653582 +santos_bra,14,yantian_chn,20,0.20111381435029493,0.011310434190498734 +seattle_usa,15,losAngeles_usa,4,0.2912736754142012,0.023523817623575388 +seattle_usa,15,pusan_kor,11,0.12538821688519616,0.01876947062956955 +seattle_usa,15,qingdao_chn,12,0.11617748395503369,0.02029673306956859 +seattle_usa,15,shanghai_chn,16,0.11488243812354347,0.011329185762784782 +seattle_usa,15,singapore_sgp,17,0.09977785851511455,0.00024014816912197655 +seattle_usa,15,vancouver_can,19,0.1273542861551621,0.02057342705041732 +seattle_usa,15,yantian_chn,20,0.12514604095174878,0.020083358186626803 +shanghai_chn,16,bremerhaven_ger,0,0.21398371679064213,0.021959106592952006 +shanghai_chn,16,durban_sau,1,0.017462167174606275,0.00010435314516980227 +shanghai_chn,16,itagual_bra,2,0.011430536063236027,0.0020594969751376174 +shanghai_chn,16,leHavre_fra,3,0.04575353580273499,0.0009873199353313093 +shanghai_chn,16,losAngeles_usa,4,0.16004173887997492,0.014743978442771179 +shanghai_chn,16,manzanillo_mex,5,0.05976808154382374,0.0012374290703743276 +shanghai_chn,16,oakland_usa,9,0.1716979971992254,0.022426459611386496 +shanghai_chn,16,pusan_kor,11,0.00930596400725694,0.0005070099495173756 +shanghai_chn,16,qingdao_chn,12,0.0009364036349727401,1.8783239577936313e-05 +shanghai_chn,16,santos_bra,14,0.011430536346128936,0.0012407696401487867 +shanghai_chn,16,seattle_usa,15,0.12100465463531186,0.011299147787220907 +shanghai_chn,16,vancouver_can,19,0.011092475826618629,0.002147321460772641 +shanghai_chn,16,yantian_chn,20,0.009085896257062817,0.000883163479902347 +shanghai_chn,16,yokohama_jpn,21,0.1570062958384047,0.012495339718107597 +singapore_sgp,17,bremerhaven_ger,0,0.16163465526933787,0.02403294164090424 +singapore_sgp,17,durban_sau,1,0.052947229587582224,0.007339857249890276 +singapore_sgp,17,itagual_bra,2,0.049611409618781833,0.004826948332878951 +singapore_sgp,17,leHavre_fra,3,0.06859394881607536,0.000697323478201817 +singapore_sgp,17,melbourne_aus,6,0.054598902525719965,0.009748446374379698 +singapore_sgp,17,pusan_kor,11,0.04843640111672956,0.0052864459821632135 +singapore_sgp,17,qingdao_chn,12,0.04380756610509279,0.00020264478777542088 +singapore_sgp,17,santos_bra,14,0.0496114180206766,0.0060662606703405855 +singapore_sgp,17,seattle_usa,15,0.11021203091597927,0.002184489196293904 +singapore_sgp,17,shanghai_chn,16,0.043156750350596414,0.00446890645579302 +singapore_sgp,17,singapore_sgp,17,0.03556597867096123,0.0005079804300151535 +singapore_sgp,17,sydney_aus,18,0.053961624659387086,0.0032814259751614335 +singapore_sgp,17,vancouver_can,19,0.04942443509903704,0.0013725415556900408 +singapore_sgp,17,yantian_chn,20,0.04831469461601993,0.007866049277209214 +singapore_sgp,17,yokohama_jpn,21,0.13012295462802295,0.013995694253814077 +sydney_aus,18,melbourne_aus,6,0.33665441119009204,0.010959524006592607 +sydney_aus,18,singapore_sgp,17,0.3291642505650358,0.013378806626384264 +sydney_aus,18,yantian_chn,20,0.3341813382448723,0.043398566324619615 +vancouver_can,19,pusan_kor,11,0.16323476832180567,0.01022517907397126 +vancouver_can,19,qingdao_chn,12,0.16078294599848347,0.025111111836521687 +vancouver_can,19,seattle_usa,15,0.19595625809774195,0.025666860266381805 +vancouver_can,19,shanghai_chn,16,0.1604382196746799,0.00391575019491024 +vancouver_can,19,singapore_sgp,17,0.15641751563334233,0.009869415376296499 +vancouver_can,19,yantian_chn,20,0.1631702922739467,0.01646828787230074 +yantian_chn,20,bremerhaven_ger,0,0.23063506921338092,0.03183934815230198 +yantian_chn,20,durban_sau,1,0.036977112629701755,0.006955761417407662 +yantian_chn,20,itagual_bra,2,0.03103337062146673,0.003879986047538319 +yantian_chn,20,leHavre_fra,3,0.06485623632186012,0.0033746270986396783 +yantian_chn,20,manzanillo_mex,5,0.07866656356344162,0.014297914647911386 +yantian_chn,20,melbourne_aus,6,0.03992004007199072,0.0055998037357593185 +yantian_chn,20,pusan_kor,11,0.028939753926030695,0.004337884540903886 +yantian_chn,20,qingdao_chn,12,0.020692152164438926,0.0034845335834618927 +yantian_chn,20,santos_bra,14,0.03103337095193521,0.0023084677027446535 +yantian_chn,20,seattle_usa,15,0.13901084519757625,0.0043141199296154705 +yantian_chn,20,shanghai_chn,16,0.01953252624072474,0.0001996474876791818 +yantian_chn,20,singapore_sgp,17,0.006007386215135132,0.0011502086812016938 +yantian_chn,20,sydney_aus,18,0.03878455068512264,0.0019319056238978993 +yantian_chn,20,vancouver_can,19,0.03070023678474206,0.0007858696914021227 +yantian_chn,20,yantian_chn,20,0.02872289950079146,0.0016168667042640794 +yantian_chn,20,yokohama_jpn,21,0.17448788591166092,0.023246811699828965 +yokohama_jpn,21,manzanillo_mex,5,0.224773870587767,0.03244047063421112 +yokohama_jpn,21,pusan_kor,11,0.1473122137604972,0.018126081484701975 +yokohama_jpn,21,qingdao_chn,12,0.1344645529933837,0.017152304680050526 +yokohama_jpn,21,sanAntonio_par,13,0.10222734144856105,0.0003086488347039955 +yokohama_jpn,21,shanghai_chn,16,0.13265815413379464,0.0056287128415065625 +yokohama_jpn,21,singapore_sgp,17,0.1115894561579222,0.019438990767313084 +yokohama_jpn,21,yantian_chn,20,0.14697441091807414,0.014838419143613479 diff --git a/tests/data/cim/case_data/dump_folder/ports.csv b/tests/data/cim/case_data/dump_folder/ports.csv new file mode 100644 index 000000000..5a103ca3c --- /dev/null +++ b/tests/data/cim/case_data/dump_folder/ports.csv @@ -0,0 +1,23 @@ +index,name,capacity,empty,order_proportion,order_proportion_noise,empty_return_buffer,empty_return_buffer_noise,full_return_buffer,full_return_buffer_noise +0,bremerhaven_ger,10000,16000,0.16,0.025134751494652507,1,1,1,1 +1,durban_sau,10000,1000,0.01,0.0003905851406890507,1,1,1,1 +2,itagual_bra,10000,1000,0.01,0.0017049012580563228,1,1,1,1 +3,leHavre_fra,10000,6000,0.06,0.010023093144315015,1,1,1,1 +4,losAngeles_usa,10000,5000,0.05,0.007441531984981497,1,1,1,1 +5,manzanillo_mex,10000,5000,0.05,0.005425915310841583,1,1,1,1 +6,melbourne_aus,10000,1500,0.015,0.0029979553756433923,1,1,1,1 +7,montreal_can,10000,1500,0.015,0.0005621554647653919,1,1,1,1 +8,newYork_usa,10000,3000,0.03,0.00016057717079772171,1,1,1,1 +9,oakland_usa,10000,3000,0.03,0.004910915544061917,1,1,1,1 +10,princeRupert_can,10000,1500,0.015,0.0027667472862969076,1,1,1,1 +11,pusan_kor,10000,6000,0.06,0.009320427377742116,1,1,1,1 +12,qingdao_chn,10000,8000,0.08,0.007181454765417904,1,1,1,1 +13,sanAntonio_par,10000,1000,0.01,0.0015229565945744795,1,1,1,1 +14,santos_bra,10000,1000,0.01,0.0017591918390130468,1,1,1,1 +15,seattle_usa,10000,7000,0.07,0.009178972285261822,1,1,1,1 +16,shanghai_chn,10000,10000,0.1,0.017109626305467616,1,1,1,1 +17,singapore_sgp,10000,4000,0.04,0.0014198451145378962,1,1,1,1 +18,sydney_aus,10000,1500,0.015,5.318259543305715e-05,1,1,1,1 +19,vancouver_can,10000,2000,0.02,0.002260078974868654,1,1,1,1 +20,yantian_chn,10000,7000,0.07,0.005460106707641842,1,1,1,1 +21,yokohama_jpn,10000,8000,0.08,0.014254332625813097,1,1,1,1 diff --git a/tests/data/cim/case_data/dump_folder/routes.csv b/tests/data/cim/case_data/dump_folder/routes.csv new file mode 100644 index 000000000..7ff5b8c29 --- /dev/null +++ b/tests/data/cim/case_data/dump_folder/routes.csv @@ -0,0 +1,69 @@ +index,name,port_name,port_index,distance_to_next_port +0,a3s,yantian_chn,20,320 +0,a3s,sydney_aus,18,240 +0,a3s,melbourne_aus,6,80 +1,asa,singapore_sgp,17,320 +1,asa,sydney_aus,18,260 +1,asa,melbourne_aus,6,60 +2,ate1,newYork_usa,8,160 +2,ate1,bremerhaven_ger,0,240 +2,ate1,leHavre_fra,3,40 +3,gex1,montreal_can,7,220 +3,gex1,bremerhaven_ger,0,220 +3,gex1,leHavre_fra,3,40 +4,ktx5,singapore_sgp,17,240 +4,ktx5,yantian_chn,20,80 +4,ktx5,yokohama_jpn,21,140 +5,ll4,shanghai_chn,16,80 +5,ll4,yantian_chn,20,60 +5,ll4,singapore_sgp,17,80 +5,ll4,leHavre_fra,3,380 +5,ll4,bremerhaven_ger,0,80 +5,ll4,singapore_sgp,17,420 +5,ll4,qingdao_chn,12,200 +5,ll4,pusan_kor,11,80 +6,pcc1,qingdao_chn,12,240 +6,pcc1,shanghai_chn,16,40 +6,pcc1,princeRupert_can,10,240 +6,pcc1,losAngeles_usa,4,120 +6,pcc1,oakland_usa,9,100 +7,pcc2,shanghai_chn,16,80 +7,pcc2,losAngeles_usa,4,280 +7,pcc2,seattle_usa,15,160 +7,pcc2,qingdao_chn,12,280 +8,pnw1,yantian_chn,20,80 +8,pnw1,vancouver_can,19,260 +8,pnw1,seattle_usa,15,80 +8,pnw1,pusan_kor,11,320 +9,pnw2,singapore_sgp,17,200 +9,pnw2,yantian_chn,20,120 +9,pnw2,shanghai_chn,16,80 +9,pnw2,pusan_kor,11,60 +9,pnw2,seattle_usa,15,200 +9,pnw2,vancouver_can,19,40 +9,pnw2,qingdao_chn,12,520 +9,pnw2,shanghai_chn,16,60 +10,saf3,qingdao_chn,12,80 +10,saf3,shanghai_chn,16,40 +10,saf3,yantian_chn,20,60 +10,saf3,singapore_sgp,17,80 +10,saf3,durban_sau,1,260 +10,saf3,yantian_chn,20,360 +11,tla2,qingdao_chn,12,60 +11,tla2,shanghai_chn,16,60 +11,tla2,yantian_chn,20,80 +11,tla2,singapore_sgp,17,100 +11,tla2,itagual_bra,2,460 +11,tla2,santos_bra,14,20 +11,tla2,singapore_sgp,17,580 +11,tla2,yantian_chn,20,140 +11,tla2,shanghai_chn,16,80 +12,tlp1,yantian_chn,20,100 +12,tlp1,shanghai_chn,16,60 +12,tlp1,qingdao_chn,12,60 +12,tlp1,pusan_kor,11,40 +12,tlp1,manzanillo_mex,5,260 +12,tlp1,sanAntonio_par,13,140 +12,tlp1,manzanillo_mex,5,180 +12,tlp1,yokohama_jpn,21,260 +12,tlp1,shanghai_chn,16,60 diff --git a/tests/data/cim/case_data/dump_folder/stops.csv b/tests/data/cim/case_data/dump_folder/stops.csv new file mode 100644 index 000000000..16d64d4df --- /dev/null +++ b/tests/data/cim/case_data/dump_folder/stops.csv @@ -0,0 +1,616 @@ +vessel_name,vessel_index,port_name,port_index,arrival_tick,departure_tick +a3s_vessel_000,0,yantian_chn,20,0,1 +a3s_vessel_000,0,sydney_aus,18,38,40 +a3s_vessel_000,0,melbourne_aus,6,65,66 +a3s_vessel_000,0,yantian_chn,20,73,74 +a3s_vessel_000,0,sydney_aus,18,104,105 +a3s_vessel_000,0,melbourne_aus,6,127,129 +a3s_vessel_000,0,yantian_chn,20,139,141 +a3s_vessel_000,0,sydney_aus,18,181,183 +a3s_vessel_000,0,melbourne_aus,6,209,211 +a3s_vessel_000,0,yantian_chn,20,220,222 +a3s_vessel_000,0,sydney_aus,18,255,256 +a3s_vessel_001,1,sydney_aus,18,0,2 +a3s_vessel_001,1,melbourne_aus,6,30,31 +a3s_vessel_001,1,yantian_chn,20,39,40 +a3s_vessel_001,1,sydney_aus,18,77,79 +a3s_vessel_001,1,melbourne_aus,6,108,109 +a3s_vessel_001,1,yantian_chn,20,117,118 +a3s_vessel_001,1,sydney_aus,18,150,152 +a3s_vessel_001,1,melbourne_aus,6,177,179 +a3s_vessel_001,1,yantian_chn,20,191,193 +a3s_vessel_001,1,sydney_aus,18,237,239 +a3s_vessel_001,1,melbourne_aus,6,262,264 +a3s_vessel_001,1,yantian_chn,20,274,276 +asa_vessel_000,2,singapore_sgp,17,0,2 +asa_vessel_000,2,sydney_aus,18,45,47 +asa_vessel_000,2,melbourne_aus,6,77,79 +asa_vessel_000,2,singapore_sgp,17,87,88 +asa_vessel_000,2,sydney_aus,18,129,131 +asa_vessel_000,2,melbourne_aus,6,161,162 +asa_vessel_000,2,singapore_sgp,17,170,171 +asa_vessel_000,2,sydney_aus,18,207,208 +asa_vessel_000,2,melbourne_aus,6,245,246 +asa_vessel_000,2,singapore_sgp,17,254,255 +asa_vessel_001,3,melbourne_aus,6,0,2 +asa_vessel_001,3,singapore_sgp,17,8,9 +asa_vessel_001,3,sydney_aus,18,47,48 +asa_vessel_001,3,melbourne_aus,6,74,75 +asa_vessel_001,3,singapore_sgp,17,81,82 +asa_vessel_001,3,sydney_aus,18,111,113 +asa_vessel_001,3,melbourne_aus,6,138,139 +asa_vessel_001,3,singapore_sgp,17,146,148 +asa_vessel_001,3,sydney_aus,18,185,186 +asa_vessel_001,3,melbourne_aus,6,214,216 +asa_vessel_001,3,singapore_sgp,17,223,224 +asa_vessel_001,3,sydney_aus,18,253,254 +ate1_vessel_000,4,newYork_usa,8,0,1 +ate1_vessel_000,4,bremerhaven_ger,0,19,20 +ate1_vessel_000,4,leHavre_fra,3,46,47 +ate1_vessel_000,4,newYork_usa,8,52,54 +ate1_vessel_000,4,bremerhaven_ger,0,74,75 +ate1_vessel_000,4,leHavre_fra,3,100,101 +ate1_vessel_000,4,newYork_usa,8,106,108 +ate1_vessel_000,4,bremerhaven_ger,0,127,128 +ate1_vessel_000,4,leHavre_fra,3,157,158 +ate1_vessel_000,4,newYork_usa,8,163,164 +ate1_vessel_000,4,bremerhaven_ger,0,183,185 +ate1_vessel_000,4,leHavre_fra,3,210,212 +ate1_vessel_000,4,newYork_usa,8,217,219 +ate1_vessel_000,4,bremerhaven_ger,0,238,240 +ate1_vessel_001,5,bremerhaven_ger,0,0,2 +ate1_vessel_001,5,leHavre_fra,3,34,36 +ate1_vessel_001,5,newYork_usa,8,42,44 +ate1_vessel_001,5,bremerhaven_ger,0,63,64 +ate1_vessel_001,5,leHavre_fra,3,92,94 +ate1_vessel_001,5,newYork_usa,8,100,102 +ate1_vessel_001,5,bremerhaven_ger,0,120,121 +ate1_vessel_001,5,leHavre_fra,3,151,152 +ate1_vessel_001,5,newYork_usa,8,158,160 +ate1_vessel_001,5,bremerhaven_ger,0,183,185 +ate1_vessel_001,5,leHavre_fra,3,212,214 +ate1_vessel_001,5,newYork_usa,8,220,222 +ate1_vessel_001,5,bremerhaven_ger,0,242,244 +gex1_vessel_000,6,bremerhaven_ger,0,0,1 +gex1_vessel_000,6,leHavre_fra,3,20,22 +gex1_vessel_000,6,montreal_can,7,26,27 +gex1_vessel_000,6,bremerhaven_ger,0,47,48 +gex1_vessel_000,6,leHavre_fra,3,73,75 +gex1_vessel_000,6,montreal_can,7,80,82 +gex1_vessel_000,6,bremerhaven_ger,0,102,103 +gex1_vessel_000,6,leHavre_fra,3,131,133 +gex1_vessel_000,6,montreal_can,7,138,139 +gex1_vessel_000,6,bremerhaven_ger,0,165,166 +gex1_vessel_000,6,leHavre_fra,3,190,192 +gex1_vessel_000,6,montreal_can,7,196,197 +gex1_vessel_000,6,bremerhaven_ger,0,216,218 +gex1_vessel_000,6,leHavre_fra,3,238,240 +gex1_vessel_000,6,montreal_can,7,245,247 +gex1_vessel_001,7,montreal_can,7,0,2 +gex1_vessel_001,7,bremerhaven_ger,0,30,31 +gex1_vessel_001,7,leHavre_fra,3,57,58 +gex1_vessel_001,7,montreal_can,7,63,64 +gex1_vessel_001,7,bremerhaven_ger,0,88,90 +gex1_vessel_001,7,leHavre_fra,3,115,117 +gex1_vessel_001,7,montreal_can,7,122,123 +gex1_vessel_001,7,bremerhaven_ger,0,149,151 +gex1_vessel_001,7,leHavre_fra,3,175,177 +gex1_vessel_001,7,montreal_can,7,182,183 +gex1_vessel_001,7,bremerhaven_ger,0,210,212 +gex1_vessel_001,7,leHavre_fra,3,237,238 +gex1_vessel_001,7,montreal_can,7,243,245 +ktx5_vessel_000,8,singapore_sgp,17,0,2 +ktx5_vessel_000,8,yantian_chn,20,35,37 +ktx5_vessel_000,8,yokohama_jpn,21,47,49 +ktx5_vessel_000,8,singapore_sgp,17,69,70 +ktx5_vessel_000,8,yantian_chn,20,103,104 +ktx5_vessel_000,8,yokohama_jpn,21,114,116 +ktx5_vessel_000,8,singapore_sgp,17,135,136 +ktx5_vessel_000,8,yantian_chn,20,164,166 +ktx5_vessel_000,8,yokohama_jpn,21,176,178 +ktx5_vessel_000,8,singapore_sgp,17,196,197 +ktx5_vessel_000,8,yantian_chn,20,225,226 +ktx5_vessel_000,8,yokohama_jpn,21,236,238 +ktx5_vessel_000,8,singapore_sgp,17,257,259 +ktx5_vessel_001,9,yokohama_jpn,21,0,1 +ktx5_vessel_001,9,singapore_sgp,17,14,15 +ktx5_vessel_001,9,yantian_chn,20,38,39 +ktx5_vessel_001,9,yokohama_jpn,21,48,49 +ktx5_vessel_001,9,singapore_sgp,17,63,64 +ktx5_vessel_001,9,yantian_chn,20,86,88 +ktx5_vessel_001,9,yokohama_jpn,21,96,97 +ktx5_vessel_001,9,singapore_sgp,17,112,114 +ktx5_vessel_001,9,yantian_chn,20,140,141 +ktx5_vessel_001,9,yokohama_jpn,21,149,150 +ktx5_vessel_001,9,singapore_sgp,17,166,168 +ktx5_vessel_001,9,yantian_chn,20,193,194 +ktx5_vessel_001,9,yokohama_jpn,21,203,204 +ktx5_vessel_001,9,singapore_sgp,17,219,220 +ktx5_vessel_001,9,yantian_chn,20,242,243 +ll4_vessel_000,10,bremerhaven_ger,0,0,2 +ll4_vessel_000,10,singapore_sgp,17,13,15 +ll4_vessel_000,10,qingdao_chn,12,69,71 +ll4_vessel_000,10,pusan_kor,11,97,98 +ll4_vessel_000,10,shanghai_chn,16,108,110 +ll4_vessel_000,10,yantian_chn,20,119,120 +ll4_vessel_000,10,singapore_sgp,17,129,131 +ll4_vessel_000,10,leHavre_fra,3,140,141 +ll4_vessel_000,10,bremerhaven_ger,0,193,194 +ll4_vessel_000,10,singapore_sgp,17,205,207 +ll4_vessel_000,10,qingdao_chn,12,247,249 +ll4_vessel_000,10,pusan_kor,11,272,273 +ll4_vessel_001,11,yantian_chn,20,0,2 +ll4_vessel_001,11,singapore_sgp,17,12,14 +ll4_vessel_001,11,leHavre_fra,3,25,27 +ll4_vessel_001,11,bremerhaven_ger,0,78,80 +ll4_vessel_001,11,singapore_sgp,17,94,96 +ll4_vessel_001,11,qingdao_chn,12,163,164 +ll4_vessel_001,11,pusan_kor,11,191,192 +ll4_vessel_001,11,shanghai_chn,16,202,203 +ll4_vessel_001,11,yantian_chn,20,215,216 +ll4_vessel_001,11,singapore_sgp,17,224,226 +ll4_vessel_002,12,singapore_sgp,17,0,1 +ll4_vessel_002,12,leHavre_fra,3,10,12 +ll4_vessel_002,12,bremerhaven_ger,0,51,53 +ll4_vessel_002,12,singapore_sgp,17,61,62 +ll4_vessel_002,12,qingdao_chn,12,106,108 +ll4_vessel_002,12,pusan_kor,11,131,133 +ll4_vessel_002,12,shanghai_chn,16,142,144 +ll4_vessel_002,12,yantian_chn,20,152,153 +ll4_vessel_002,12,singapore_sgp,17,159,160 +ll4_vessel_002,12,leHavre_fra,3,169,171 +ll4_vessel_002,12,bremerhaven_ger,0,207,209 +ll4_vessel_002,12,singapore_sgp,17,218,220 +ll4_vessel_002,12,qingdao_chn,12,260,261 +ll4_vessel_003,13,leHavre_fra,3,0,2 +ll4_vessel_003,13,bremerhaven_ger,0,46,47 +ll4_vessel_003,13,singapore_sgp,17,56,58 +ll4_vessel_003,13,qingdao_chn,12,102,103 +ll4_vessel_003,13,pusan_kor,11,124,125 +ll4_vessel_003,13,shanghai_chn,16,134,136 +ll4_vessel_003,13,yantian_chn,20,146,147 +ll4_vessel_003,13,singapore_sgp,17,154,155 +ll4_vessel_003,13,leHavre_fra,3,165,166 +ll4_vessel_003,13,bremerhaven_ger,0,209,211 +ll4_vessel_003,13,singapore_sgp,17,220,221 +ll4_vessel_003,13,qingdao_chn,12,267,268 +ll4_vessel_004,14,pusan_kor,11,0,2 +ll4_vessel_004,14,shanghai_chn,16,12,14 +ll4_vessel_004,14,yantian_chn,20,23,25 +ll4_vessel_004,14,singapore_sgp,17,34,36 +ll4_vessel_004,14,leHavre_fra,3,47,48 +ll4_vessel_004,14,bremerhaven_ger,0,93,95 +ll4_vessel_004,14,singapore_sgp,17,107,109 +ll4_vessel_004,14,qingdao_chn,12,168,170 +ll4_vessel_004,14,pusan_kor,11,194,196 +ll4_vessel_004,14,shanghai_chn,16,207,208 +ll4_vessel_004,14,yantian_chn,20,218,219 +ll4_vessel_004,14,singapore_sgp,17,228,230 +ll4_vessel_005,15,qingdao_chn,12,0,2 +ll4_vessel_005,15,pusan_kor,11,22,24 +ll4_vessel_005,15,shanghai_chn,16,32,33 +ll4_vessel_005,15,yantian_chn,20,41,42 +ll4_vessel_005,15,singapore_sgp,17,49,51 +ll4_vessel_005,15,leHavre_fra,3,60,62 +ll4_vessel_005,15,bremerhaven_ger,0,98,99 +ll4_vessel_005,15,singapore_sgp,17,107,109 +ll4_vessel_005,15,qingdao_chn,12,154,155 +ll4_vessel_005,15,pusan_kor,11,177,178 +ll4_vessel_005,15,shanghai_chn,16,187,188 +ll4_vessel_005,15,yantian_chn,20,197,198 +ll4_vessel_005,15,singapore_sgp,17,204,206 +ll4_vessel_005,15,leHavre_fra,3,215,216 +ll4_vessel_005,15,bremerhaven_ger,0,253,254 +pcc1_vessel_000,16,oakland_usa,9,0,2 +pcc1_vessel_000,16,qingdao_chn,12,14,16 +pcc1_vessel_000,16,shanghai_chn,16,45,46 +pcc1_vessel_000,16,princeRupert_can,10,51,53 +pcc1_vessel_000,16,losAngeles_usa,4,79,81 +pcc1_vessel_000,16,oakland_usa,9,95,96 +pcc1_vessel_000,16,qingdao_chn,12,109,110 +pcc1_vessel_000,16,shanghai_chn,16,136,137 +pcc1_vessel_000,16,princeRupert_can,10,142,144 +pcc1_vessel_000,16,losAngeles_usa,4,170,172 +pcc1_vessel_000,16,oakland_usa,9,185,186 +pcc1_vessel_000,16,qingdao_chn,12,197,198 +pcc1_vessel_000,16,shanghai_chn,16,223,225 +pcc1_vessel_000,16,princeRupert_can,10,230,232 +pcc1_vessel_000,16,losAngeles_usa,4,259,261 +pcc1_vessel_001,17,qingdao_chn,12,0,2 +pcc1_vessel_001,17,shanghai_chn,16,34,36 +pcc1_vessel_001,17,princeRupert_can,10,43,45 +pcc1_vessel_001,17,losAngeles_usa,4,70,71 +pcc1_vessel_001,17,oakland_usa,9,88,90 +pcc1_vessel_001,17,qingdao_chn,12,106,108 +pcc1_vessel_001,17,shanghai_chn,16,142,143 +pcc1_vessel_001,17,princeRupert_can,10,148,149 +pcc1_vessel_001,17,losAngeles_usa,4,181,182 +pcc1_vessel_001,17,oakland_usa,9,200,202 +pcc1_vessel_001,17,qingdao_chn,12,213,215 +pcc1_vessel_001,17,shanghai_chn,16,244,246 +pcc1_vessel_001,17,princeRupert_can,10,251,252 +pcc1_vessel_002,18,princeRupert_can,10,0,2 +pcc1_vessel_002,18,losAngeles_usa,4,27,28 +pcc1_vessel_002,18,oakland_usa,9,41,42 +pcc1_vessel_002,18,qingdao_chn,12,53,54 +pcc1_vessel_002,18,shanghai_chn,16,78,79 +pcc1_vessel_002,18,princeRupert_can,10,83,84 +pcc1_vessel_002,18,losAngeles_usa,4,109,111 +pcc1_vessel_002,18,oakland_usa,9,123,125 +pcc1_vessel_002,18,qingdao_chn,12,136,137 +pcc1_vessel_002,18,shanghai_chn,16,163,164 +pcc1_vessel_002,18,princeRupert_can,10,168,170 +pcc1_vessel_002,18,losAngeles_usa,4,193,195 +pcc1_vessel_002,18,oakland_usa,9,207,209 +pcc1_vessel_002,18,qingdao_chn,12,220,222 +pcc1_vessel_002,18,shanghai_chn,16,249,250 +pcc2_vessel_000,19,seattle_usa,15,0,2 +pcc2_vessel_000,19,qingdao_chn,12,21,22 +pcc2_vessel_000,19,shanghai_chn,16,62,63 +pcc2_vessel_000,19,losAngeles_usa,4,72,74 +pcc2_vessel_000,19,seattle_usa,15,100,101 +pcc2_vessel_000,19,qingdao_chn,12,117,118 +pcc2_vessel_000,19,shanghai_chn,16,154,155 +pcc2_vessel_000,19,losAngeles_usa,4,166,167 +pcc2_vessel_000,19,seattle_usa,15,196,198 +pcc2_vessel_000,19,qingdao_chn,12,213,214 +pcc2_vessel_000,19,shanghai_chn,16,242,244 +pcc2_vessel_000,19,losAngeles_usa,4,253,255 +pcc2_vessel_001,20,losAngeles_usa,4,0,1 +pcc2_vessel_001,20,seattle_usa,15,41,43 +pcc2_vessel_001,20,qingdao_chn,12,60,62 +pcc2_vessel_001,20,shanghai_chn,16,96,97 +pcc2_vessel_001,20,losAngeles_usa,4,109,110 +pcc2_vessel_001,20,seattle_usa,15,145,147 +pcc2_vessel_001,20,qingdao_chn,12,165,167 +pcc2_vessel_001,20,shanghai_chn,16,197,199 +pcc2_vessel_001,20,losAngeles_usa,4,212,213 +pcc2_vessel_001,20,seattle_usa,15,247,248 +pcc2_vessel_001,20,qingdao_chn,12,274,275 +pcc2_vessel_002,21,shanghai_chn,16,0,2 +pcc2_vessel_002,21,losAngeles_usa,4,10,11 +pcc2_vessel_002,21,seattle_usa,15,40,41 +pcc2_vessel_002,21,qingdao_chn,12,61,62 +pcc2_vessel_002,21,shanghai_chn,16,92,93 +pcc2_vessel_002,21,losAngeles_usa,4,101,102 +pcc2_vessel_002,21,seattle_usa,15,128,130 +pcc2_vessel_002,21,qingdao_chn,12,149,150 +pcc2_vessel_002,21,shanghai_chn,16,183,184 +pcc2_vessel_002,21,losAngeles_usa,4,191,192 +pcc2_vessel_002,21,seattle_usa,15,218,220 +pcc2_vessel_002,21,qingdao_chn,12,240,242 +pcc2_vessel_002,21,shanghai_chn,16,270,272 +pnw1_vessel_000,22,seattle_usa,15,0,2 +pnw1_vessel_000,22,pusan_kor,11,11,13 +pnw1_vessel_000,22,yantian_chn,20,48,50 +pnw1_vessel_000,22,vancouver_can,19,59,61 +pnw1_vessel_000,22,seattle_usa,15,92,94 +pnw1_vessel_000,22,pusan_kor,11,104,105 +pnw1_vessel_000,22,yantian_chn,20,139,141 +pnw1_vessel_000,22,vancouver_can,19,151,153 +pnw1_vessel_000,22,seattle_usa,15,182,184 +pnw1_vessel_000,22,pusan_kor,11,194,195 +pnw1_vessel_000,22,yantian_chn,20,230,232 +pnw1_vessel_000,22,vancouver_can,19,241,242 +pnw1_vessel_000,22,seattle_usa,15,272,274 +pnw1_vessel_001,23,vancouver_can,19,0,2 +pnw1_vessel_001,23,seattle_usa,15,38,39 +pnw1_vessel_001,23,pusan_kor,11,50,52 +pnw1_vessel_001,23,yantian_chn,20,90,92 +pnw1_vessel_001,23,vancouver_can,19,104,106 +pnw1_vessel_001,23,seattle_usa,15,140,142 +pnw1_vessel_001,23,pusan_kor,11,152,154 +pnw1_vessel_001,23,yantian_chn,20,196,198 +pnw1_vessel_001,23,vancouver_can,19,208,210 +pnw1_vessel_001,23,seattle_usa,15,241,242 +pnw1_vessel_001,23,pusan_kor,11,252,254 +pnw1_vessel_002,24,yantian_chn,20,0,1 +pnw1_vessel_002,24,vancouver_can,19,9,10 +pnw1_vessel_002,24,seattle_usa,15,35,36 +pnw1_vessel_002,24,pusan_kor,11,45,46 +pnw1_vessel_002,24,yantian_chn,20,76,78 +pnw1_vessel_002,24,vancouver_can,19,86,87 +pnw1_vessel_002,24,seattle_usa,15,114,115 +pnw1_vessel_002,24,pusan_kor,11,123,124 +pnw1_vessel_002,24,yantian_chn,20,156,157 +pnw1_vessel_002,24,vancouver_can,19,165,167 +pnw1_vessel_002,24,seattle_usa,15,194,195 +pnw1_vessel_002,24,pusan_kor,11,204,205 +pnw1_vessel_002,24,yantian_chn,20,237,239 +pnw1_vessel_002,24,vancouver_can,19,248,249 +pnw2_vessel_000,25,pusan_kor,11,0,1 +pnw2_vessel_000,25,seattle_usa,15,9,10 +pnw2_vessel_000,25,vancouver_can,19,36,38 +pnw2_vessel_000,25,qingdao_chn,12,44,46 +pnw2_vessel_000,25,shanghai_chn,16,97,99 +pnw2_vessel_000,25,singapore_sgp,17,107,109 +pnw2_vessel_000,25,yantian_chn,20,133,135 +pnw2_vessel_000,25,shanghai_chn,16,150,151 +pnw2_vessel_000,25,pusan_kor,11,161,163 +pnw2_vessel_000,25,seattle_usa,15,170,172 +pnw2_vessel_000,25,vancouver_can,19,194,196 +pnw2_vessel_000,25,qingdao_chn,12,201,202 +pnw2_vessel_000,25,shanghai_chn,16,254,255 +pnw2_vessel_000,25,singapore_sgp,17,263,264 +pnw2_vessel_001,26,yantian_chn,20,0,2 +pnw2_vessel_001,26,shanghai_chn,16,16,18 +pnw2_vessel_001,26,pusan_kor,11,27,28 +pnw2_vessel_001,26,seattle_usa,15,37,38 +pnw2_vessel_001,26,vancouver_can,19,71,72 +pnw2_vessel_001,26,qingdao_chn,12,78,80 +pnw2_vessel_001,26,shanghai_chn,16,134,136 +pnw2_vessel_001,26,singapore_sgp,17,146,148 +pnw2_vessel_001,26,yantian_chn,20,174,176 +pnw2_vessel_001,26,shanghai_chn,16,189,191 +pnw2_vessel_001,26,pusan_kor,11,200,202 +pnw2_vessel_001,26,seattle_usa,15,210,212 +pnw2_vessel_001,26,vancouver_can,19,242,244 +pnw2_vessel_001,26,qingdao_chn,12,251,252 +pnw2_vessel_002,27,vancouver_can,19,0,2 +pnw2_vessel_002,27,qingdao_chn,12,6,7 +pnw2_vessel_002,27,shanghai_chn,16,57,59 +pnw2_vessel_002,27,singapore_sgp,17,66,68 +pnw2_vessel_002,27,yantian_chn,20,89,91 +pnw2_vessel_002,27,shanghai_chn,16,103,105 +pnw2_vessel_002,27,pusan_kor,11,113,115 +pnw2_vessel_002,27,seattle_usa,15,122,124 +pnw2_vessel_002,27,vancouver_can,19,144,146 +pnw2_vessel_002,27,qingdao_chn,12,150,152 +pnw2_vessel_002,27,shanghai_chn,16,209,211 +pnw2_vessel_002,27,singapore_sgp,17,218,219 +pnw2_vessel_002,27,yantian_chn,20,238,240 +pnw2_vessel_003,28,pusan_kor,11,0,2 +pnw2_vessel_003,28,seattle_usa,15,9,10 +pnw2_vessel_003,28,vancouver_can,19,34,36 +pnw2_vessel_003,28,qingdao_chn,12,41,42 +pnw2_vessel_003,28,shanghai_chn,16,103,104 +pnw2_vessel_003,28,singapore_sgp,17,112,113 +pnw2_vessel_003,28,yantian_chn,20,135,136 +pnw2_vessel_003,28,shanghai_chn,16,149,150 +pnw2_vessel_003,28,pusan_kor,11,159,160 +pnw2_vessel_003,28,seattle_usa,15,167,168 +pnw2_vessel_003,28,vancouver_can,19,192,194 +pnw2_vessel_003,28,qingdao_chn,12,199,201 +pnw2_vessel_003,28,shanghai_chn,16,262,264 +pnw2_vessel_003,28,singapore_sgp,17,271,272 +pnw2_vessel_003,28,yantian_chn,20,294,295 +pnw2_vessel_004,29,qingdao_chn,12,0,2 +pnw2_vessel_004,29,shanghai_chn,16,76,77 +pnw2_vessel_004,29,singapore_sgp,17,84,86 +pnw2_vessel_004,29,yantian_chn,20,110,112 +pnw2_vessel_004,29,shanghai_chn,16,130,131 +pnw2_vessel_004,29,pusan_kor,11,143,145 +pnw2_vessel_004,29,seattle_usa,15,153,155 +pnw2_vessel_004,29,vancouver_can,19,179,180 +pnw2_vessel_004,29,qingdao_chn,12,185,187 +pnw2_vessel_004,29,shanghai_chn,16,252,253 +pnw2_vessel_004,29,singapore_sgp,17,260,262 +pnw2_vessel_004,29,yantian_chn,20,285,287 +saf3_vessel_000,30,yantian_chn,20,0,1 +saf3_vessel_000,30,singapore_sgp,17,8,10 +saf3_vessel_000,30,durban_sau,1,18,19 +saf3_vessel_000,30,yantian_chn,20,48,50 +saf3_vessel_000,30,qingdao_chn,12,86,88 +saf3_vessel_000,30,shanghai_chn,16,96,97 +saf3_vessel_000,30,yantian_chn,20,101,102 +saf3_vessel_000,30,singapore_sgp,17,109,111 +saf3_vessel_000,30,durban_sau,1,119,120 +saf3_vessel_000,30,yantian_chn,20,146,148 +saf3_vessel_000,30,qingdao_chn,12,184,185 +saf3_vessel_000,30,shanghai_chn,16,193,194 +saf3_vessel_000,30,yantian_chn,20,198,199 +saf3_vessel_000,30,singapore_sgp,17,205,206 +saf3_vessel_000,30,durban_sau,1,214,216 +saf3_vessel_000,30,yantian_chn,20,242,244 +saf3_vessel_001,31,qingdao_chn,12,0,2 +saf3_vessel_001,31,shanghai_chn,16,12,14 +saf3_vessel_001,31,yantian_chn,20,19,20 +saf3_vessel_001,31,singapore_sgp,17,28,30 +saf3_vessel_001,31,durban_sau,1,40,42 +saf3_vessel_001,31,yantian_chn,20,71,73 +saf3_vessel_001,31,qingdao_chn,12,116,118 +saf3_vessel_001,31,shanghai_chn,16,128,130 +saf3_vessel_001,31,yantian_chn,20,135,137 +saf3_vessel_001,31,singapore_sgp,17,144,145 +saf3_vessel_001,31,durban_sau,1,155,156 +saf3_vessel_001,31,yantian_chn,20,188,189 +saf3_vessel_001,31,qingdao_chn,12,230,232 +saf3_vessel_001,31,shanghai_chn,16,241,243 +saf3_vessel_001,31,yantian_chn,20,248,250 +saf3_vessel_002,32,singapore_sgp,17,0,2 +saf3_vessel_002,32,durban_sau,1,14,16 +saf3_vessel_002,32,yantian_chn,20,53,55 +saf3_vessel_002,32,qingdao_chn,12,96,97 +saf3_vessel_002,32,shanghai_chn,16,110,112 +saf3_vessel_002,32,yantian_chn,20,117,118 +saf3_vessel_002,32,singapore_sgp,17,128,130 +saf3_vessel_002,32,durban_sau,1,142,143 +saf3_vessel_002,32,yantian_chn,20,175,177 +saf3_vessel_002,32,qingdao_chn,12,222,224 +saf3_vessel_002,32,shanghai_chn,16,233,235 +saf3_vessel_002,32,yantian_chn,20,240,241 +saf3_vessel_003,33,durban_sau,1,0,2 +saf3_vessel_003,33,yantian_chn,20,29,30 +saf3_vessel_003,33,qingdao_chn,12,63,65 +saf3_vessel_003,33,shanghai_chn,16,74,76 +saf3_vessel_003,33,yantian_chn,20,80,82 +saf3_vessel_003,33,singapore_sgp,17,89,91 +saf3_vessel_003,33,durban_sau,1,98,100 +saf3_vessel_003,33,yantian_chn,20,122,123 +saf3_vessel_003,33,qingdao_chn,12,163,165 +saf3_vessel_003,33,shanghai_chn,16,173,174 +saf3_vessel_003,33,yantian_chn,20,178,180 +saf3_vessel_003,33,singapore_sgp,17,187,189 +saf3_vessel_003,33,durban_sau,1,196,197 +saf3_vessel_003,33,yantian_chn,20,225,227 +saf3_vessel_003,33,qingdao_chn,12,261,263 +saf3_vessel_003,33,shanghai_chn,16,271,272 +tla2_vessel_000,34,itagual_bra,2,0,1 +tla2_vessel_000,34,santos_bra,14,53,55 +tla2_vessel_000,34,singapore_sgp,17,58,60 +tla2_vessel_000,34,yantian_chn,20,131,133 +tla2_vessel_000,34,shanghai_chn,16,151,153 +tla2_vessel_000,34,qingdao_chn,12,163,165 +tla2_vessel_000,34,shanghai_chn,16,173,175 +tla2_vessel_000,34,yantian_chn,20,182,184 +tla2_vessel_000,34,singapore_sgp,17,193,194 +tla2_vessel_000,34,itagual_bra,2,205,206 +tla2_vessel_000,34,santos_bra,14,258,259 +tla2_vessel_000,34,singapore_sgp,17,262,264 +tla2_vessel_001,35,santos_bra,14,0,2 +tla2_vessel_001,35,singapore_sgp,17,5,6 +tla2_vessel_001,35,yantian_chn,20,76,78 +tla2_vessel_001,35,shanghai_chn,16,97,99 +tla2_vessel_001,35,qingdao_chn,12,109,110 +tla2_vessel_001,35,shanghai_chn,16,118,120 +tla2_vessel_001,35,yantian_chn,20,127,128 +tla2_vessel_001,35,singapore_sgp,17,138,139 +tla2_vessel_001,35,itagual_bra,2,152,154 +tla2_vessel_001,35,santos_bra,14,209,210 +tla2_vessel_001,35,singapore_sgp,17,213,215 +tla2_vessel_001,35,yantian_chn,20,291,292 +tla2_vessel_002,36,singapore_sgp,17,0,1 +tla2_vessel_002,36,itagual_bra,2,12,14 +tla2_vessel_002,36,santos_bra,14,64,66 +tla2_vessel_002,36,singapore_sgp,17,69,71 +tla2_vessel_002,36,yantian_chn,20,124,125 +tla2_vessel_002,36,shanghai_chn,16,138,139 +tla2_vessel_002,36,qingdao_chn,12,146,147 +tla2_vessel_002,36,shanghai_chn,16,153,155 +tla2_vessel_002,36,yantian_chn,20,162,164 +tla2_vessel_002,36,singapore_sgp,17,174,176 +tla2_vessel_002,36,itagual_bra,2,185,186 +tla2_vessel_002,36,santos_bra,14,243,245 +tla2_vessel_002,36,singapore_sgp,17,248,250 +tla2_vessel_002,36,yantian_chn,20,301,303 +tla2_vessel_003,37,shanghai_chn,16,0,2 +tla2_vessel_003,37,yantian_chn,20,9,10 +tla2_vessel_003,37,singapore_sgp,17,19,21 +tla2_vessel_003,37,itagual_bra,2,34,35 +tla2_vessel_003,37,santos_bra,14,90,92 +tla2_vessel_003,37,singapore_sgp,17,95,97 +tla2_vessel_003,37,yantian_chn,20,169,170 +tla2_vessel_003,37,shanghai_chn,16,185,187 +tla2_vessel_003,37,qingdao_chn,12,196,198 +tla2_vessel_003,37,shanghai_chn,16,205,206 +tla2_vessel_003,37,yantian_chn,20,213,215 +tla2_vessel_003,37,singapore_sgp,17,224,226 +tla2_vessel_004,38,qingdao_chn,12,0,2 +tla2_vessel_004,38,shanghai_chn,16,9,11 +tla2_vessel_004,38,yantian_chn,20,21,22 +tla2_vessel_004,38,singapore_sgp,17,32,33 +tla2_vessel_004,38,itagual_bra,2,46,48 +tla2_vessel_004,38,santos_bra,14,124,126 +tla2_vessel_004,38,singapore_sgp,17,129,131 +tla2_vessel_004,38,yantian_chn,20,210,212 +tla2_vessel_004,38,shanghai_chn,16,232,234 +tla2_vessel_004,38,qingdao_chn,12,245,247 +tla2_vessel_005,39,yantian_chn,20,0,1 +tla2_vessel_005,39,singapore_sgp,17,10,11 +tla2_vessel_005,39,itagual_bra,2,23,25 +tla2_vessel_005,39,santos_bra,14,76,77 +tla2_vessel_005,39,singapore_sgp,17,80,81 +tla2_vessel_005,39,yantian_chn,20,135,137 +tla2_vessel_005,39,shanghai_chn,16,151,153 +tla2_vessel_005,39,qingdao_chn,12,162,163 +tla2_vessel_005,39,shanghai_chn,16,170,172 +tla2_vessel_005,39,yantian_chn,20,178,179 +tla2_vessel_005,39,singapore_sgp,17,187,189 +tla2_vessel_005,39,itagual_bra,2,200,201 +tla2_vessel_005,39,santos_bra,14,249,250 +tla2_vessel_005,39,singapore_sgp,17,252,253 +tla2_vessel_005,39,yantian_chn,20,307,308 +tlp1_vessel_000,40,yantian_chn,20,0,2 +tlp1_vessel_000,40,shanghai_chn,16,12,14 +tlp1_vessel_000,40,qingdao_chn,12,21,22 +tlp1_vessel_000,40,pusan_kor,11,28,30 +tlp1_vessel_000,40,manzanillo_mex,5,34,36 +tlp1_vessel_000,40,sanAntonio_par,13,64,65 +tlp1_vessel_000,40,manzanillo_mex,5,82,83 +tlp1_vessel_000,40,yokohama_jpn,21,107,109 +tlp1_vessel_000,40,shanghai_chn,16,135,137 +tlp1_vessel_000,40,yantian_chn,20,144,145 +tlp1_vessel_000,40,shanghai_chn,16,160,161 +tlp1_vessel_000,40,qingdao_chn,12,169,170 +tlp1_vessel_000,40,pusan_kor,11,179,181 +tlp1_vessel_000,40,manzanillo_mex,5,187,188 +tlp1_vessel_000,40,sanAntonio_par,13,214,216 +tlp1_vessel_000,40,manzanillo_mex,5,233,235 +tlp1_vessel_000,40,yokohama_jpn,21,252,253 +tlp1_vessel_001,41,manzanillo_mex,5,0,1 +tlp1_vessel_001,41,sanAntonio_par,13,35,37 +tlp1_vessel_001,41,manzanillo_mex,5,54,55 +tlp1_vessel_001,41,yokohama_jpn,21,81,83 +tlp1_vessel_001,41,shanghai_chn,16,114,116 +tlp1_vessel_001,41,yantian_chn,20,125,126 +tlp1_vessel_001,41,shanghai_chn,16,138,140 +tlp1_vessel_001,41,qingdao_chn,12,149,151 +tlp1_vessel_001,41,pusan_kor,11,160,161 +tlp1_vessel_001,41,manzanillo_mex,5,167,168 +tlp1_vessel_001,41,sanAntonio_par,13,198,200 +tlp1_vessel_001,41,manzanillo_mex,5,220,221 +tlp1_vessel_001,41,yokohama_jpn,21,245,247 +tlp1_vessel_001,41,shanghai_chn,16,284,285 +tlp1_vessel_002,42,pusan_kor,11,0,2 +tlp1_vessel_002,42,manzanillo_mex,5,7,9 +tlp1_vessel_002,42,sanAntonio_par,13,36,38 +tlp1_vessel_002,42,manzanillo_mex,5,54,56 +tlp1_vessel_002,42,yokohama_jpn,21,76,77 +tlp1_vessel_002,42,shanghai_chn,16,103,104 +tlp1_vessel_002,42,yantian_chn,20,111,113 +tlp1_vessel_002,42,shanghai_chn,16,123,124 +tlp1_vessel_002,42,qingdao_chn,12,131,132 +tlp1_vessel_002,42,pusan_kor,11,139,141 +tlp1_vessel_002,42,manzanillo_mex,5,145,146 +tlp1_vessel_002,42,sanAntonio_par,13,171,172 +tlp1_vessel_002,42,manzanillo_mex,5,185,187 +tlp1_vessel_002,42,yokohama_jpn,21,204,206 +tlp1_vessel_002,42,shanghai_chn,16,234,235 +tlp1_vessel_002,42,yantian_chn,20,242,244 +tlp1_vessel_003,43,manzanillo_mex,5,0,2 +tlp1_vessel_003,43,sanAntonio_par,13,39,40 +tlp1_vessel_003,43,manzanillo_mex,5,59,61 +tlp1_vessel_003,43,yokohama_jpn,21,81,83 +tlp1_vessel_003,43,shanghai_chn,16,119,120 +tlp1_vessel_003,43,yantian_chn,20,126,128 +tlp1_vessel_003,43,shanghai_chn,16,138,140 +tlp1_vessel_003,43,qingdao_chn,12,146,148 +tlp1_vessel_003,43,pusan_kor,11,155,157 +tlp1_vessel_003,43,manzanillo_mex,5,163,165 +tlp1_vessel_003,43,sanAntonio_par,13,200,202 +tlp1_vessel_003,43,manzanillo_mex,5,217,218 +tlp1_vessel_003,43,yokohama_jpn,21,241,243 +tlp1_vessel_003,43,shanghai_chn,16,269,270 +tlp1_vessel_004,44,sanAntonio_par,13,0,2 +tlp1_vessel_004,44,manzanillo_mex,5,21,22 +tlp1_vessel_004,44,yokohama_jpn,21,43,45 +tlp1_vessel_004,44,shanghai_chn,16,76,77 +tlp1_vessel_004,44,yantian_chn,20,85,86 +tlp1_vessel_004,44,shanghai_chn,16,98,99 +tlp1_vessel_004,44,qingdao_chn,12,108,110 +tlp1_vessel_004,44,pusan_kor,11,119,120 +tlp1_vessel_004,44,manzanillo_mex,5,125,127 +tlp1_vessel_004,44,sanAntonio_par,13,161,162 +tlp1_vessel_004,44,manzanillo_mex,5,179,181 +tlp1_vessel_004,44,yokohama_jpn,21,203,204 +tlp1_vessel_004,44,shanghai_chn,16,235,237 +tlp1_vessel_004,44,yantian_chn,20,244,245 +tlp1_vessel_005,45,yokohama_jpn,21,0,1 +tlp1_vessel_005,45,shanghai_chn,16,23,24 +tlp1_vessel_005,45,yantian_chn,20,30,31 +tlp1_vessel_005,45,shanghai_chn,16,42,44 +tlp1_vessel_005,45,qingdao_chn,12,51,52 +tlp1_vessel_005,45,pusan_kor,11,59,61 +tlp1_vessel_005,45,manzanillo_mex,5,66,68 +tlp1_vessel_005,45,sanAntonio_par,13,94,96 +tlp1_vessel_005,45,manzanillo_mex,5,111,113 +tlp1_vessel_005,45,yokohama_jpn,21,129,131 +tlp1_vessel_005,45,shanghai_chn,16,155,157 +tlp1_vessel_005,45,yantian_chn,20,163,164 +tlp1_vessel_005,45,shanghai_chn,16,173,174 +tlp1_vessel_005,45,qingdao_chn,12,182,184 +tlp1_vessel_005,45,pusan_kor,11,191,193 +tlp1_vessel_005,45,manzanillo_mex,5,198,199 +tlp1_vessel_005,45,sanAntonio_par,13,223,224 +tlp1_vessel_005,45,manzanillo_mex,5,240,241 +tlp1_vessel_005,45,yokohama_jpn,21,257,259 diff --git a/tests/data/cim/case_data/dump_folder/vessels.csv b/tests/data/cim/case_data/dump_folder/vessels.csv new file mode 100644 index 000000000..bfa5ecc9c --- /dev/null +++ b/tests/data/cim/case_data/dump_folder/vessels.csv @@ -0,0 +1,47 @@ +index,name,capacity,route_name,route_index,start_port_name,start_port_index,sailing_speed,sailing_speed_noise,parking_duration,parking_noise,period,empty +0,a3s_vessel_000,871,a3s,0,yantian_chn,20,10,2,1,1,67,0 +1,a3s_vessel_001,967,a3s,0,sydney_aus,18,9,2,1,1,75,0 +2,asa_vessel_000,601,asa,1,singapore_sgp,17,8,1,1,1,84,0 +3,asa_vessel_001,493,asa,1,melbourne_aus,6,10,2,1,1,67,0 +4,ate1_vessel_000,5758,ate1,2,newYork_usa,8,9,1,1,1,53,0 +5,ate1_vessel_001,6333,ate1,2,bremerhaven_ger,0,8,1,1,1,58,0 +6,gex1_vessel_000,1033,gex1,3,bremerhaven_ger,0,10,2,1,1,51,0 +7,gex1_vessel_001,1147,gex1,3,montreal_can,7,9,1,1,1,58,0 +8,ktx5_vessel_000,1557,ktx5,4,singapore_sgp,17,8,1,1,1,61,0 +9,ktx5_vessel_001,1275,ktx5,4,yokohama_jpn,21,10,1,1,1,49,0 +10,ll4_vessel_000,6603,ll4,5,bremerhaven_ger,0,9,2,1,1,164,0 +11,ll4_vessel_001,7263,ll4,5,yantian_chn,20,8,2,1,1,182,0 +12,ll4_vessel_002,5943,ll4,5,singapore_sgp,17,10,1,1,1,146,0 +13,ll4_vessel_003,6603,ll4,5,leHavre_fra,3,9,1,1,1,164,0 +14,ll4_vessel_004,7263,ll4,5,pusan_kor,11,8,1,1,1,182,0 +15,ll4_vessel_005,5943,ll4,5,qingdao_chn,12,10,1,1,1,146,0 +16,pcc1_vessel_000,4922,pcc1,6,oakland_usa,9,9,1,1,1,90,0 +17,pcc1_vessel_001,5414,pcc1,6,qingdao_chn,12,8,2,1,1,98,0 +18,pcc1_vessel_002,4430,pcc1,6,princeRupert_can,10,10,1,1,1,79,0 +19,pcc2_vessel_000,2443,pcc2,7,seattle_usa,15,9,2,1,1,95,0 +20,pcc2_vessel_001,2687,pcc2,7,losAngeles_usa,4,8,2,1,1,104,0 +21,pcc2_vessel_002,2199,pcc2,7,shanghai_chn,16,10,2,1,1,84,0 +22,pnw1_vessel_000,2129,pnw1,8,seattle_usa,15,9,1,1,1,87,0 +23,pnw1_vessel_001,2341,pnw1,8,vancouver_can,19,8,1,1,1,97,0 +24,pnw1_vessel_002,1917,pnw1,8,yantian_chn,20,10,1,1,1,78,0 +25,pnw2_vessel_000,897,pnw2,9,pusan_kor,11,9,2,1,1,154,0 +26,pnw2_vessel_001,986,pnw2,9,yantian_chn,20,8,2,1,1,169,0 +27,pnw2_vessel_002,808,pnw2,9,vancouver_can,19,10,1,1,1,136,0 +28,pnw2_vessel_003,897,pnw2,9,pusan_kor,11,9,1,1,1,154,0 +29,pnw2_vessel_004,986,pnw2,9,qingdao_chn,12,8,1,1,1,169,0 +30,saf3_vessel_000,583,saf3,10,yantian_chn,20,10,1,1,1,94,0 +31,saf3_vessel_001,647,saf3,10,qingdao_chn,12,9,1,1,1,105,0 +32,saf3_vessel_002,711,saf3,10,singapore_sgp,17,8,2,1,1,117,0 +33,saf3_vessel_003,583,saf3,10,durban_sau,1,10,2,1,1,94,0 +34,tla2_vessel_000,1185,tla2,11,itagual_bra,2,9,1,1,1,189,0 +35,tla2_vessel_001,1303,tla2,11,santos_bra,14,8,1,1,1,210,0 +36,tla2_vessel_002,1067,tla2,11,singapore_sgp,17,10,2,1,1,167,0 +37,tla2_vessel_003,1185,tla2,11,shanghai_chn,16,9,1,1,1,189,0 +38,tla2_vessel_004,1303,tla2,11,qingdao_chn,12,8,2,1,1,210,0 +39,tla2_vessel_005,1067,tla2,11,yantian_chn,20,10,1,1,1,167,0 +40,tlp1_vessel_000,6332,tlp1,12,yantian_chn,20,9,2,1,1,141,0 +41,tlp1_vessel_001,6965,tlp1,12,manzanillo_mex,5,8,1,1,1,158,0 +42,tlp1_vessel_002,5699,tlp1,12,pusan_kor,11,10,1,1,1,125,0 +43,tlp1_vessel_003,6332,tlp1,12,manzanillo_mex,5,9,2,1,1,141,0 +44,tlp1_vessel_004,6965,tlp1,12,sanAntonio_par,13,8,1,1,1,158,0 +45,tlp1_vessel_005,5699,tlp1,12,yokohama_jpn,21,10,2,1,1,125,0 diff --git a/tests/data/cim/case_data/real_folder_bin/misc.yml b/tests/data/cim/case_data/real_folder_bin/misc.yml new file mode 100644 index 000000000..7b9a713ac --- /dev/null +++ b/tests/data/cim/case_data/real_folder_bin/misc.yml @@ -0,0 +1,7 @@ +max_tick: 224 +container_volume: 1 +past_stop_number: 4 +future_stop_number: 3 +seed: 4096 +dsch_cost_factor: 0.05 +load_cost_factor: 0.05 diff --git a/tests/data/cim/case_data/real_folder_bin/orders.bin b/tests/data/cim/case_data/real_folder_bin/orders.bin new file mode 100644 index 000000000..d087fd8a5 Binary files /dev/null and b/tests/data/cim/case_data/real_folder_bin/orders.bin differ diff --git a/tests/data/cim/case_data/real_folder_bin/ports.csv b/tests/data/cim/case_data/real_folder_bin/ports.csv new file mode 100644 index 000000000..cfb96fcab --- /dev/null +++ b/tests/data/cim/case_data/real_folder_bin/ports.csv @@ -0,0 +1,5 @@ +index,name,capacity,empty,empty_return_buffer,empty_return_buffer_noise,full_return_buffer,full_return_buffer_noise +0,demand_port_001,100000,25000,1,1,1,1 +1,demand_port_002,100000,25000,1,1,1,1 +2,supply_port_001,1000000,25000,1,1,1,1 +3,supply_port_002,100000,25000,1,1,1,1 diff --git a/tests/data/cim/case_data/real_folder_bin/routes.csv b/tests/data/cim/case_data/real_folder_bin/routes.csv new file mode 100644 index 000000000..5c1ca39e4 --- /dev/null +++ b/tests/data/cim/case_data/real_folder_bin/routes.csv @@ -0,0 +1,6 @@ +index,name,port_name,port_index,distance_to_next_port +0,route_001,supply_port_001,2,60 +0,route_001,demand_port_001,0,60 +1,route_002,supply_port_001,2,60 +1,route_002,supply_port_002,3,60 +1,route_002,demand_port_002,1,60 diff --git a/tests/data/cim/case_data/real_folder_bin/stops.bin b/tests/data/cim/case_data/real_folder_bin/stops.bin new file mode 100644 index 000000000..ef8b0c67f Binary files /dev/null and b/tests/data/cim/case_data/real_folder_bin/stops.bin differ diff --git a/tests/data/cim/case_data/real_folder_bin/vessels.csv b/tests/data/cim/case_data/real_folder_bin/vessels.csv new file mode 100644 index 000000000..0507805ba --- /dev/null +++ b/tests/data/cim/case_data/real_folder_bin/vessels.csv @@ -0,0 +1,6 @@ +index,name,capacity,route_name,route_index,start_port_name,start_port_index,sailing_speed,sailing_speed_noise,parking_duration,parking_noise,period,empty +0,rt1_vessel_001,10395,route_001,0,supply_port_001,2,10,2,1,1,14,0 +1,rt1_vessel_002,11550,route_001,0,demand_port_001,0,9,2,1,1,16,0 +2,rt2_vessel_001,25795,route_002,1,supply_port_001,2,8,1,1,1,27,0 +3,rt2_vessel_002,21105,route_002,1,supply_port_002,3,10,2,1,1,21,0 +4,rt2_vessel_003,23450,route_002,1,demand_port_002,1,9,1,1,1,24,0 diff --git a/tests/data/cim/case_data/real_folder_csv/misc.yml b/tests/data/cim/case_data/real_folder_csv/misc.yml new file mode 100644 index 000000000..7b9a713ac --- /dev/null +++ b/tests/data/cim/case_data/real_folder_csv/misc.yml @@ -0,0 +1,7 @@ +max_tick: 224 +container_volume: 1 +past_stop_number: 4 +future_stop_number: 3 +seed: 4096 +dsch_cost_factor: 0.05 +load_cost_factor: 0.05 diff --git a/tests/data/cim/case_data/real_folder_csv/orders.csv b/tests/data/cim/case_data/real_folder_csv/orders.csv new file mode 100644 index 000000000..02a559874 --- /dev/null +++ b/tests/data/cim/case_data/real_folder_csv/orders.csv @@ -0,0 +1,673 @@ +tick,source_port_name,source_port_index,dest_port_name,dest_port_index,quantity +0,demand_port_001,0,supply_port_001,2,445 +0,demand_port_002,1,supply_port_001,2,173 +0,demand_port_002,1,supply_port_002,3,764 +1,demand_port_001,0,supply_port_001,2,536 +1,demand_port_002,1,supply_port_001,2,169 +1,demand_port_002,1,supply_port_002,3,672 +2,demand_port_001,0,supply_port_001,2,579 +2,demand_port_002,1,supply_port_001,2,182 +2,demand_port_002,1,supply_port_002,3,888 +3,demand_port_001,0,supply_port_001,2,558 +3,demand_port_002,1,supply_port_001,2,196 +3,demand_port_002,1,supply_port_002,3,752 +4,demand_port_001,0,supply_port_001,2,487 +4,demand_port_002,1,supply_port_001,2,158 +4,demand_port_002,1,supply_port_002,3,742 +5,demand_port_001,0,supply_port_001,2,589 +5,demand_port_002,1,supply_port_001,2,198 +5,demand_port_002,1,supply_port_002,3,880 +6,demand_port_001,0,supply_port_001,2,499 +6,demand_port_002,1,supply_port_001,2,199 +6,demand_port_002,1,supply_port_002,3,665 +7,demand_port_001,0,supply_port_001,2,556 +7,demand_port_002,1,supply_port_001,2,220 +7,demand_port_002,1,supply_port_002,3,822 +8,demand_port_001,0,supply_port_001,2,429 +8,demand_port_002,1,supply_port_001,2,238 +8,demand_port_002,1,supply_port_002,3,704 +9,demand_port_001,0,supply_port_001,2,533 +9,demand_port_002,1,supply_port_001,2,283 +9,demand_port_002,1,supply_port_002,3,847 +10,demand_port_001,0,supply_port_001,2,542 +10,demand_port_002,1,supply_port_001,2,247 +10,demand_port_002,1,supply_port_002,3,823 +11,demand_port_001,0,supply_port_001,2,509 +11,demand_port_002,1,supply_port_001,2,232 +11,demand_port_002,1,supply_port_002,3,756 +12,demand_port_001,0,supply_port_001,2,653 +12,demand_port_002,1,supply_port_001,2,250 +12,demand_port_002,1,supply_port_002,3,893 +13,demand_port_001,0,supply_port_001,2,455 +13,demand_port_002,1,supply_port_001,2,199 +13,demand_port_002,1,supply_port_002,3,844 +14,demand_port_001,0,supply_port_001,2,524 +14,demand_port_002,1,supply_port_001,2,194 +14,demand_port_002,1,supply_port_002,3,1014 +15,demand_port_001,0,supply_port_001,2,607 +15,demand_port_002,1,supply_port_001,2,232 +15,demand_port_002,1,supply_port_002,3,858 +16,demand_port_001,0,supply_port_001,2,542 +16,demand_port_002,1,supply_port_001,2,233 +16,demand_port_002,1,supply_port_002,3,1087 +17,demand_port_001,0,supply_port_001,2,545 +17,demand_port_002,1,supply_port_001,2,257 +17,demand_port_002,1,supply_port_002,3,924 +18,demand_port_001,0,supply_port_001,2,598 +18,demand_port_002,1,supply_port_001,2,218 +18,demand_port_002,1,supply_port_002,3,962 +19,demand_port_001,0,supply_port_001,2,579 +19,demand_port_002,1,supply_port_001,2,216 +19,demand_port_002,1,supply_port_002,3,874 +20,demand_port_001,0,supply_port_001,2,576 +20,demand_port_002,1,supply_port_001,2,181 +20,demand_port_002,1,supply_port_002,3,777 +21,demand_port_001,0,supply_port_001,2,496 +21,demand_port_002,1,supply_port_001,2,184 +21,demand_port_002,1,supply_port_002,3,765 +22,demand_port_001,0,supply_port_001,2,487 +22,demand_port_002,1,supply_port_001,2,191 +22,demand_port_002,1,supply_port_002,3,968 +23,demand_port_001,0,supply_port_001,2,607 +23,demand_port_002,1,supply_port_001,2,251 +23,demand_port_002,1,supply_port_002,3,866 +24,demand_port_001,0,supply_port_001,2,543 +24,demand_port_002,1,supply_port_001,2,283 +24,demand_port_002,1,supply_port_002,3,1000 +25,demand_port_001,0,supply_port_001,2,723 +25,demand_port_002,1,supply_port_001,2,352 +25,demand_port_002,1,supply_port_002,3,1079 +26,demand_port_001,0,supply_port_001,2,592 +26,demand_port_002,1,supply_port_001,2,218 +26,demand_port_002,1,supply_port_002,3,1168 +27,demand_port_001,0,supply_port_001,2,797 +27,demand_port_002,1,supply_port_001,2,292 +27,demand_port_002,1,supply_port_002,3,1115 +28,demand_port_001,0,supply_port_001,2,812 +28,demand_port_002,1,supply_port_001,2,308 +28,demand_port_002,1,supply_port_002,3,1300 +29,demand_port_001,0,supply_port_001,2,694 +29,demand_port_002,1,supply_port_001,2,366 +29,demand_port_002,1,supply_port_002,3,1180 +30,demand_port_001,0,supply_port_001,2,757 +30,demand_port_002,1,supply_port_001,2,219 +30,demand_port_002,1,supply_port_002,3,1116 +31,demand_port_001,0,supply_port_001,2,794 +31,demand_port_002,1,supply_port_001,2,301 +31,demand_port_002,1,supply_port_002,3,1223 +32,demand_port_001,0,supply_port_001,2,614 +32,demand_port_002,1,supply_port_001,2,314 +32,demand_port_002,1,supply_port_002,3,1049 +33,demand_port_001,0,supply_port_001,2,753 +33,demand_port_002,1,supply_port_001,2,266 +33,demand_port_002,1,supply_port_002,3,1021 +34,demand_port_001,0,supply_port_001,2,578 +34,demand_port_002,1,supply_port_001,2,220 +34,demand_port_002,1,supply_port_002,3,979 +35,demand_port_001,0,supply_port_001,2,527 +35,demand_port_002,1,supply_port_001,2,251 +35,demand_port_002,1,supply_port_002,3,811 +36,demand_port_001,0,supply_port_001,2,521 +36,demand_port_002,1,supply_port_001,2,309 +36,demand_port_002,1,supply_port_002,3,963 +37,demand_port_001,0,supply_port_001,2,522 +37,demand_port_002,1,supply_port_001,2,243 +37,demand_port_002,1,supply_port_002,3,1055 +38,demand_port_001,0,supply_port_001,2,855 +38,demand_port_002,1,supply_port_001,2,333 +38,demand_port_002,1,supply_port_002,3,1199 +39,demand_port_001,0,supply_port_001,2,722 +39,demand_port_002,1,supply_port_001,2,309 +39,demand_port_002,1,supply_port_002,3,1276 +40,demand_port_001,0,supply_port_001,2,823 +40,demand_port_002,1,supply_port_001,2,466 +40,demand_port_002,1,supply_port_002,3,1409 +41,demand_port_001,0,supply_port_001,2,983 +41,demand_port_002,1,supply_port_001,2,369 +41,demand_port_002,1,supply_port_002,3,1591 +42,demand_port_001,0,supply_port_001,2,952 +42,demand_port_002,1,supply_port_001,2,358 +42,demand_port_002,1,supply_port_002,3,1658 +43,demand_port_001,0,supply_port_001,2,895 +43,demand_port_002,1,supply_port_001,2,350 +43,demand_port_002,1,supply_port_002,3,1498 +44,demand_port_001,0,supply_port_001,2,893 +44,demand_port_002,1,supply_port_001,2,328 +44,demand_port_002,1,supply_port_002,3,1644 +45,demand_port_001,0,supply_port_001,2,872 +45,demand_port_002,1,supply_port_001,2,346 +45,demand_port_002,1,supply_port_002,3,1357 +46,demand_port_001,0,supply_port_001,2,762 +46,demand_port_002,1,supply_port_001,2,352 +46,demand_port_002,1,supply_port_002,3,1389 +47,demand_port_001,0,supply_port_001,2,750 +47,demand_port_002,1,supply_port_001,2,284 +47,demand_port_002,1,supply_port_002,3,1012 +48,demand_port_001,0,supply_port_001,2,625 +48,demand_port_002,1,supply_port_001,2,258 +48,demand_port_002,1,supply_port_002,3,1148 +49,demand_port_001,0,supply_port_001,2,566 +49,demand_port_002,1,supply_port_001,2,201 +49,demand_port_002,1,supply_port_002,3,899 +50,demand_port_001,0,supply_port_001,2,665 +50,demand_port_002,1,supply_port_001,2,231 +50,demand_port_002,1,supply_port_002,3,997 +51,demand_port_001,0,supply_port_001,2,713 +51,demand_port_002,1,supply_port_001,2,299 +51,demand_port_002,1,supply_port_002,3,1136 +52,demand_port_001,0,supply_port_001,2,757 +52,demand_port_002,1,supply_port_001,2,318 +52,demand_port_002,1,supply_port_002,3,1204 +53,demand_port_001,0,supply_port_001,2,841 +53,demand_port_002,1,supply_port_001,2,393 +53,demand_port_002,1,supply_port_002,3,1474 +54,demand_port_001,0,supply_port_001,2,1026 +54,demand_port_002,1,supply_port_001,2,471 +54,demand_port_002,1,supply_port_002,3,1597 +55,demand_port_001,0,supply_port_001,2,1057 +55,demand_port_002,1,supply_port_001,2,452 +55,demand_port_002,1,supply_port_002,3,1655 +56,demand_port_001,0,supply_port_001,2,890 +56,demand_port_002,1,supply_port_001,2,409 +56,demand_port_002,1,supply_port_002,3,1674 +57,demand_port_001,0,supply_port_001,2,847 +57,demand_port_002,1,supply_port_001,2,363 +57,demand_port_002,1,supply_port_002,3,1738 +58,demand_port_001,0,supply_port_001,2,934 +58,demand_port_002,1,supply_port_001,2,330 +58,demand_port_002,1,supply_port_002,3,1520 +59,demand_port_001,0,supply_port_001,2,898 +59,demand_port_002,1,supply_port_001,2,376 +59,demand_port_002,1,supply_port_002,3,1624 +60,demand_port_001,0,supply_port_001,2,723 +60,demand_port_002,1,supply_port_001,2,369 +60,demand_port_002,1,supply_port_002,3,1324 +61,demand_port_001,0,supply_port_001,2,728 +61,demand_port_002,1,supply_port_001,2,253 +61,demand_port_002,1,supply_port_002,3,1004 +62,demand_port_001,0,supply_port_001,2,584 +62,demand_port_002,1,supply_port_001,2,226 +62,demand_port_002,1,supply_port_002,3,885 +63,demand_port_001,0,supply_port_001,2,494 +63,demand_port_002,1,supply_port_001,2,204 +63,demand_port_002,1,supply_port_002,3,877 +64,demand_port_001,0,supply_port_001,2,613 +64,demand_port_002,1,supply_port_001,2,220 +64,demand_port_002,1,supply_port_002,3,818 +65,demand_port_001,0,supply_port_001,2,784 +65,demand_port_002,1,supply_port_001,2,268 +65,demand_port_002,1,supply_port_002,3,1285 +66,demand_port_001,0,supply_port_001,2,866 +66,demand_port_002,1,supply_port_001,2,325 +66,demand_port_002,1,supply_port_002,3,1347 +67,demand_port_001,0,supply_port_001,2,788 +67,demand_port_002,1,supply_port_001,2,477 +67,demand_port_002,1,supply_port_002,3,1425 +68,demand_port_001,0,supply_port_001,2,893 +68,demand_port_002,1,supply_port_001,2,399 +68,demand_port_002,1,supply_port_002,3,1380 +69,demand_port_001,0,supply_port_001,2,1018 +69,demand_port_002,1,supply_port_001,2,303 +69,demand_port_002,1,supply_port_002,3,1377 +70,demand_port_001,0,supply_port_001,2,991 +70,demand_port_002,1,supply_port_001,2,300 +70,demand_port_002,1,supply_port_002,3,1411 +71,demand_port_001,0,supply_port_001,2,988 +71,demand_port_002,1,supply_port_001,2,402 +71,demand_port_002,1,supply_port_002,3,1420 +72,demand_port_001,0,supply_port_001,2,944 +72,demand_port_002,1,supply_port_001,2,341 +72,demand_port_002,1,supply_port_002,3,1264 +73,demand_port_001,0,supply_port_001,2,829 +73,demand_port_002,1,supply_port_001,2,439 +73,demand_port_002,1,supply_port_002,3,1379 +74,demand_port_001,0,supply_port_001,2,680 +74,demand_port_002,1,supply_port_001,2,356 +74,demand_port_002,1,supply_port_002,3,1119 +75,demand_port_001,0,supply_port_001,2,788 +75,demand_port_002,1,supply_port_001,2,272 +75,demand_port_002,1,supply_port_002,3,1086 +76,demand_port_001,0,supply_port_001,2,698 +76,demand_port_002,1,supply_port_001,2,231 +76,demand_port_002,1,supply_port_002,3,931 +77,demand_port_001,0,supply_port_001,2,587 +77,demand_port_002,1,supply_port_001,2,169 +77,demand_port_002,1,supply_port_002,3,811 +78,demand_port_001,0,supply_port_001,2,532 +78,demand_port_002,1,supply_port_001,2,182 +78,demand_port_002,1,supply_port_002,3,834 +79,demand_port_001,0,supply_port_001,2,587 +79,demand_port_002,1,supply_port_001,2,268 +79,demand_port_002,1,supply_port_002,3,1031 +80,demand_port_001,0,supply_port_001,2,715 +80,demand_port_002,1,supply_port_001,2,310 +80,demand_port_002,1,supply_port_002,3,1118 +81,demand_port_001,0,supply_port_001,2,661 +81,demand_port_002,1,supply_port_001,2,318 +81,demand_port_002,1,supply_port_002,3,1117 +82,demand_port_001,0,supply_port_001,2,644 +82,demand_port_002,1,supply_port_001,2,294 +82,demand_port_002,1,supply_port_002,3,1156 +83,demand_port_001,0,supply_port_001,2,801 +83,demand_port_002,1,supply_port_001,2,303 +83,demand_port_002,1,supply_port_002,3,1153 +84,demand_port_001,0,supply_port_001,2,760 +84,demand_port_002,1,supply_port_001,2,341 +84,demand_port_002,1,supply_port_002,3,1380 +85,demand_port_001,0,supply_port_001,2,654 +85,demand_port_002,1,supply_port_001,2,298 +85,demand_port_002,1,supply_port_002,3,1214 +86,demand_port_001,0,supply_port_001,2,695 +86,demand_port_002,1,supply_port_001,2,280 +86,demand_port_002,1,supply_port_002,3,1112 +87,demand_port_001,0,supply_port_001,2,717 +87,demand_port_002,1,supply_port_001,2,297 +87,demand_port_002,1,supply_port_002,3,1106 +88,demand_port_001,0,supply_port_001,2,552 +88,demand_port_002,1,supply_port_001,2,206 +88,demand_port_002,1,supply_port_002,3,979 +89,demand_port_001,0,supply_port_001,2,530 +89,demand_port_002,1,supply_port_001,2,304 +89,demand_port_002,1,supply_port_002,3,877 +90,demand_port_001,0,supply_port_001,2,486 +90,demand_port_002,1,supply_port_001,2,262 +90,demand_port_002,1,supply_port_002,3,753 +91,demand_port_001,0,supply_port_001,2,510 +91,demand_port_002,1,supply_port_001,2,171 +91,demand_port_002,1,supply_port_002,3,815 +92,demand_port_001,0,supply_port_001,2,481 +92,demand_port_002,1,supply_port_001,2,182 +92,demand_port_002,1,supply_port_002,3,884 +93,demand_port_001,0,supply_port_001,2,629 +93,demand_port_002,1,supply_port_001,2,250 +93,demand_port_002,1,supply_port_002,3,882 +94,demand_port_001,0,supply_port_001,2,467 +94,demand_port_002,1,supply_port_001,2,228 +94,demand_port_002,1,supply_port_002,3,860 +95,demand_port_001,0,supply_port_001,2,468 +95,demand_port_002,1,supply_port_001,2,256 +95,demand_port_002,1,supply_port_002,3,839 +96,demand_port_001,0,supply_port_001,2,653 +96,demand_port_002,1,supply_port_001,2,257 +96,demand_port_002,1,supply_port_002,3,945 +97,demand_port_001,0,supply_port_001,2,477 +97,demand_port_002,1,supply_port_001,2,192 +97,demand_port_002,1,supply_port_002,3,926 +98,demand_port_001,0,supply_port_001,2,595 +98,demand_port_002,1,supply_port_001,2,235 +98,demand_port_002,1,supply_port_002,3,831 +99,demand_port_001,0,supply_port_001,2,626 +99,demand_port_002,1,supply_port_001,2,241 +99,demand_port_002,1,supply_port_002,3,1004 +100,demand_port_001,0,supply_port_001,2,545 +100,demand_port_002,1,supply_port_001,2,186 +100,demand_port_002,1,supply_port_002,3,761 +101,demand_port_001,0,supply_port_001,2,485 +101,demand_port_002,1,supply_port_001,2,221 +101,demand_port_002,1,supply_port_002,3,895 +102,demand_port_001,0,supply_port_001,2,560 +102,demand_port_002,1,supply_port_001,2,200 +102,demand_port_002,1,supply_port_002,3,961 +103,demand_port_001,0,supply_port_001,2,484 +103,demand_port_002,1,supply_port_001,2,187 +103,demand_port_002,1,supply_port_002,3,804 +104,demand_port_001,0,supply_port_001,2,568 +104,demand_port_002,1,supply_port_001,2,148 +104,demand_port_002,1,supply_port_002,3,757 +105,demand_port_001,0,supply_port_001,2,504 +105,demand_port_002,1,supply_port_001,2,174 +105,demand_port_002,1,supply_port_002,3,710 +106,demand_port_001,0,supply_port_001,2,455 +106,demand_port_002,1,supply_port_001,2,213 +106,demand_port_002,1,supply_port_002,3,827 +107,demand_port_001,0,supply_port_001,2,634 +107,demand_port_002,1,supply_port_001,2,246 +107,demand_port_002,1,supply_port_002,3,792 +108,demand_port_001,0,supply_port_001,2,406 +108,demand_port_002,1,supply_port_001,2,225 +108,demand_port_002,1,supply_port_002,3,728 +109,demand_port_001,0,supply_port_001,2,430 +109,demand_port_002,1,supply_port_001,2,180 +109,demand_port_002,1,supply_port_002,3,818 +110,demand_port_001,0,supply_port_001,2,571 +110,demand_port_002,1,supply_port_001,2,170 +110,demand_port_002,1,supply_port_002,3,830 +111,demand_port_001,0,supply_port_001,2,578 +111,demand_port_002,1,supply_port_001,2,201 +111,demand_port_002,1,supply_port_002,3,914 +112,demand_port_001,0,supply_port_001,2,525 +112,demand_port_002,1,supply_port_001,2,243 +112,demand_port_002,1,supply_port_002,3,870 +113,demand_port_001,0,supply_port_001,2,541 +113,demand_port_002,1,supply_port_001,2,227 +113,demand_port_002,1,supply_port_002,3,867 +114,demand_port_001,0,supply_port_001,2,497 +114,demand_port_002,1,supply_port_001,2,245 +114,demand_port_002,1,supply_port_002,3,825 +115,demand_port_001,0,supply_port_001,2,554 +115,demand_port_002,1,supply_port_001,2,181 +115,demand_port_002,1,supply_port_002,3,718 +116,demand_port_001,0,supply_port_001,2,571 +116,demand_port_002,1,supply_port_001,2,232 +116,demand_port_002,1,supply_port_002,3,840 +117,demand_port_001,0,supply_port_001,2,537 +117,demand_port_002,1,supply_port_001,2,167 +117,demand_port_002,1,supply_port_002,3,793 +118,demand_port_001,0,supply_port_001,2,532 +118,demand_port_002,1,supply_port_001,2,192 +118,demand_port_002,1,supply_port_002,3,892 +119,demand_port_001,0,supply_port_001,2,494 +119,demand_port_002,1,supply_port_001,2,195 +119,demand_port_002,1,supply_port_002,3,713 +120,demand_port_001,0,supply_port_001,2,519 +120,demand_port_002,1,supply_port_001,2,200 +120,demand_port_002,1,supply_port_002,3,828 +121,demand_port_001,0,supply_port_001,2,439 +121,demand_port_002,1,supply_port_001,2,210 +121,demand_port_002,1,supply_port_002,3,861 +122,demand_port_001,0,supply_port_001,2,640 +122,demand_port_002,1,supply_port_001,2,222 +122,demand_port_002,1,supply_port_002,3,911 +123,demand_port_001,0,supply_port_001,2,545 +123,demand_port_002,1,supply_port_001,2,237 +123,demand_port_002,1,supply_port_002,3,976 +124,demand_port_001,0,supply_port_001,2,497 +124,demand_port_002,1,supply_port_001,2,207 +124,demand_port_002,1,supply_port_002,3,825 +125,demand_port_001,0,supply_port_001,2,578 +125,demand_port_002,1,supply_port_001,2,284 +125,demand_port_002,1,supply_port_002,3,958 +126,demand_port_001,0,supply_port_001,2,663 +126,demand_port_002,1,supply_port_001,2,221 +126,demand_port_002,1,supply_port_002,3,976 +127,demand_port_001,0,supply_port_001,2,490 +127,demand_port_002,1,supply_port_001,2,233 +127,demand_port_002,1,supply_port_002,3,857 +128,demand_port_001,0,supply_port_001,2,706 +128,demand_port_002,1,supply_port_001,2,194 +128,demand_port_002,1,supply_port_002,3,941 +129,demand_port_001,0,supply_port_001,2,592 +129,demand_port_002,1,supply_port_001,2,328 +129,demand_port_002,1,supply_port_002,3,1016 +130,demand_port_001,0,supply_port_001,2,605 +130,demand_port_002,1,supply_port_001,2,214 +130,demand_port_002,1,supply_port_002,3,766 +131,demand_port_001,0,supply_port_001,2,555 +131,demand_port_002,1,supply_port_001,2,290 +131,demand_port_002,1,supply_port_002,3,869 +132,demand_port_001,0,supply_port_001,2,474 +132,demand_port_002,1,supply_port_001,2,252 +132,demand_port_002,1,supply_port_002,3,864 +133,demand_port_001,0,supply_port_001,2,416 +133,demand_port_002,1,supply_port_001,2,205 +133,demand_port_002,1,supply_port_002,3,772 +134,demand_port_001,0,supply_port_001,2,637 +134,demand_port_002,1,supply_port_001,2,252 +134,demand_port_002,1,supply_port_002,3,890 +135,demand_port_001,0,supply_port_001,2,566 +135,demand_port_002,1,supply_port_001,2,248 +135,demand_port_002,1,supply_port_002,3,768 +136,demand_port_001,0,supply_port_001,2,674 +136,demand_port_002,1,supply_port_001,2,250 +136,demand_port_002,1,supply_port_002,3,990 +137,demand_port_001,0,supply_port_001,2,752 +137,demand_port_002,1,supply_port_001,2,298 +137,demand_port_002,1,supply_port_002,3,1158 +138,demand_port_001,0,supply_port_001,2,751 +138,demand_port_002,1,supply_port_001,2,344 +138,demand_port_002,1,supply_port_002,3,1223 +139,demand_port_001,0,supply_port_001,2,750 +139,demand_port_002,1,supply_port_001,2,242 +139,demand_port_002,1,supply_port_002,3,1149 +140,demand_port_001,0,supply_port_001,2,729 +140,demand_port_002,1,supply_port_001,2,337 +140,demand_port_002,1,supply_port_002,3,1231 +141,demand_port_001,0,supply_port_001,2,856 +141,demand_port_002,1,supply_port_001,2,261 +141,demand_port_002,1,supply_port_002,3,1206 +142,demand_port_001,0,supply_port_001,2,850 +142,demand_port_002,1,supply_port_001,2,313 +142,demand_port_002,1,supply_port_002,3,1191 +143,demand_port_001,0,supply_port_001,2,856 +143,demand_port_002,1,supply_port_001,2,264 +143,demand_port_002,1,supply_port_002,3,1175 +144,demand_port_001,0,supply_port_001,2,621 +144,demand_port_002,1,supply_port_001,2,309 +144,demand_port_002,1,supply_port_002,3,1030 +145,demand_port_001,0,supply_port_001,2,618 +145,demand_port_002,1,supply_port_001,2,254 +145,demand_port_002,1,supply_port_002,3,1229 +146,demand_port_001,0,supply_port_001,2,567 +146,demand_port_002,1,supply_port_001,2,225 +146,demand_port_002,1,supply_port_002,3,1035 +147,demand_port_001,0,supply_port_001,2,516 +147,demand_port_002,1,supply_port_001,2,237 +147,demand_port_002,1,supply_port_002,3,752 +148,demand_port_001,0,supply_port_001,2,567 +148,demand_port_002,1,supply_port_001,2,247 +148,demand_port_002,1,supply_port_002,3,864 +149,demand_port_001,0,supply_port_001,2,722 +149,demand_port_002,1,supply_port_001,2,350 +149,demand_port_002,1,supply_port_002,3,1050 +150,demand_port_001,0,supply_port_001,2,743 +150,demand_port_002,1,supply_port_001,2,287 +150,demand_port_002,1,supply_port_002,3,1115 +151,demand_port_001,0,supply_port_001,2,707 +151,demand_port_002,1,supply_port_001,2,314 +151,demand_port_002,1,supply_port_002,3,1328 +152,demand_port_001,0,supply_port_001,2,946 +152,demand_port_002,1,supply_port_001,2,430 +152,demand_port_002,1,supply_port_002,3,1381 +153,demand_port_001,0,supply_port_001,2,784 +153,demand_port_002,1,supply_port_001,2,309 +153,demand_port_002,1,supply_port_002,3,1488 +154,demand_port_001,0,supply_port_001,2,900 +154,demand_port_002,1,supply_port_001,2,440 +154,demand_port_002,1,supply_port_002,3,1554 +155,demand_port_001,0,supply_port_001,2,914 +155,demand_port_002,1,supply_port_001,2,377 +155,demand_port_002,1,supply_port_002,3,1653 +156,demand_port_001,0,supply_port_001,2,825 +156,demand_port_002,1,supply_port_001,2,355 +156,demand_port_002,1,supply_port_002,3,1434 +157,demand_port_001,0,supply_port_001,2,777 +157,demand_port_002,1,supply_port_001,2,299 +157,demand_port_002,1,supply_port_002,3,1349 +158,demand_port_001,0,supply_port_001,2,959 +158,demand_port_002,1,supply_port_001,2,302 +158,demand_port_002,1,supply_port_002,3,1309 +159,demand_port_001,0,supply_port_001,2,706 +159,demand_port_002,1,supply_port_001,2,273 +159,demand_port_002,1,supply_port_002,3,1102 +160,demand_port_001,0,supply_port_001,2,598 +160,demand_port_002,1,supply_port_001,2,211 +160,demand_port_002,1,supply_port_002,3,1015 +161,demand_port_001,0,supply_port_001,2,447 +161,demand_port_002,1,supply_port_001,2,182 +161,demand_port_002,1,supply_port_002,3,735 +162,demand_port_001,0,supply_port_001,2,548 +162,demand_port_002,1,supply_port_001,2,291 +162,demand_port_002,1,supply_port_002,3,865 +163,demand_port_001,0,supply_port_001,2,635 +163,demand_port_002,1,supply_port_001,2,275 +163,demand_port_002,1,supply_port_002,3,1175 +164,demand_port_001,0,supply_port_001,2,726 +164,demand_port_002,1,supply_port_001,2,375 +164,demand_port_002,1,supply_port_002,3,1390 +165,demand_port_001,0,supply_port_001,2,1066 +165,demand_port_002,1,supply_port_001,2,361 +165,demand_port_002,1,supply_port_002,3,1460 +166,demand_port_001,0,supply_port_001,2,897 +166,demand_port_002,1,supply_port_001,2,297 +166,demand_port_002,1,supply_port_002,3,1550 +167,demand_port_001,0,supply_port_001,2,1079 +167,demand_port_002,1,supply_port_001,2,388 +167,demand_port_002,1,supply_port_002,3,1738 +168,demand_port_001,0,supply_port_001,2,1024 +168,demand_port_002,1,supply_port_001,2,422 +168,demand_port_002,1,supply_port_002,3,1654 +169,demand_port_001,0,supply_port_001,2,996 +169,demand_port_002,1,supply_port_001,2,398 +169,demand_port_002,1,supply_port_002,3,1747 +170,demand_port_001,0,supply_port_001,2,938 +170,demand_port_002,1,supply_port_001,2,427 +170,demand_port_002,1,supply_port_002,3,1681 +171,demand_port_001,0,supply_port_001,2,776 +171,demand_port_002,1,supply_port_001,2,307 +171,demand_port_002,1,supply_port_002,3,1484 +172,demand_port_001,0,supply_port_001,2,889 +172,demand_port_002,1,supply_port_001,2,348 +172,demand_port_002,1,supply_port_002,3,1386 +173,demand_port_001,0,supply_port_001,2,736 +173,demand_port_002,1,supply_port_001,2,255 +173,demand_port_002,1,supply_port_002,3,1028 +174,demand_port_001,0,supply_port_001,2,656 +174,demand_port_002,1,supply_port_001,2,288 +174,demand_port_002,1,supply_port_002,3,990 +175,demand_port_001,0,supply_port_001,2,395 +175,demand_port_002,1,supply_port_001,2,183 +175,demand_port_002,1,supply_port_002,3,746 +176,demand_port_001,0,supply_port_001,2,596 +176,demand_port_002,1,supply_port_001,2,218 +176,demand_port_002,1,supply_port_002,3,852 +177,demand_port_001,0,supply_port_001,2,724 +177,demand_port_002,1,supply_port_001,2,309 +177,demand_port_002,1,supply_port_002,3,1057 +178,demand_port_001,0,supply_port_001,2,772 +178,demand_port_002,1,supply_port_001,2,289 +178,demand_port_002,1,supply_port_002,3,1183 +179,demand_port_001,0,supply_port_001,2,990 +179,demand_port_002,1,supply_port_001,2,392 +179,demand_port_002,1,supply_port_002,3,1302 +180,demand_port_001,0,supply_port_001,2,914 +180,demand_port_002,1,supply_port_001,2,313 +180,demand_port_002,1,supply_port_002,3,1508 +181,demand_port_001,0,supply_port_001,2,902 +181,demand_port_002,1,supply_port_001,2,367 +181,demand_port_002,1,supply_port_002,3,1670 +182,demand_port_001,0,supply_port_001,2,997 +182,demand_port_002,1,supply_port_001,2,336 +182,demand_port_002,1,supply_port_002,3,1599 +183,demand_port_001,0,supply_port_001,2,840 +183,demand_port_002,1,supply_port_001,2,394 +183,demand_port_002,1,supply_port_002,3,1547 +184,demand_port_001,0,supply_port_001,2,842 +184,demand_port_002,1,supply_port_001,2,358 +184,demand_port_002,1,supply_port_002,3,1617 +185,demand_port_001,0,supply_port_001,2,721 +185,demand_port_002,1,supply_port_001,2,276 +185,demand_port_002,1,supply_port_002,3,1368 +186,demand_port_001,0,supply_port_001,2,761 +186,demand_port_002,1,supply_port_001,2,361 +186,demand_port_002,1,supply_port_002,3,1110 +187,demand_port_001,0,supply_port_001,2,574 +187,demand_port_002,1,supply_port_001,2,290 +187,demand_port_002,1,supply_port_002,3,1067 +188,demand_port_001,0,supply_port_001,2,524 +188,demand_port_002,1,supply_port_001,2,278 +188,demand_port_002,1,supply_port_002,3,949 +189,demand_port_001,0,supply_port_001,2,552 +189,demand_port_002,1,supply_port_001,2,194 +189,demand_port_002,1,supply_port_002,3,803 +190,demand_port_001,0,supply_port_001,2,562 +190,demand_port_002,1,supply_port_001,2,298 +190,demand_port_002,1,supply_port_002,3,1029 +191,demand_port_001,0,supply_port_001,2,549 +191,demand_port_002,1,supply_port_001,2,279 +191,demand_port_002,1,supply_port_002,3,1033 +192,demand_port_001,0,supply_port_001,2,641 +192,demand_port_002,1,supply_port_001,2,305 +192,demand_port_002,1,supply_port_002,3,1073 +193,demand_port_001,0,supply_port_001,2,755 +193,demand_port_002,1,supply_port_001,2,233 +193,demand_port_002,1,supply_port_002,3,1092 +194,demand_port_001,0,supply_port_001,2,765 +194,demand_port_002,1,supply_port_001,2,313 +194,demand_port_002,1,supply_port_002,3,1261 +195,demand_port_001,0,supply_port_001,2,746 +195,demand_port_002,1,supply_port_001,2,268 +195,demand_port_002,1,supply_port_002,3,1258 +196,demand_port_001,0,supply_port_001,2,767 +196,demand_port_002,1,supply_port_001,2,267 +196,demand_port_002,1,supply_port_002,3,1198 +197,demand_port_001,0,supply_port_001,2,803 +197,demand_port_002,1,supply_port_001,2,278 +197,demand_port_002,1,supply_port_002,3,1218 +198,demand_port_001,0,supply_port_001,2,803 +198,demand_port_002,1,supply_port_001,2,267 +198,demand_port_002,1,supply_port_002,3,1174 +199,demand_port_001,0,supply_port_001,2,641 +199,demand_port_002,1,supply_port_001,2,236 +199,demand_port_002,1,supply_port_002,3,1068 +200,demand_port_001,0,supply_port_001,2,621 +200,demand_port_002,1,supply_port_001,2,278 +200,demand_port_002,1,supply_port_002,3,1061 +201,demand_port_001,0,supply_port_001,2,499 +201,demand_port_002,1,supply_port_001,2,254 +201,demand_port_002,1,supply_port_002,3,852 +202,demand_port_001,0,supply_port_001,2,550 +202,demand_port_002,1,supply_port_001,2,218 +202,demand_port_002,1,supply_port_002,3,938 +203,demand_port_001,0,supply_port_001,2,500 +203,demand_port_002,1,supply_port_001,2,208 +203,demand_port_002,1,supply_port_002,3,931 +204,demand_port_001,0,supply_port_001,2,609 +204,demand_port_002,1,supply_port_001,2,189 +204,demand_port_002,1,supply_port_002,3,801 +205,demand_port_001,0,supply_port_001,2,472 +205,demand_port_002,1,supply_port_001,2,201 +205,demand_port_002,1,supply_port_002,3,843 +206,demand_port_001,0,supply_port_001,2,509 +206,demand_port_002,1,supply_port_001,2,185 +206,demand_port_002,1,supply_port_002,3,870 +207,demand_port_001,0,supply_port_001,2,540 +207,demand_port_002,1,supply_port_001,2,213 +207,demand_port_002,1,supply_port_002,3,896 +208,demand_port_001,0,supply_port_001,2,506 +208,demand_port_002,1,supply_port_001,2,290 +208,demand_port_002,1,supply_port_002,3,875 +209,demand_port_001,0,supply_port_001,2,584 +209,demand_port_002,1,supply_port_001,2,215 +209,demand_port_002,1,supply_port_002,3,1115 +210,demand_port_001,0,supply_port_001,2,593 +210,demand_port_002,1,supply_port_001,2,219 +210,demand_port_002,1,supply_port_002,3,1044 +211,demand_port_001,0,supply_port_001,2,494 +211,demand_port_002,1,supply_port_001,2,214 +211,demand_port_002,1,supply_port_002,3,865 +212,demand_port_001,0,supply_port_001,2,466 +212,demand_port_002,1,supply_port_001,2,228 +212,demand_port_002,1,supply_port_002,3,814 +213,demand_port_001,0,supply_port_001,2,539 +213,demand_port_002,1,supply_port_001,2,236 +213,demand_port_002,1,supply_port_002,3,967 +214,demand_port_001,0,supply_port_001,2,626 +214,demand_port_002,1,supply_port_001,2,235 +214,demand_port_002,1,supply_port_002,3,833 +215,demand_port_001,0,supply_port_001,2,476 +215,demand_port_002,1,supply_port_001,2,247 +215,demand_port_002,1,supply_port_002,3,847 +216,demand_port_001,0,supply_port_001,2,466 +216,demand_port_002,1,supply_port_001,2,206 +216,demand_port_002,1,supply_port_002,3,886 +217,demand_port_001,0,supply_port_001,2,459 +217,demand_port_002,1,supply_port_001,2,198 +217,demand_port_002,1,supply_port_002,3,879 +218,demand_port_001,0,supply_port_001,2,521 +218,demand_port_002,1,supply_port_001,2,234 +218,demand_port_002,1,supply_port_002,3,739 +219,demand_port_001,0,supply_port_001,2,519 +219,demand_port_002,1,supply_port_001,2,256 +219,demand_port_002,1,supply_port_002,3,867 +220,demand_port_001,0,supply_port_001,2,482 +220,demand_port_002,1,supply_port_001,2,180 +220,demand_port_002,1,supply_port_002,3,715 +221,demand_port_001,0,supply_port_001,2,538 +221,demand_port_002,1,supply_port_001,2,213 +221,demand_port_002,1,supply_port_002,3,795 +222,demand_port_001,0,supply_port_001,2,451 +222,demand_port_002,1,supply_port_001,2,197 +222,demand_port_002,1,supply_port_002,3,863 +223,demand_port_001,0,supply_port_001,2,445 +223,demand_port_002,1,supply_port_001,2,194 +223,demand_port_002,1,supply_port_002,3,762 diff --git a/tests/data/cim/case_data/real_folder_csv/ports.csv b/tests/data/cim/case_data/real_folder_csv/ports.csv new file mode 100644 index 000000000..cfb96fcab --- /dev/null +++ b/tests/data/cim/case_data/real_folder_csv/ports.csv @@ -0,0 +1,5 @@ +index,name,capacity,empty,empty_return_buffer,empty_return_buffer_noise,full_return_buffer,full_return_buffer_noise +0,demand_port_001,100000,25000,1,1,1,1 +1,demand_port_002,100000,25000,1,1,1,1 +2,supply_port_001,1000000,25000,1,1,1,1 +3,supply_port_002,100000,25000,1,1,1,1 diff --git a/tests/data/cim/case_data/real_folder_csv/routes.csv b/tests/data/cim/case_data/real_folder_csv/routes.csv new file mode 100644 index 000000000..5c1ca39e4 --- /dev/null +++ b/tests/data/cim/case_data/real_folder_csv/routes.csv @@ -0,0 +1,6 @@ +index,name,port_name,port_index,distance_to_next_port +0,route_001,supply_port_001,2,60 +0,route_001,demand_port_001,0,60 +1,route_002,supply_port_001,2,60 +1,route_002,supply_port_002,3,60 +1,route_002,demand_port_002,1,60 diff --git a/tests/data/cim/case_data/real_folder_csv/stops.csv b/tests/data/cim/case_data/real_folder_csv/stops.csv new file mode 100644 index 000000000..ce7e02610 --- /dev/null +++ b/tests/data/cim/case_data/real_folder_csv/stops.csv @@ -0,0 +1,150 @@ +vessel_name,vessel_index,port_name,port_index,arrival_tick,departure_tick +rt1_vessel_001,0,supply_port_001,2,0,2 +rt1_vessel_001,0,demand_port_001,0,8,10 +rt1_vessel_001,0,supply_port_001,2,16,18 +rt1_vessel_001,0,demand_port_001,0,25,27 +rt1_vessel_001,0,supply_port_001,2,35,36 +rt1_vessel_001,0,demand_port_001,0,42,44 +rt1_vessel_001,0,supply_port_001,2,52,53 +rt1_vessel_001,0,demand_port_001,0,59,61 +rt1_vessel_001,0,supply_port_001,2,68,70 +rt1_vessel_001,0,demand_port_001,0,78,79 +rt1_vessel_001,0,supply_port_001,2,87,89 +rt1_vessel_001,0,demand_port_001,0,97,98 +rt1_vessel_001,0,supply_port_001,2,104,106 +rt1_vessel_001,0,demand_port_001,0,112,114 +rt1_vessel_001,0,supply_port_001,2,120,121 +rt1_vessel_001,0,demand_port_001,0,127,128 +rt1_vessel_001,0,supply_port_001,2,134,135 +rt1_vessel_001,0,demand_port_001,0,141,143 +rt1_vessel_001,0,supply_port_001,2,149,151 +rt1_vessel_001,0,demand_port_001,0,157,158 +rt1_vessel_001,0,supply_port_001,2,165,167 +rt1_vessel_001,0,demand_port_001,0,175,176 +rt1_vessel_001,0,supply_port_001,2,183,184 +rt1_vessel_001,0,demand_port_001,0,191,193 +rt1_vessel_001,0,supply_port_001,2,199,200 +rt1_vessel_001,0,demand_port_001,0,207,208 +rt1_vessel_001,0,supply_port_001,2,215,217 +rt1_vessel_001,0,demand_port_001,0,223,224 +rt1_vessel_001,0,supply_port_001,2,231,233 +rt1_vessel_001,0,demand_port_001,0,240,241 +rt1_vessel_001,0,supply_port_001,2,247,248 +rt1_vessel_002,1,demand_port_001,0,0,1 +rt1_vessel_002,1,supply_port_001,2,7,9 +rt1_vessel_002,1,demand_port_001,0,17,19 +rt1_vessel_002,1,supply_port_001,2,27,28 +rt1_vessel_002,1,demand_port_001,0,34,36 +rt1_vessel_002,1,supply_port_001,2,42,43 +rt1_vessel_002,1,demand_port_001,0,49,50 +rt1_vessel_002,1,supply_port_001,2,56,57 +rt1_vessel_002,1,demand_port_001,0,64,65 +rt1_vessel_002,1,supply_port_001,2,73,74 +rt1_vessel_002,1,demand_port_001,0,83,85 +rt1_vessel_002,1,supply_port_001,2,93,94 +rt1_vessel_002,1,demand_port_001,0,101,102 +rt1_vessel_002,1,supply_port_001,2,110,111 +rt1_vessel_002,1,demand_port_001,0,119,120 +rt1_vessel_002,1,supply_port_001,2,126,128 +rt1_vessel_002,1,demand_port_001,0,136,138 +rt1_vessel_002,1,supply_port_001,2,145,146 +rt1_vessel_002,1,demand_port_001,0,153,154 +rt1_vessel_002,1,supply_port_001,2,161,162 +rt1_vessel_002,1,demand_port_001,0,170,172 +rt1_vessel_002,1,supply_port_001,2,179,181 +rt1_vessel_002,1,demand_port_001,0,187,188 +rt1_vessel_002,1,supply_port_001,2,196,197 +rt1_vessel_002,1,demand_port_001,0,203,204 +rt1_vessel_002,1,supply_port_001,2,211,212 +rt1_vessel_002,1,demand_port_001,0,219,220 +rt1_vessel_002,1,supply_port_001,2,228,229 +rt1_vessel_002,1,demand_port_001,0,236,238 +rt1_vessel_002,1,supply_port_001,2,245,247 +rt2_vessel_001,2,supply_port_001,2,0,2 +rt2_vessel_001,2,supply_port_002,3,10,12 +rt2_vessel_001,2,demand_port_002,1,19,20 +rt2_vessel_001,2,supply_port_001,2,28,29 +rt2_vessel_001,2,supply_port_002,3,37,39 +rt2_vessel_001,2,demand_port_002,1,48,49 +rt2_vessel_001,2,supply_port_001,2,57,58 +rt2_vessel_001,2,supply_port_002,3,66,68 +rt2_vessel_001,2,demand_port_002,1,76,77 +rt2_vessel_001,2,supply_port_001,2,85,87 +rt2_vessel_001,2,supply_port_002,3,95,96 +rt2_vessel_001,2,demand_port_002,1,104,106 +rt2_vessel_001,2,supply_port_001,2,114,116 +rt2_vessel_001,2,supply_port_002,3,124,125 +rt2_vessel_001,2,demand_port_002,1,133,135 +rt2_vessel_001,2,supply_port_001,2,143,145 +rt2_vessel_001,2,supply_port_002,3,154,156 +rt2_vessel_001,2,demand_port_002,1,165,166 +rt2_vessel_001,2,supply_port_001,2,174,175 +rt2_vessel_001,2,supply_port_002,3,184,185 +rt2_vessel_001,2,demand_port_002,1,194,196 +rt2_vessel_001,2,supply_port_001,2,204,205 +rt2_vessel_001,2,supply_port_002,3,214,216 +rt2_vessel_001,2,demand_port_002,1,224,226 +rt2_vessel_001,2,supply_port_001,2,233,235 +rt2_vessel_001,2,supply_port_002,3,243,245 +rt2_vessel_001,2,demand_port_002,1,253,254 +rt2_vessel_002,3,supply_port_002,3,0,1 +rt2_vessel_002,3,demand_port_002,1,8,9 +rt2_vessel_002,3,supply_port_001,2,15,17 +rt2_vessel_002,3,supply_port_002,3,23,24 +rt2_vessel_002,3,demand_port_002,1,30,32 +rt2_vessel_002,3,supply_port_001,2,38,40 +rt2_vessel_002,3,supply_port_002,3,46,48 +rt2_vessel_002,3,demand_port_002,1,55,56 +rt2_vessel_002,3,supply_port_001,2,63,65 +rt2_vessel_002,3,supply_port_002,3,71,72 +rt2_vessel_002,3,demand_port_002,1,78,79 +rt2_vessel_002,3,supply_port_001,2,85,87 +rt2_vessel_002,3,supply_port_002,3,93,95 +rt2_vessel_002,3,demand_port_002,1,101,103 +rt2_vessel_002,3,supply_port_001,2,109,110 +rt2_vessel_002,3,supply_port_002,3,116,117 +rt2_vessel_002,3,demand_port_002,1,124,125 +rt2_vessel_002,3,supply_port_001,2,131,133 +rt2_vessel_002,3,supply_port_002,3,139,141 +rt2_vessel_002,3,demand_port_002,1,147,149 +rt2_vessel_002,3,supply_port_001,2,155,156 +rt2_vessel_002,3,supply_port_002,3,162,164 +rt2_vessel_002,3,demand_port_002,1,170,172 +rt2_vessel_002,3,supply_port_001,2,178,180 +rt2_vessel_002,3,supply_port_002,3,187,189 +rt2_vessel_002,3,demand_port_002,1,195,196 +rt2_vessel_002,3,supply_port_001,2,202,203 +rt2_vessel_002,3,supply_port_002,3,211,213 +rt2_vessel_002,3,demand_port_002,1,219,220 +rt2_vessel_002,3,supply_port_001,2,227,229 +rt2_vessel_002,3,supply_port_002,3,237,238 +rt2_vessel_002,3,demand_port_002,1,244,246 +rt2_vessel_003,4,demand_port_002,1,0,1 +rt2_vessel_003,4,supply_port_001,2,8,9 +rt2_vessel_003,4,supply_port_002,3,16,17 +rt2_vessel_003,4,demand_port_002,1,24,25 +rt2_vessel_003,4,supply_port_001,2,33,34 +rt2_vessel_003,4,supply_port_002,3,42,43 +rt2_vessel_003,4,demand_port_002,1,50,51 +rt2_vessel_003,4,supply_port_001,2,58,59 +rt2_vessel_003,4,supply_port_002,3,67,68 +rt2_vessel_003,4,demand_port_002,1,75,77 +rt2_vessel_003,4,supply_port_001,2,85,87 +rt2_vessel_003,4,supply_port_002,3,94,96 +rt2_vessel_003,4,demand_port_002,1,103,104 +rt2_vessel_003,4,supply_port_001,2,112,114 +rt2_vessel_003,4,supply_port_002,3,121,122 +rt2_vessel_003,4,demand_port_002,1,129,131 +rt2_vessel_003,4,supply_port_001,2,138,139 +rt2_vessel_003,4,supply_port_002,3,146,147 +rt2_vessel_003,4,demand_port_002,1,155,157 +rt2_vessel_003,4,supply_port_001,2,164,166 +rt2_vessel_003,4,supply_port_002,3,173,174 +rt2_vessel_003,4,demand_port_002,1,181,182 +rt2_vessel_003,4,supply_port_001,2,190,192 +rt2_vessel_003,4,supply_port_002,3,200,202 +rt2_vessel_003,4,demand_port_002,1,209,210 +rt2_vessel_003,4,supply_port_001,2,218,219 +rt2_vessel_003,4,supply_port_002,3,226,227 +rt2_vessel_003,4,demand_port_002,1,234,236 +rt2_vessel_003,4,supply_port_001,2,244,246 diff --git a/tests/data/cim/case_data/real_folder_csv/vessels.csv b/tests/data/cim/case_data/real_folder_csv/vessels.csv new file mode 100644 index 000000000..0507805ba --- /dev/null +++ b/tests/data/cim/case_data/real_folder_csv/vessels.csv @@ -0,0 +1,6 @@ +index,name,capacity,route_name,route_index,start_port_name,start_port_index,sailing_speed,sailing_speed_noise,parking_duration,parking_noise,period,empty +0,rt1_vessel_001,10395,route_001,0,supply_port_001,2,10,2,1,1,14,0 +1,rt1_vessel_002,11550,route_001,0,demand_port_001,0,9,2,1,1,16,0 +2,rt2_vessel_001,25795,route_002,1,supply_port_001,2,8,1,1,1,27,0 +3,rt2_vessel_002,21105,route_002,1,supply_port_002,3,10,2,1,1,21,0 +4,rt2_vessel_003,23450,route_002,1,demand_port_002,1,9,1,1,1,24,0 diff --git a/tests/utils.py b/tests/utils.py index 63d0142e5..5e76a594e 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -7,6 +7,7 @@ backends_to_test = ["static", "dynamic"] + def next_step(eb: EventBuffer, be: AbsBusinessEngine, tick: int): if tick > 0: # lets post process last tick first before start a new tick @@ -38,3 +39,30 @@ def be_run_to_end(eb, be): while not is_done: is_done = next_step(eb, be, tick) tick += 1 + + +def compare_list(list1: list, list2: list) -> bool: + return len(list1) == len(list2) and all(val1 == val2 for val1, val2 in zip(list1, list2)) + + +def compare_dictionary(dict1: dict, dict2: dict) -> bool: + keys1 = sorted(list(dict1.keys())) + keys2 = sorted(list(dict2.keys())) + if not compare_list(keys1, keys2): + return False + + for key in keys1: + value1 = dict1[key] + value2 = dict2[key] + if type(value1) != type(value2): + return False + if type(value1) == dict: + if not compare_dictionary(value1, value2): + return False + elif type(value1) == list: + if not compare_list(value1, value2): + return False + else: + if value1 != value2: + return False + return True