scieee AI-readable full text Open interactive document viewer

CLInstGen - An Instance Generation Framework for Two-Tier City Logistics

Lange, Julia

Abstract

CLInstGen is an instance generation framework creating data on planning problems in two-tier city logistics for academic use. The instance generation is based on a selected urban region, may include the design of road- or rail-based transportation services and can be customized with fifteen general instance parameters.

Full text

CLInstGen Manual CLInstGen is an instance generation framework creating data on planning problems in two-tier city logistics for academic use. The instance generation is based on a selected urban region, may include the design of roador rail-based transportation services and can be customized with fifteen general instance parameters. creator: Julia Lange (RPTU Kaiserslautern-Landau, Germany) contributor: Johannes Gückel (Catholic University of Eichstätt-Ingolstadt, Germany) latest version: August 2025 license: Creative Commons Attribution 4.0 International doi: 10.5281/zenodo.17307312 cite as: Lange, J. (2025). CLInstGen - An Instance Generation Framework for Two-Tier City Logistics. Zenodo. https://doi.org/10.5281/zenodo.17307312 Getting Started Check and install the required python packages, which can be found in this manual and in the requirements.txt . openrouteservice.org by HeiGIT: The code uses openrouteservice to determine addresses, coordinates, distances and travel times whenever needed. Every user needs a license key, which is available for free for academic use. The key needs to be entered at the top of main_instance_generation.py openrouteservice_api_key: str = 'your openrouteservice API key' Note that the code is not runnable without the license key! It may be necessary in increase your quota (send request via mail) to request larger distance matrices. Running main_instance_generation.py generates instances. First, specify your personal license key for openrouteservice (see above). First Output: The initially given parameter values generate one instance with 10 customer locations and demands, 10 road-based satellites, a mix of three demand types, an inner-city access time window (TW) width of 120 minutes, medium TW widths and short waiting allowed for the district Oststadt of Karlsruhe, Germany. Its ID is 100. The instance is stored as a json-file in directory CL_instances/ka_east/ . 1 / 21 Distribution Centers Satellites Customer Locations Demands Transportation Network location (address, coordinates) location (address, coordinates) location (address, coordinates) origin distance matrix including both tiers  type maximum waiting time destination services with given routes, starting TW and fixed cost  region  direction tours with given routes and fixed cost maximum waiting time  handover time  City Logistics Context We refer to consolidation-based two-tier urban freight transportation, where a transport network is used to ship commodities between different locations in the system. Distribution centers are located in the outskirts of the city. Freight is transfered between the inter-regional and regional transport network there. Customer locations are located in dense urban areas. Satellite locations (short: satellites) with limited capacity represent (de-)consolidation points for freight within the dense area, where commodities are handed over between different types of delivery vehicles. The transport network defines the transportation options between these locations, which can be separated into two tiers (also named layers, echelons or levels). The first (also named outer) tier connects distribution centers and satellites, while the second (also named inner) tier connects satellites and customer locations. On each tier, a homogeneous fleet of vehicles with a certain capacity is operated. The transport network is represented in two ways, namely, (i) transportation links with given distances and vehicle capacities and (ii) transportation services with routes, capacities and fixed cost. Concerning the latter, services are operated by urban vehicles on the first tier and tours are operated by city freighters on the second tier. Demands with certain volume and time restrictions may flow inbound (e2c, from the external zone to a customer location), outbound (c2e, from a customer location to the external zone) and within the city (c2c, between two customer locations). They are not preassigned to distribution centers or satellites to handle them. We assume that deliveries and pickups at customer locations in the dense inner-city area are restricted by an inner-city access TW, in which all demand TWs are allocated. Nonetheless, travel activities on the first and second tier may take place before and after the inner-city access TW during the planning time horizon. The following table summarizes the entities and their corresponding parameters involved in the benchmark instances: 2 / 21 Distribution Centers Satellites Customer Locations Demands Transportation Network  maximum number of urban vehicles  volume portion of an urban vehicle   maximum number of city freighters  volume portion of a city freighter  size of parking area pickup TW (for c2e and c2c)   maximum demand volume per time period  delivery TW (for e2c and c2c)   maximum frequency (for rail satellites only) availability time (for e2c)     travel time to/from distribution centers  Functionality of the key method generate_set_of_instances() Input External Parameters / Method Arguments: The file instance_characteristics.txt provides full lists of abbreviations for some of the following instance characteristics and instance generation parameters. New settings can be added and parameter values can be changed there. base_data_name (str): base data name of the urban region (city) to consider This string needs to match all relevant data files in the data directory (see below). built-in: ka_east (district Oststadt of Karlsruhe), ... openrouteservice_key (str): your individual license key for openrouteservice (automatically added) 3 / 21 start_id (int): identifier of the first instance Every instance gets an integer identifier. For instance sets, start_id is the smallest integer and identifiers are stepwise incremented. The corresponding identifier is used as the random seed for each instance. num_of_cust_list (list): list of int, numbers of customers cust_demand_factors (list): list of int, setting number of demands in relation to number of customers examples: 1: number of customers = number of demands 2: 2*number of customers = number of demands demand_portions_list (list): list of abbrev codes, demand portions values: "dpRE" for 65% e2c, 20% c2e and 15% c2c, "dpIO" for 80% e2c and 20% c2e, "dpOIN" for e2c only, "dpEQ" for 33.3% e2c, 33.3% c2e and 33.3% c2c, "dpHA" for 50% e2c and 50% c2e sat_type_list (list, optional): list of abbrev codes, satellite types, default = 'stRO' values: "stRO" for road-based or "stRA" for rail-based satellites sat_density_list (list, optional): list of abbrev codes, satellite densities, default = 'sdAG' values: "sdMI" for minimal (one satellite per region), "sdHA" for halved (uniformly distributed over regions), "sdAG" for as given (takes all given satellites) tour_type_list (list, optional): list of abbrev codes, tour types, default = 'ttBI' values: "ttBI" for bike, "ttCA" for car, defines travel speed and road usage options of city freighters instance_density_list (list, optional): list of abbrev codes, spread of demand TWs, default = 'idME' values: "idLO" for loose, "idME" for medium, "idHI" for high, see determination rule below Note that instance density is ignored, if access TW widths list is used! access_time_window_widths_list (list): list of int, inner-city access TW widths (in min), default = [] Note that if an access TW widths list is given, the instance density parameter will be ignored! tw_width_distr_list (list, optional): list of abbrev codes, TW widths, default = 'twEQ' values: "twEQ" for short/medium/long TWs uniformly distributed, "twSH" for mainly short, "twOSH" for only short, "twOME" for only medium, "twOLO" for only long, "twLO" for mainly long, see determination rule below 4 / 21 Size Abbrev. XS S M L XL Probability of Occurrence 10% 20% 40% 20% 10% Size in cm^3 4,800 10,000 27,000 100,000 300,000 Handover Time in min 1 1 2 2 3 cust_wait_policy_distr_list (list): list of abbrev codes, customer waiting time policy distributions, default = 'cwtEQ' values: "cwtEQ" for no/short/infinite waiting allowed uniformly distributed, "cwtSH" for mainly no waiting allowed, "cwtOSH" for no waiting allowed everywhere, "cwtOME" for short waiting allowed everywhere, "cwtOLO" for infinite waiting allowed everywhere, "cwtLO" for mainly infinite waiting allowed, see determination rule below demand_sizes_code (str): list of abbrev codes, distributions of demand sizes applied, default = 'dsDE' values: "dsDE" for default distribution, "dsAG" for given demand sizes, see details below num_instances_per_type (int): number of instances with the same parameter combination, default = 1 Decisions based on distributions are involved in the instance generation. The number of sample values drawn is specified here. See details below. no_services_and_tours (bool): if True instances without services and tours are generated, default = False no_feasibility_assuring_services_and_tours (bool): if True instances without feasibility-assuring services and tours are generated, default = False write_instance_data_overview_to_file (bool): if True an instance data summary is written to .txt file, default = False write_instance_data_to_txt (bool): if True raw instance data is written to .txt file, default = False Internal Parameters These parameters are set in internal_parameters.py . They are generally adaptable, but this may require changes in the instance generation framework. Demand types: e2c (external zone to customer, inbound), c2e (customer to external zone, outbound), c2c (customer to customer) Default demand sizes as well as their distribution and handover times The sizes are based on the real-world parcel categories of leading European logistics service providers. Vehicle sizes and demand volume in proportional vehicle usage 5 / 21 Vehicle Bike Car Truck Tram Capacity in m^3 2 4 11 21 Volume Portion XS 0.0024 0.0012 0.0004 0.0002 Volume Portion S 0.005 0.0025 0.0009 0.0005 Volume Portion M 0.0135 0.0068 0.0025 0.0013 Volume Portion L 0.05 0.025 0.0091 0.0048 Volume Portion XL 0.15 0.075 0.0273 0.0143 The vehicle capacities of bike, car and truck are based on real-world delivery vehicles of leading European logistics service providers. For the integration of public transport, we refer to the scenario of adding one tram compartment to a regular passenger tram as given in several pilot projects. Demand TW Multipliers The widths of the demand TWs are determined based on the given handover times to assure instance feasibility. short TW: short_time_window_multiplier * maximum demand handover time = 5 * 3 min =15 min for default values medium TW: medium_time_window_multiplier * maximum demand handover time = 10 * 3 min = 30 min for default values long TW: inner-city access TW width Note that instances, where TW widths are long for all demands refer to problem settings without TWs. Demand-to-Distribution Center Travel Times Artificial travel times of inter-regional transportation between a dummy origin of an e2c or a dummy destination of a c2e demand and all distribution centers are determined as a basis for assignment cost calculations. The dummy locations are randomly generated within a radius (given in km) defined by parameter artificial_demand_location_radius around the city center coordinates of the considered urban region. Customer Maximum Waiting Time Multiplier The values of the maximum waiting times allowed at customer locations are determined based on the given maximum handover time. no waiting time = 0 min short waiting time = short_waiting_time_multiplier * maximal existing demand handover time = (10/3) * 3 min = 10 min for default values long waiting time = planning time horizon 6 / 21 Service Generation For road-based services, the amount of fixed cost for a service is aligned with its temporal flexibility, namely, the width of the starting TW. The fixed cost are measured in a travel time equivalent and determined as total travel time * flexibility multiplier. mid_start_flexibility_multiplier = 2 for services with a medium temporal flexibility (width of starting TW = 0.5 * planning time horizon) max_start_flexibility_multiplier = 3 for services with maximal temporal flexibility (width of starting TW = planning time horizon, no restriction on the starting time) For rail-based services, the width of the starting TW in minutes can be set by the parameter rail_start_time_window_width. Default value is 5 minutes. City Data Information on the urban region (city) to consider has to exist in the ./data/ directory. The framework comes with data for: the district Oststadt of Karlsruhe, Germany (about 21,000 inhabitants and 5 km^2) with base_data_name = "ka_east" the city of Ingolstadt, Germany (about 141,000 inhabitants and 133 km^2) with base_data_name = "ing" the city of Munich, Germany (about 1.5 million inhabitants and 311 km^2) with base_data_name = "mun" The following files exist for each urban region in /data/: base_data_name_city_info.json base_data_name_distributioncenters.json base_data_name_road_satellites.json base_data_name_rail_satellites.json (optional, only provided for Karlsruhe and Munich) base_data_name_rail_satellites_info.json (optional, only provided for Karlsruhe and Munich) base_data_name_customer_locations.json A description of the content of these .json files can be found in Section Data Directory below. Output The main method generates one .json file per instance including a python dictionary holding the following information: {  "locations": {    "dc": {      "id": {        "address": string, 1 2 3 4 5 7 / 21        "coordinates": [longitutde, latitude]      }    }    "sat": {      "id": {        "address": string,        "coordinates": [longitutde, latitude],        "type": string,        "region": int, # if type = road        "maxWaitingTime": int, # in min        "maxNumUrbanVehicle": int,        "maxNumCityFreighter": int,        "sizeOfParkingArea": int, # in m^2, if provided        "maxDemVolumePerTime": int, # in cm^3, if provided        "maxFrequency": int # number of visits per hour, if type = rail      }    }    "cust": {      "id": {        "address": string,        "coordinates": [longitutde, latitude],        "maxWaitingTime": int # in min      }    }  },  "dima": [nested list of float], # loc. sorted acc. to id, in min, 9999.0 if link not existing  "accessTWWidth": int, # in min  "accessTW": [start, end], # in min  "timeHorizon": int, # in min  "demands": [    {      "origin": loc. id, # 0 indicates external zone      "destination": loc. id, # 0 indicates external zone      "direction": int, # 1 = e2c, -1 = c2e, 0 = c2c      "handoverTime": int, # in min      "volumeUrb": float, # between 0 and 1, set to 0 for c2c      "volumeCit": float, # between 0 and 1      "pickupTW": [float, float], # null, if not applicable      "deliveryTW": [float, float], # null, if not applicable      "availabilityTime": int, # set to 0 for c2e and c2c      "traveltimeDC": [float, float, ...] # as many floats as distribution centers, null, if not applicable    },    {}, ...  ],  "tours": [ # second/inner tier transportation    { 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 8 / 21 The name of the .json file indicates the characteristics of the instance using the abbreviations given in instance_characteristics.txt . file name structure:      "stops": [loc. id, loc. id, ...],      "fixedCost": float    },    {}, ...  ],  "services": [ # first/outer tier transportation    {      "stops": [loc. id, loc. id, ...],      "startTW": [int, int],      "fixedCost": float    },    {}, ...  ],  "characteristics": {    "Number of Customers": int,    "Customer Replicate": int,    "Satellite Type": str,    "Satellite Density": str,    "Satellite Density Replicate": int,    "Tour Vehicle": str,    "Instance Density": str, # equals time horizon int, if generation time horizon is used    "Time Horizon": int,    "Access TW": list, # list with start and end    "Access TW Width": int,    "Number of Demands": int,    "Demand Type Distribution": str, # str of tuple of demand type portions    "Demand Size Distribution": str,    "Demand Size Replicate": int,    "TW Widths Distribution": str, # str of tuple of TW widths distribution    "TW Widths Distr. Replicate": int,    "Customer Waiting Policy Distribution": str, # str of tuple of customer waiting policy distribution    "Customer Waiting Policy Replicate": int,    "Number of Tours": int,    "Number of Services": int,    "Neglect Feas.-Assur. Serv. Tours": bool,    "Instance ID": int  } } 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 9 / 21 Step 1: Generate feasibility-assuring tours For every e2c and c2e demand, a trivial tour is added. The trivial tour connects the corresponding customer location with the nearest satellite. Stops list = [satellite, customer location, satellite] For every c2c demand, a trivial tour directly connecting the pickup and the delivery customer location is added. This tours starts and ends at the nearest satellite of the corresponding location. Stops list = [nearest satellite to pickup, pickup customer location, delivery customer location, nearest satellite to delivery] Step 2: Generate random tours A reasonable tour length (number of visited customer locations) is determined as min[(2 x number of customers/number of satellites) ; number of customers] rounded to the nearest integer. For each random tour, a length is drawn from a triangular distribution with left bound = 2, right bound = number of customers and mode = reasonable tour length, a random sequence of customer locations with the given length is determined, the nearest satellites of the first and last customer location are added as start and terminal satellite. Services Services define transportation of the first/outer tier between distribution centers and satellites. Modes of Transport Services can be road-based operated by small trucks or rail-based operated by trams or buses. The existence of train tracks in the urban region is not necessary, but rail-based services are supposed to rely on a given network of public transportation lines. Urban vehicles cannot use arbitrary routes in this case. The service mode is controlled by the parameter sat_type_list. Corresponding data on roadbased and/or rail-based satellites must exist in the data directory (see above). Road-Based Service Generation Scheme Requirement: Every satellite should have sufficiently many inbound and outbound options to assure instance feasibility. Rules and Assumptions: The final set of services includes 2 * number of satellites feasibility-assuring + a variable number of randomly generated services. Services can be cyclic or acyclic (start and end at the same or at different distribution centers). Services with different temporal flexibility, namely, width of the starting TW exist. Higher temporal flexibility results in higher fixed cost. 16 / 21 Fixed cost equal the total travel time of the service * flexibility multiplier. Flexibility multipliers are internal parameters and exist for medium and maximal temporal flexibility. Services visiting 5 or more satellite locations are not considered practically reasonable. The reasonable service length (number of satellites visited) is defined between 1 and 4. Step 1: Generate feasibility-assuring services For every satellite, a trivial inbound and a trivial outbound service are added. The trivial services connect the corresponding satellite with the nearest distribution center. The trivial services get maximal temporal flexibility with a starting TW = [0, planning time horizon]. Stops list (inbound) = [nearest distribution center, satellite] Stops list (outbound) = [satellite, nearest distribution center] Step 2: Generate random services A reasonable service lengths (number of visited satellite locations) are determined as 1, 2, 3 and 4, while it is assured that the number of satellites existing in the instance is not exceeded. Ideas: For each service length, different service routes are determined and used so that every satellite is involved in at least one route. Multiple and frequent departure times common for public transport systems shall be imitated by generating several services with the same route but different starting TWs with equal time difference. For each service length: all permutations of satellites (= routes) with the corresponding length are determined, if number of permutations > 10: permutations with distance > average distance of all permutations are removed to avoid unreasonably long services with regard to travel time At most four random satellites are considered for each service length, while over all service lengths, it is assured that each satellite is considered at least once. For each satellite, one permutation (route) including it is chosen to be added as a service. For all chosen permutations: add the nearest distribution center as start and end randomly choose an indicator for the width of the starting TW and frequency from [XS, S, M, L, XL] XS: starting TW width = time horizon/15, number of starts randomly chosen between 1 and 7 within planning time horizon, flexibility multiplier = 1 S: starting TW width = time horizon/10, number of starts randomly chosen between 1 and 5 within planning time horizon, flexibility multiplier = 1 M: starting TW width = time horizon/5, 2 starts within planning time horizon, flexibility multiplier = 1 L: starting TW width = time horizon/2, 1 start within planning time horizon, flexibility multiplier = 2 XL: starting TW width = time horizon, 1 start within planning time horizon, flexibility 17 / 21 multiplier = 3 Note that if several starts within the planning time horizon are chosen, several services are generated based on the same permutation (route). This behavior effects the final number of services generated and makes it volatile. Rail-Based Service Generation Scheme Requirement: Every satellite should have sufficiently many inbound and outbound options to assure instance feasibility. Rules and Assumptions: The final set of services includes 2 * number of satellites feasibility-assuring + a variable number of randomly generated services. Rail-based services are generated based on the given lines and properties of the satellites (stations) only. Services can be cyclic or acyclic (start and end at the same or at different distribution centers) dependent on the given lines. All services feature the same starting TW width given by the parameter rail_start_time_window_width. Its default value is 5 minutes to allow the services being fitted into an existing tram or bus schedule. Fixed cost equal the total travel time of the service * flexibility multiplier. Flexibility multipliers are internal parameters. For rail-based services only a very small flexibility given by parameter rail_start_time_window_width or the maximal flexibility is used. Step 1: Generate feasibility-assuring services For every satellite, a trivial inbound and a trivial outbound service are added. The trivial services connect the corresponding satellite with a distribution center using the shortest existing lines. The trivial services get maximal temporal flexibility with a starting TW = [0, planning time horizon]. Stops list (inbound) = [distribution center, (potentially other stops,) satellite] Stops list (outbound) = [satellite, (potentially other stops,) distribution center] Step 2: Generate random services For each satellite, one line visiting the satellite is randomly chosen. the maximum possible frequency of the chosen line is determined based on the given satellite properties. the maximum number of services possible according to the maximum possible frequency and the length of the planning time horizon is generated. the width of the starting TW of the services is given by the parameter rail_start_time_window_width. Note that basing the service generation on to the maximum frequencies of the satellites effects the final number of services generated and makes it volatile. 18 / 21 Satellites Satellite locations and properties need to be given as input to the instance generation framework. Most properties of the locations coming by default with the framework are based on the real-world surrounding. However, the following rules are applied to complete the property values. Maximum waiting times are taken from triangular distribution with lower bound = peak = 0 and upper bound = time horizon. are rounded to multiples of 10, when <= 1 hour. are rounded to multiples of 15, when <= 2 hours, are rounded to multiples of 30, when > 2 hours. For road-based satellites the size of the parking area (given in m^2), the maximum demand volume per time period (given in cm^3) and maximum number of urban vehicles / city freighters are aligned. It is assumed that a small truck (urban vehicle) requires 12 m^2 and a cargo bike (city freighter) requires 5 m^2. The maximum demand volume per time period is calculated as (500,000 cm^3 * total maximum number of vehicles) / 2, where 500,000 cm^3 refers to the maximum existing demand volume. For each road-based satellite, a region identifier is given. These identifiers describe the allocation of the satellite locations in the urban area. If the set of satellites is reduced during the instance generation (satellite density set to minimal or halved), it is assured that roughly equally many and at least one satellite for each region is selected. How to ... ? How to add a new urban region? If instances for a new urban region (city) shall be created, you need to decide for a name of this region, which is used as base_data_name in the whole framework. For the new city, the following .json files need to exist in the data/ directory following the exact same structure than the ones given for Karlsruhe (ka_east). base_data_name_city_info.json base_data_name_distributioncenters.json base_data_name_road_satellites.json (optional, dependent on chosen satellite type) base_data_name_rail_satellites.json (optional, dependent on chosen satellite type) base_data_name_rail_satellites_info.json (optional, dependent on chosen satellite type) base_data_name_customer_locations.json base_data_name_customer_locations_withSize.json (optional, if given sizes shall be used) 19 / 21 A description of the content of these .json files can be found in Section Data Directory below. To use the new city to generate instances, insert the chosen base_data_name at the top of main_instance_generation.py . How to add satellite properties? Satellite properties can just be added in the dictionaries of the source files data/base_data_name_road_satellites.json and data/base_data_name_rail_satellites.json . The property dictionaries described in Section Data Directories are transferred to the output files as they are. How to change the values determined as maximum waiting times at customer locations? The concrete values of the maximum waiting times allowed at customer locations are defined in function generate_set_of_customer_waiting_time() in file customer_generator.py . For each customer maximum waiting time policy distribution, a list waiting_times_list is set up holding the max. waiting time values for all customers. Dependent on to which part (segment) of the given distribution tuple the current customer belongs, the cust_wait_time is set. How to change the values and rules used to determine the demand TW widths? The concrete TW width for a demand is defined in function generate_time_window_width() in file demand_generator.py . Based on the given TW widths distribution and a random integer, the time_window_width is set to the value for a short, medium or long TW. Exception: If the extreme case of only long TWs (parameter tw_width_distr_list includes 'twOLO' = (0, 0, 1)) is used, the TW width is set in the function get_single_timewindow_width() in file demand_generator.py . Changes in the width of long TWs must also be made there. How to change the values and rules used to determine a demand availability time? Availability times are determined in function get_random_availability_time() in file demand_generator.py . 20 / 21 How to change the generation scheme of the second/inner-tier tours? The generation of the tours is implemented in the method generate_tours() in file tour_generator.py . How to change the generation scheme of first/outer-tier services? The generation of services is implemented in the file service_generator.py . Road-based services are generated with the function generate_road_services() , while rail-based services are generated with the function generate_rail_services() . Requirements The following python packages are necessary to run the code: numpy~=1.21.5 pandas~=1.4.2 openrouteservice==2.3.3 21 / 21