diff --git a/conflowgen/api/container_dwell_time_distribution_manager.py b/conflowgen/api/container_dwell_time_distribution_manager.py index b3e82579..8c60d54d 100644 --- a/conflowgen/api/container_dwell_time_distribution_manager.py +++ b/conflowgen/api/container_dwell_time_distribution_manager.py @@ -1,11 +1,14 @@ import typing +import datetime +from conflowgen.api import AbstractDistributionManager from conflowgen.data_summaries.data_summaries_cache import DataSummariesCache +from conflowgen.domain_models.data_types.mode_of_transport import ModeOfTransport from conflowgen.domain_models.data_types.storage_requirement import StorageRequirement -from conflowgen.api import AbstractDistributionManager from conflowgen.domain_models.distribution_repositories.container_dwell_time_distribution_repository import \ ContainerDwellTimeDistributionRepository -from conflowgen.domain_models.data_types.mode_of_transport import ModeOfTransport +from conflowgen.application.services.average_container_dwell_time_calculator_service import \ + AverageContainerDwellTimeCalculatorService from conflowgen.tools.continuous_distribution import ContinuousDistribution @@ -21,7 +24,7 @@ def __init__(self): def get_container_dwell_time_distribution( self ) -> typing.Dict[ModeOfTransport, typing.Dict[ - ModeOfTransport, typing.Dict[StorageRequirement, ContinuousDistribution]]]: + ModeOfTransport, typing.Dict[StorageRequirement, ContinuousDistribution]]]: """ Returns: @@ -58,3 +61,20 @@ def set_container_dwell_time_distribution( sanitized_distribution ) DataSummariesCache.reset_cache() + + def get_average_container_dwell_time(self, start_date: datetime.date, end_date: datetime.date) -> float: + """ + Uses :class:`.ModeOfTransportDistributionManager` to calculate the expected average container dwell time + based on the scheduled container flow. + + Args: + start_date: The earliest day to consider for scheduled vehicles + end_date: The latest day to consider for scheduled vehicles + + Returns: + Weighted average of all container dwell times based on inbound and outbound vehicle capacities + """ + return AverageContainerDwellTimeCalculatorService().get_average_container_dwell_time( + start_date=start_date, + end_date=end_date + ) diff --git a/conflowgen/application/services/average_container_dwell_time_calculator_service.py b/conflowgen/application/services/average_container_dwell_time_calculator_service.py new file mode 100644 index 00000000..708420e8 --- /dev/null +++ b/conflowgen/application/services/average_container_dwell_time_calculator_service.py @@ -0,0 +1,46 @@ +import datetime + +from conflowgen.api.container_length_distribution_manager import ContainerLengthDistributionManager +from conflowgen.api.mode_of_transport_distribution_manager import ModeOfTransportDistributionManager +from conflowgen.api.storage_requirement_distribution_manager import StorageRequirementDistributionManager +from conflowgen.application.services.inbound_and_outbound_vehicle_capacity_calculator_service import \ + InboundAndOutboundVehicleCapacityCalculatorService +from conflowgen.domain_models.data_types.container_length import ContainerLength +from conflowgen.domain_models.data_types.mode_of_transport import ModeOfTransport +from conflowgen.domain_models.data_types.storage_requirement import StorageRequirement +from conflowgen.domain_models.distribution_repositories.container_dwell_time_distribution_repository import \ + ContainerDwellTimeDistributionRepository + + +class AverageContainerDwellTimeCalculatorService: + + def get_average_container_dwell_time(self, start_date: datetime.date, end_date: datetime.date) -> float: + inbound_vehicle_capacity = InboundAndOutboundVehicleCapacityCalculatorService.get_inbound_capacity_of_vehicles( + start_date=start_date, + end_date=end_date + ) + mode_of_transport_distribution = ModeOfTransportDistributionManager().get_mode_of_transport_distribution() + container_length_distribution = ContainerLengthDistributionManager().get_container_length_distribution() + container_storage_requirement_distribution = \ + StorageRequirementDistributionManager().get_storage_requirement_distribution() + container_dwell_time_distribution = ContainerDwellTimeDistributionRepository(). \ + get_distributions() + average_container_dwell_time = 0 + total_containers = 0 + for delivering_vehicle_type in ModeOfTransport: + for picking_up_vehicle_type in ModeOfTransport: + for container_length in ContainerLength: + for storage_requirement in StorageRequirement: + num_containers = inbound_vehicle_capacity.containers[delivering_vehicle_type] * \ + mode_of_transport_distribution[delivering_vehicle_type][ + picking_up_vehicle_type] * \ + container_length_distribution[container_length] * \ + container_storage_requirement_distribution[container_length][ + storage_requirement] + total_containers += num_containers + average_container_dwell_time += \ + container_dwell_time_distribution[delivering_vehicle_type][picking_up_vehicle_type][ + storage_requirement].average * num_containers + + average_container_dwell_time /= total_containers + return average_container_dwell_time diff --git a/conflowgen/application/services/inbound_and_outbound_vehicle_capacity_calculator_service.py b/conflowgen/application/services/inbound_and_outbound_vehicle_capacity_calculator_service.py new file mode 100644 index 00000000..38548e10 --- /dev/null +++ b/conflowgen/application/services/inbound_and_outbound_vehicle_capacity_calculator_service.py @@ -0,0 +1,166 @@ +from typing import Dict + +import numpy as np + +from conflowgen.data_summaries.data_summaries_cache import DataSummariesCache +from conflowgen.descriptive_datatypes import ContainerVolumeByVehicleType +from conflowgen.domain_models.data_types.mode_of_transport import ModeOfTransport +from conflowgen.descriptive_datatypes import OutboundUsedAndMaximumCapacity +from conflowgen.domain_models.distribution_repositories.container_length_distribution_repository import \ + ContainerLengthDistributionRepository +from conflowgen.domain_models.distribution_repositories.mode_of_transport_distribution_repository import \ + ModeOfTransportDistributionRepository +from conflowgen.domain_models.factories.fleet_factory import create_arrivals_within_time_range +from conflowgen.domain_models.large_vehicle_schedule import Schedule + + +class InboundAndOutboundVehicleCapacityCalculatorService: + + @staticmethod + @DataSummariesCache.cache_result + def get_truck_capacity_for_export_containers( + inbound_capacity_of_vehicles: Dict[ModeOfTransport, float] + ) -> float: + """ + Get the capacity in TEU which is transported by truck. Currently, during the generation process each import + container is picked up by one truck and for each import container, in the next step one export container is + created. + Thus, this method accounts for both import and export. + """ + truck_capacity = 0 + for vehicle_type in ModeOfTransport.get_scheduled_vehicles(): + number_of_containers_delivered_to_terminal_by_vehicle_type = inbound_capacity_of_vehicles[vehicle_type] + mode_of_transport_distribution_of_vehicle_type = \ + ModeOfTransportDistributionRepository().get_distribution()[vehicle_type] + vehicle_to_truck_fraction = mode_of_transport_distribution_of_vehicle_type[ModeOfTransport.truck] + number_of_containers_to_pick_up_by_truck_from_vehicle_type = \ + number_of_containers_delivered_to_terminal_by_vehicle_type * vehicle_to_truck_fraction + truck_capacity += number_of_containers_to_pick_up_by_truck_from_vehicle_type + return truck_capacity + + @staticmethod + @DataSummariesCache.cache_result + def get_inbound_capacity_of_vehicles(start_date, end_date) -> ContainerVolumeByVehicleType: + """ + For the inbound capacity, first vehicles that adhere to a schedule are considered. Trucks, which are created + depending on the outbound distribution, are created based on the assumptions of the further container flow + generation process. + """ + containers: Dict[ModeOfTransport, float] = { + vehicle_type: 0 + for vehicle_type in ModeOfTransport + } + inbound_capacity_in_teu: Dict[ModeOfTransport, float] = { + vehicle_type: 0 + for vehicle_type in ModeOfTransport + } + + for schedule in Schedule.select(): + arrivals = create_arrivals_within_time_range( + start_date, + schedule.vehicle_arrives_at, + end_date, + schedule.vehicle_arrives_every_k_days, + schedule.vehicle_arrives_at_time + ) + total_capacity_moved_by_vessel = (len(arrivals) # number of vehicles that are planned + * schedule.average_moved_capacity) # TEU capacity of each vehicle + containers[schedule.vehicle_type] += total_capacity_moved_by_vessel / \ + (ContainerLengthDistributionRepository.get_teu_factor() * 20) + inbound_capacity_in_teu[schedule.vehicle_type] += total_capacity_moved_by_vessel + + inbound_capacity_in_teu[ModeOfTransport.truck] = \ + InboundAndOutboundVehicleCapacityCalculatorService.get_truck_capacity_for_export_containers( + inbound_capacity_in_teu + ) + containers[ModeOfTransport.truck] = \ + inbound_capacity_in_teu[ModeOfTransport.truck] / \ + (ContainerLengthDistributionRepository.get_teu_factor() * 20) + + return ContainerVolumeByVehicleType( + containers=containers, + teu=inbound_capacity_in_teu + ) + + @staticmethod + @DataSummariesCache.cache_result + def get_outbound_capacity_of_vehicles(start_date, end_date, transportation_buffer) \ + -> OutboundUsedAndMaximumCapacity: + """ + For the outbound capacity, both the used outbound capacity (estimated) and the maximum outbound capacity is + reported. If a vehicle type reaches the maximum outbound capacity, this means that containers need to be + redistributed to other vehicle types due to a lack of capacity. The capacities are only calculated in TEU, not + in containers. + """ + outbound_used_containers: Dict[ModeOfTransport, float] = { + vehicle_type: 0 + for vehicle_type in ModeOfTransport + } + outbound_maximum_containers: Dict[ModeOfTransport, float] = { + vehicle_type: 0 + for vehicle_type in ModeOfTransport + } + outbound_used_capacity_in_teu: Dict[ModeOfTransport, float] = { + vehicle_type: 0 + for vehicle_type in ModeOfTransport + } + outbound_maximum_capacity_in_teu: Dict[ModeOfTransport, float] = { + vehicle_type: 0 + for vehicle_type in ModeOfTransport + } + + schedule: Schedule + for schedule in Schedule.select(): + assert schedule.average_moved_capacity <= schedule.average_vehicle_capacity, \ + "A vehicle cannot move a larger amount of containers (in TEU) than its capacity, " \ + f"the input data is malformed. Schedule '{schedule.service_name}' of vehicle type " \ + f"{schedule.vehicle_type} has an average moved capacity of {schedule.average_moved_capacity} but an " \ + f"averaged vehicle capacity of {schedule.average_vehicle_capacity}." + + arrivals = create_arrivals_within_time_range( + start_date, + schedule.vehicle_arrives_at, + end_date, + schedule.vehicle_arrives_every_k_days, + schedule.vehicle_arrives_at_time + ) + + # If all container flows are balanced, only the average moved capacity is required + total_average_capacity_moved_by_vessel_in_teu = len(arrivals) * schedule.average_moved_capacity + outbound_used_capacity_in_teu[schedule.vehicle_type] += total_average_capacity_moved_by_vessel_in_teu + outbound_used_containers[schedule.vehicle_type] += total_average_capacity_moved_by_vessel_in_teu / \ + (ContainerLengthDistributionRepository.get_teu_factor() * 20) + + # If there are unbalanced container flows, a vehicle departs with more containers than it delivered + maximum_capacity_of_vehicle_in_teu = min( + schedule.average_moved_capacity * (1 + transportation_buffer), + schedule.average_vehicle_capacity + ) + total_maximum_capacity_moved_by_vessel = len(arrivals) * maximum_capacity_of_vehicle_in_teu + outbound_maximum_capacity_in_teu[schedule.vehicle_type] += total_maximum_capacity_moved_by_vessel + outbound_maximum_containers[schedule.vehicle_type] += total_maximum_capacity_moved_by_vessel / \ + (ContainerLengthDistributionRepository.get_teu_factor() * 20) + + inbound_capacity = InboundAndOutboundVehicleCapacityCalculatorService.\ + get_inbound_capacity_of_vehicles(start_date, end_date) + outbound_used_capacity_in_teu[ModeOfTransport.truck] = \ + InboundAndOutboundVehicleCapacityCalculatorService.get_truck_capacity_for_export_containers( + inbound_capacity.teu + ) + outbound_used_containers[ModeOfTransport.truck] = \ + outbound_used_capacity_in_teu[ModeOfTransport.truck] / \ + (ContainerLengthDistributionRepository.get_teu_factor() * 20) + + outbound_maximum_capacity_in_teu[ModeOfTransport.truck] = np.nan # Trucks can always be added as required + outbound_maximum_containers[ModeOfTransport.truck] = np.nan + + return OutboundUsedAndMaximumCapacity( + used=ContainerVolumeByVehicleType( + containers=outbound_used_containers, + teu=outbound_used_capacity_in_teu + ), + maximum=ContainerVolumeByVehicleType( + containers=outbound_maximum_containers, + teu=outbound_maximum_capacity_in_teu + ) + ) diff --git a/conflowgen/previews/inbound_and_outbound_vehicle_capacity_preview.py b/conflowgen/previews/inbound_and_outbound_vehicle_capacity_preview.py index d56b4d4b..2e0a741a 100644 --- a/conflowgen/previews/inbound_and_outbound_vehicle_capacity_preview.py +++ b/conflowgen/previews/inbound_and_outbound_vehicle_capacity_preview.py @@ -1,7 +1,6 @@ from __future__ import annotations import datetime from typing import Dict -import numpy as np from conflowgen.data_summaries.data_summaries_cache import DataSummariesCache from conflowgen.descriptive_datatypes import OutboundUsedAndMaximumCapacity, ContainerVolumeByVehicleType @@ -10,8 +9,8 @@ from conflowgen.domain_models.data_types.mode_of_transport import ModeOfTransport from conflowgen.domain_models.distribution_repositories.mode_of_transport_distribution_repository import \ ModeOfTransportDistributionRepository -from conflowgen.domain_models.factories.fleet_factory import create_arrivals_within_time_range -from conflowgen.domain_models.large_vehicle_schedule import Schedule +from conflowgen.application.services.inbound_and_outbound_vehicle_capacity_calculator_service import \ + InboundAndOutboundVehicleCapacityCalculatorService class InboundAndOutboundVehicleCapacityPreview(AbstractPreview): @@ -61,15 +60,8 @@ def _get_truck_capacity_for_export_containers( created. Thus, this method accounts for both import and export. """ - truck_capacity = 0 - for vehicle_type in ModeOfTransport.get_scheduled_vehicles(): - number_of_containers_delivered_to_terminal_by_vehicle_type = inbound_capacity_of_vehicles[vehicle_type] - mode_of_transport_distribution_of_vehicle_type = self.mode_of_transport_distribution[vehicle_type] - vehicle_to_truck_fraction = mode_of_transport_distribution_of_vehicle_type[ModeOfTransport.truck] - number_of_containers_to_pick_up_by_truck_from_vehicle_type = \ - number_of_containers_delivered_to_terminal_by_vehicle_type * vehicle_to_truck_fraction - truck_capacity += number_of_containers_to_pick_up_by_truck_from_vehicle_type - return truck_capacity + return InboundAndOutboundVehicleCapacityCalculatorService.\ + get_truck_capacity_for_export_containers(inbound_capacity_of_vehicles) @DataSummariesCache.cache_result def hypothesize_with_mode_of_transport_distribution( @@ -88,31 +80,8 @@ def get_inbound_capacity_of_vehicles(self) -> ContainerVolumeByVehicleType: depending on the outbound distribution, are created based on the assumptions of the further container flow generation process. """ - inbound_capacity_in_teu: Dict[ModeOfTransport, float] = { - vehicle_type: 0 - for vehicle_type in ModeOfTransport - } - - for schedule in Schedule.select(): - arrivals = create_arrivals_within_time_range( - self.start_date, - schedule.vehicle_arrives_at, - self.end_date, - schedule.vehicle_arrives_every_k_days, - schedule.vehicle_arrives_at_time - ) - total_capacity_moved_by_vessel = (len(arrivals) # number of vehicles that are planned - * schedule.average_moved_capacity) # TEU capacity of each vehicle - inbound_capacity_in_teu[schedule.vehicle_type] += total_capacity_moved_by_vessel - - inbound_capacity_in_teu[ModeOfTransport.truck] = self._get_truck_capacity_for_export_containers( - inbound_capacity_in_teu - ) - - return ContainerVolumeByVehicleType( - containers=None, - teu=inbound_capacity_in_teu - ) + return InboundAndOutboundVehicleCapacityCalculatorService.\ + get_inbound_capacity_of_vehicles(self.start_date, self.end_date) @DataSummariesCache.cache_result def get_outbound_capacity_of_vehicles(self) -> OutboundUsedAndMaximumCapacity: @@ -122,57 +91,5 @@ def get_outbound_capacity_of_vehicles(self) -> OutboundUsedAndMaximumCapacity: redistributed to other vehicle types due to a lack of capacity. The capacities are only calculated in TEU, not in containers. """ - outbound_used_capacity_in_teu: Dict[ModeOfTransport, float] = { - vehicle_type: 0 - for vehicle_type in ModeOfTransport - } - outbound_maximum_capacity_in_teu: Dict[ModeOfTransport, float] = { - vehicle_type: 0 - for vehicle_type in ModeOfTransport - } - - schedule: Schedule - for schedule in Schedule.select(): - - assert schedule.average_moved_capacity <= schedule.average_vehicle_capacity, \ - "A vehicle cannot move a larger amount of containers (in TEU) than its capacity, " \ - f"the input data is malformed. Schedule '{schedule.service_name}' of vehicle type " \ - f"{schedule.vehicle_type} has an average moved capacity of {schedule.average_moved_capacity} but an " \ - f"averaged vehicle capacity of {schedule.average_vehicle_capacity}." - - arrivals = create_arrivals_within_time_range( - self.start_date, - schedule.vehicle_arrives_at, - self.end_date, - schedule.vehicle_arrives_every_k_days, - schedule.vehicle_arrives_at_time - ) - - # If all container flows are balanced, only the average moved capacity is required - total_average_capacity_moved_by_vessel_in_teu = len(arrivals) * schedule.average_moved_capacity - outbound_used_capacity_in_teu[schedule.vehicle_type] += total_average_capacity_moved_by_vessel_in_teu - - # If there are unbalanced container flows, a vehicle departs with more containers than it delivered - maximum_capacity_of_vehicle_in_teu = min( - schedule.average_moved_capacity * (1 + self.transportation_buffer), - schedule.average_vehicle_capacity - ) - total_maximum_capacity_moved_by_vessel = len(arrivals) * maximum_capacity_of_vehicle_in_teu - outbound_maximum_capacity_in_teu[schedule.vehicle_type] += total_maximum_capacity_moved_by_vessel - - inbound_capacity = self.get_inbound_capacity_of_vehicles() - outbound_used_capacity_in_teu[ModeOfTransport.truck] = self._get_truck_capacity_for_export_containers( - inbound_capacity.teu - ) - outbound_maximum_capacity_in_teu[ModeOfTransport.truck] = np.nan # Trucks can always be added as required - - return OutboundUsedAndMaximumCapacity( - used=ContainerVolumeByVehicleType( - containers=None, - teu=outbound_used_capacity_in_teu - ), - maximum=ContainerVolumeByVehicleType( - containers=None, - teu=outbound_maximum_capacity_in_teu - ) - ) + return InboundAndOutboundVehicleCapacityCalculatorService.\ + get_outbound_capacity_of_vehicles(self.start_date, self.end_date, self.transportation_buffer) diff --git a/conflowgen/tests/api/test_container_dwell_time_distribution_manager.py b/conflowgen/tests/api/test_container_dwell_time_distribution_manager.py index 467bf053..752a6a7f 100644 --- a/conflowgen/tests/api/test_container_dwell_time_distribution_manager.py +++ b/conflowgen/tests/api/test_container_dwell_time_distribution_manager.py @@ -1,8 +1,23 @@ import unittest import unittest.mock +import datetime -from conflowgen import ContainerDwellTimeDistributionManager +from conflowgen.api.container_dwell_time_distribution_manager import ContainerDwellTimeDistributionManager +from conflowgen.domain_models.distribution_models.container_dwell_time_distribution import \ + ContainerDwellTimeDistribution +from conflowgen.domain_models.distribution_models.storage_requirement_distribution import StorageRequirementDistribution from conflowgen.domain_models.distribution_seeders import container_dwell_time_distribution_seeder +from conflowgen.domain_models.distribution_seeders import storage_requirement_distribution_seeder + +from conflowgen.domain_models.data_types.container_length import ContainerLength +from conflowgen.api.container_length_distribution_manager import ContainerLengthDistributionManager +from conflowgen.domain_models.distribution_models.container_length_distribution import ContainerLengthDistribution +from conflowgen.domain_models.distribution_models.mode_of_transport_distribution import ModeOfTransportDistribution +from conflowgen.domain_models.data_types.mode_of_transport import ModeOfTransport +from conflowgen.domain_models.distribution_repositories.mode_of_transport_distribution_repository import \ + ModeOfTransportDistributionRepository +from conflowgen.domain_models.large_vehicle_schedule import Schedule +from conflowgen.tests.substitute_peewee_database import setup_sqlite_in_memory_db class TestContainerDwellTimeDistributionManager(unittest.TestCase): @@ -10,8 +25,70 @@ class TestContainerDwellTimeDistributionManager(unittest.TestCase): SAMPLE_DISTRIBUTION = container_dwell_time_distribution_seeder.DEFAULT_CONTAINER_DWELL_TIME_DISTRIBUTIONS def setUp(self) -> None: + self.sqlite_db = setup_sqlite_in_memory_db() + self.sqlite_db.create_tables([ + Schedule, + ModeOfTransportDistribution, + ContainerDwellTimeDistribution, + ContainerLengthDistribution, + StorageRequirementDistribution + ]) + self.container_dwell_time_distribution_manager = ContainerDwellTimeDistributionManager() + ModeOfTransportDistributionRepository().set_mode_of_transport_distributions({ + ModeOfTransport.truck: { + ModeOfTransport.truck: 0, + ModeOfTransport.train: 0, + ModeOfTransport.barge: 0, + ModeOfTransport.feeder: 0.5, + ModeOfTransport.deep_sea_vessel: 0.5 + }, + ModeOfTransport.train: { + ModeOfTransport.truck: 0, + ModeOfTransport.train: 0, + ModeOfTransport.barge: 0, + ModeOfTransport.feeder: 0.5, + ModeOfTransport.deep_sea_vessel: 0.5 + }, + ModeOfTransport.barge: { + ModeOfTransport.truck: 0, + ModeOfTransport.train: 0, + ModeOfTransport.barge: 0, + ModeOfTransport.feeder: 0.5, + ModeOfTransport.deep_sea_vessel: 0.5 + }, + ModeOfTransport.feeder: { + ModeOfTransport.truck: 0.2, + ModeOfTransport.train: 0.4, + ModeOfTransport.barge: 0.1, + ModeOfTransport.feeder: 0.15, + ModeOfTransport.deep_sea_vessel: 0.15 + }, + ModeOfTransport.deep_sea_vessel: { + ModeOfTransport.truck: 0.2, + ModeOfTransport.train: 0.4, + ModeOfTransport.barge: 0.1, + ModeOfTransport.feeder: 0.15, + ModeOfTransport.deep_sea_vessel: 0.15 + } + }) + + container_length_manager = ContainerLengthDistributionManager() + container_length_manager.set_container_length_distribution( # Set default container length distribution + { + ContainerLength.other: 0.001, + ContainerLength.twenty_feet: 0.4, + ContainerLength.forty_feet: 0.57, + ContainerLength.forty_five_feet: 0.029 + } + ) + + container_dwell_time_distribution_manager = ContainerDwellTimeDistributionManager() + container_dwell_time_distribution_manager.set_container_dwell_time_distribution(self.SAMPLE_DISTRIBUTION) + + storage_requirement_distribution_seeder.seed() + def test_get_container_dwell_time_distributions(self): with unittest.mock.patch.object( self.container_dwell_time_distribution_manager.container_dwell_time_distribution_repository, @@ -30,3 +107,134 @@ def test_set_container_dwell_time_distributions(self): self.SAMPLE_DISTRIBUTION ) mock_method.assert_called_once_with(self.SAMPLE_DISTRIBUTION) + + def test_get_average_container_dwell_time_base_case(self): + one_week_later = datetime.datetime.now() + datetime.timedelta(weeks=1) + schedule = Schedule.create( + vehicle_type=ModeOfTransport.feeder, + service_name="TestFeederService", + vehicle_arrives_at=one_week_later.date(), + vehicle_arrives_at_time=one_week_later.time(), + average_vehicle_capacity=300, + average_moved_capacity=300, + vehicle_arrives_every_k_days=-1 + ) + schedule.save() + + now = datetime.datetime.now() + average_container_dwell_time = self.container_dwell_time_distribution_manager.get_average_container_dwell_time( + start_date=now.date(), + end_date=(now + datetime.timedelta(weeks=2)).date(), + ) + + print("average_container_dwell_time: ", average_container_dwell_time) + self.assertEqual(average_container_dwell_time, 129.9408) + + def test_get_average_container_dwell_time_1_sd(self): + one_week_later = datetime.datetime.now() + datetime.timedelta(weeks=1) + schedule = Schedule.create( + vehicle_type=ModeOfTransport.feeder, + service_name="TestFeederService", + vehicle_arrives_at=one_week_later.date(), + vehicle_arrives_at_time=one_week_later.time(), + average_vehicle_capacity=300, + average_moved_capacity=300, + vehicle_arrives_every_k_days=-1 + ) + schedule.save() + + # Container dwell time increase + container_dwell_time_increase = 1 + + container_dwell_time_distribution_manager = ContainerDwellTimeDistributionManager() + + original_dwell_time_distributions = container_dwell_time_distribution_manager.\ + get_container_dwell_time_distribution() + + new_container_dwell_time_distributions = {} + + for mode1, mode1_dict in original_dwell_time_distributions.items(): + new_mode1_dict = {} + for mode2, mode2_dict in mode1_dict.items(): + new_mode2_dict = {} + for requirement, distribution in mode2_dict.items(): + sd = distribution.variance ** 0.5 + new_average = distribution.average + sd * container_dwell_time_increase + new_maximum = new_average * 3 # Necessary to avoid average > max + # Create a new dictionary with updated average value + new_distribution_dict = { + "distribution_name": "lognormal", + "average_number_of_hours": new_average, + "variance": distribution.variance, # Keep variance same + "minimum_number_of_hours": distribution.minimum, # Keep minimum same + "maximum_number_of_hours": new_maximum, + } + new_mode2_dict[requirement] = new_distribution_dict + new_mode1_dict[mode2] = new_mode2_dict + new_container_dwell_time_distributions[mode1] = new_mode1_dict + + container_dwell_time_distribution_manager.set_container_dwell_time_distribution( + new_container_dwell_time_distributions) + + now = datetime.datetime.now() + average_container_dwell_time = self.container_dwell_time_distribution_manager.get_average_container_dwell_time( + start_date=now.date(), + end_date=(now + datetime.timedelta(weeks=2)).date(), + ) + + print("average_container_dwell_time: ", average_container_dwell_time) + self.assertEqual(average_container_dwell_time, 207.68489589654993) + + def test_get_average_container_dwell_time_2_sd(self): + one_week_later = datetime.datetime.now() + datetime.timedelta(weeks=1) + schedule = Schedule.create( + vehicle_type=ModeOfTransport.feeder, + service_name="TestFeederService", + vehicle_arrives_at=one_week_later.date(), + vehicle_arrives_at_time=one_week_later.time(), + average_vehicle_capacity=300, + average_moved_capacity=300, + vehicle_arrives_every_k_days=-1 + ) + schedule.save() + + # Container dwell time increase + container_dwell_time_increase = 2 + + container_dwell_time_distribution_manager = ContainerDwellTimeDistributionManager() + + original_dwell_time_distributions = container_dwell_time_distribution_manager.\ + get_container_dwell_time_distribution() + + new_container_dwell_time_distributions = {} + + for mode1, mode1_dict in original_dwell_time_distributions.items(): + new_mode1_dict = {} + for mode2, mode2_dict in mode1_dict.items(): + new_mode2_dict = {} + for requirement, distribution in mode2_dict.items(): + sd = distribution.variance ** 0.5 + new_average = distribution.average + sd * container_dwell_time_increase + new_maximum = new_average * 3 # Necessary to avoid average > max + # Create a new dictionary with updated average value + new_distribution_dict = { + "distribution_name": "lognormal", + "average_number_of_hours": new_average, + "variance": distribution.variance, # Keep variance same + "minimum_number_of_hours": distribution.minimum, # Keep minimum same + "maximum_number_of_hours": new_maximum, + } + new_mode2_dict[requirement] = new_distribution_dict + new_mode1_dict[mode2] = new_mode2_dict + new_container_dwell_time_distributions[mode1] = new_mode1_dict + + container_dwell_time_distribution_manager.set_container_dwell_time_distribution( + new_container_dwell_time_distributions) + + now = datetime.datetime.now() + average_container_dwell_time = self.container_dwell_time_distribution_manager.get_average_container_dwell_time( + start_date=now.date(), + end_date=(now + datetime.timedelta(weeks=2)).date(), + ) + + self.assertEqual(average_container_dwell_time, 285.42899179309984) diff --git a/conflowgen/tests/data_summaries/test_data_summaries_cache.py b/conflowgen/tests/data_summaries/test_data_summaries_cache.py index e088b6ed..fcae2a6a 100644 --- a/conflowgen/tests/data_summaries/test_data_summaries_cache.py +++ b/conflowgen/tests/data_summaries/test_data_summaries_cache.py @@ -136,26 +136,28 @@ def test_with_preview(self): ) preview = self.preview.get_weekly_truck_arrivals(True, True) self.assertEqual(preview, {3: 12, 4: 48}, "Uncached result is incorrect") - self.assertEqual(len(DataSummariesCache.cached_results), 7, "There should be 7 cached results") + self.assertEqual(len(DataSummariesCache.cached_results), 9, "There should be 9 cached results") self.assertTrue(59.999999999999986 in list(DataSummariesCache.cached_results.values()) and {3: 12, 4: 48} in list(DataSummariesCache.cached_results.values()), "Incorrect results cached") # pylint: disable=protected-access - self.assertEqual(DataSummariesCache._hit_counter, {'_get_number_of_trucks_per_week': 1, '_get_total_trucks': 1, - '_get_truck_capacity_for_export_containers': 2, - 'get_inbound_capacity_of_vehicles': 2, - 'get_outbound_capacity_of_vehicles': 1, + self.assertEqual(DataSummariesCache._hit_counter, {'_get_number_of_trucks_per_week': 1, + '_get_total_trucks': 1, + 'get_truck_capacity_for_export_containers': 2, + 'get_inbound_capacity_of_vehicles': 3, + 'get_outbound_capacity_of_vehicles': 2, 'get_weekly_truck_arrivals': 1}, "Incorrect hit counter") preview = self.preview.get_weekly_truck_arrivals(True, True) - self.assertEqual(preview, {3: 12, 4: 48}, "Cached result is incorrect") - self.assertEqual(len(DataSummariesCache.cached_results), 7, "There should be 7 cached results") + self.assertEqual(preview, {3: 12, 4: 48}, "Uncached result is incorrect") + self.assertEqual(len(DataSummariesCache.cached_results), 9, "There should be 9 cached results") self.assertTrue(59.999999999999986 in list(DataSummariesCache.cached_results.values()) and {3: 12, 4: 48} in list(DataSummariesCache.cached_results.values()), "Incorrect results cached") # pylint: disable=protected-access - self.assertEqual(DataSummariesCache._hit_counter, {'_get_number_of_trucks_per_week': 1, '_get_total_trucks': 1, - '_get_truck_capacity_for_export_containers': 2, - 'get_inbound_capacity_of_vehicles': 2, - 'get_outbound_capacity_of_vehicles': 1, + self.assertEqual(DataSummariesCache._hit_counter, {'_get_number_of_trucks_per_week': 1, + '_get_total_trucks': 1, + 'get_truck_capacity_for_export_containers': 2, + 'get_inbound_capacity_of_vehicles': 3, + 'get_outbound_capacity_of_vehicles': 2, 'get_weekly_truck_arrivals': 2}, "Incorrect hit counter") # Only get_weekly_truck_arrivals should be called again as the other functions are cached @@ -173,14 +175,15 @@ def test_with_adjusted_preview(self): ) preview = self.preview.get_weekly_truck_arrivals(True, True) self.assertEqual(preview, {3: 12, 4: 48}, "Uncached result is incorrect") - self.assertEqual(len(DataSummariesCache.cached_results), 7, "There should be 7 cached results") + self.assertEqual(len(DataSummariesCache.cached_results), 9, "There should be 9 cached results") self.assertTrue(59.999999999999986 in list(DataSummariesCache.cached_results.values()) and {3: 12, 4: 48} in list(DataSummariesCache.cached_results.values()), "Incorrect results cached") # pylint: disable=protected-access - self.assertEqual(DataSummariesCache._hit_counter, {'_get_number_of_trucks_per_week': 1, '_get_total_trucks': 1, - '_get_truck_capacity_for_export_containers': 2, - 'get_inbound_capacity_of_vehicles': 2, - 'get_outbound_capacity_of_vehicles': 1, + self.assertEqual(DataSummariesCache._hit_counter, {'_get_number_of_trucks_per_week': 1, + '_get_total_trucks': 1, + 'get_truck_capacity_for_export_containers': 2, + 'get_inbound_capacity_of_vehicles': 3, + 'get_outbound_capacity_of_vehicles': 2, 'get_weekly_truck_arrivals': 1}, "Incorrect hit counter") arrival_distribution = { @@ -197,7 +200,7 @@ def test_with_adjusted_preview(self): ) preview = self.preview.get_weekly_truck_arrivals(True, True) self.assertEqual(preview, {3: 6, 4: 24, 5: 30}, "New result is incorrect") - self.assertEqual(len(DataSummariesCache.cached_results), 7, "There should be 7 cached results, because" + self.assertEqual(len(DataSummariesCache.cached_results), 9, "There should be 9 cached results, because" "the preview was adjusted") self.assertTrue(59.999999999999986 in list(DataSummariesCache.cached_results.values()) and {3: 6, 4: 24, 5: 30} in list(DataSummariesCache.cached_results.values()), @@ -205,11 +208,11 @@ def test_with_adjusted_preview(self): # pylint: disable=protected-access self.assertEqual(DataSummariesCache._hit_counter, {'_get_number_of_trucks_per_week': 1, '_get_total_trucks': 1, - '_get_truck_capacity_for_export_containers': 2, - 'get_inbound_capacity_of_vehicles': 2, - 'get_outbound_capacity_of_vehicles': 1, + 'get_truck_capacity_for_export_containers': 2, + 'get_inbound_capacity_of_vehicles': 3, + 'get_outbound_capacity_of_vehicles': 2, 'get_weekly_truck_arrivals': 1}, "Incorrect hit counter") - # Hit counter should be the same as before, because the preview was adjusted i.e. the cache was reset and then + # Hit counter should be the same as before, because the preview was adjusted i.e. the cache was reset, and then # we re-ran the same functions def test_cache_reset(self): diff --git a/conflowgen/tests/previews/test_container_flow_by_vehicle_type_preview.py b/conflowgen/tests/previews/test_container_flow_by_vehicle_type_preview.py index e6a35125..77fd5011 100644 --- a/conflowgen/tests/previews/test_container_flow_by_vehicle_type_preview.py +++ b/conflowgen/tests/previews/test_container_flow_by_vehicle_type_preview.py @@ -1,7 +1,10 @@ import datetime import unittest +from conflowgen.domain_models.data_types.container_length import ContainerLength +from conflowgen.api.container_length_distribution_manager import ContainerLengthDistributionManager from conflowgen.domain_models.data_types.mode_of_transport import ModeOfTransport +from conflowgen.domain_models.distribution_models.container_length_distribution import ContainerLengthDistribution from conflowgen.domain_models.distribution_models.mode_of_transport_distribution import ModeOfTransportDistribution from conflowgen.domain_models.distribution_repositories.mode_of_transport_distribution_repository import \ ModeOfTransportDistributionRepository @@ -17,7 +20,8 @@ def setUp(self) -> None: self.sqlite_db = setup_sqlite_in_memory_db() self.sqlite_db.create_tables([ Schedule, - ModeOfTransportDistribution + ModeOfTransportDistribution, + ContainerLengthDistribution ]) now = datetime.datetime.now() @@ -59,6 +63,16 @@ def setUp(self) -> None: } }) + container_length_manager = ContainerLengthDistributionManager() + container_length_manager.set_container_length_distribution( # Set default container length distribution + { + ContainerLength.other: 0.001, + ContainerLength.twenty_feet: 0.4, + ContainerLength.forty_feet: 0.57, + ContainerLength.forty_five_feet: 0.029 + } + ) + self.preview = ContainerFlowByVehicleTypePreview( start_date=now.date(), end_date=(now + datetime.timedelta(weeks=2)).date(), diff --git a/conflowgen/tests/previews/test_container_flow_by_vehicle_type_preview_report.py b/conflowgen/tests/previews/test_container_flow_by_vehicle_type_preview_report.py index f65fa47d..3734cf90 100644 --- a/conflowgen/tests/previews/test_container_flow_by_vehicle_type_preview_report.py +++ b/conflowgen/tests/previews/test_container_flow_by_vehicle_type_preview_report.py @@ -1,9 +1,12 @@ import datetime import unittest +from conflowgen.domain_models.data_types.container_length import ContainerLength +from conflowgen.api.container_length_distribution_manager import ContainerLengthDistributionManager from conflowgen.application.models.container_flow_generation_properties import ContainerFlowGenerationProperties from conflowgen.domain_models.data_types.mode_of_transport import ModeOfTransport from conflowgen.domain_models.distribution_models.mode_of_transport_distribution import ModeOfTransportDistribution +from conflowgen.domain_models.distribution_models.container_length_distribution import ContainerLengthDistribution from conflowgen.domain_models.distribution_repositories.mode_of_transport_distribution_repository import \ ModeOfTransportDistributionRepository from conflowgen.domain_models.large_vehicle_schedule import Schedule @@ -19,7 +22,8 @@ def setUp(self) -> None: self.sqlite_db.create_tables([ Schedule, ModeOfTransportDistribution, - ContainerFlowGenerationProperties + ContainerFlowGenerationProperties, + ContainerLengthDistribution ]) ModeOfTransportDistributionRepository().set_mode_of_transport_distributions({ ModeOfTransport.truck: { @@ -58,6 +62,17 @@ def setUp(self) -> None: ModeOfTransport.deep_sea_vessel: 0.15 } }) + + container_length_manager = ContainerLengthDistributionManager() + container_length_manager.set_container_length_distribution( # Set default container length distribution + { + ContainerLength.other: 0.001, + ContainerLength.twenty_feet: 0.4, + ContainerLength.forty_feet: 0.57, + ContainerLength.forty_five_feet: 0.029 + } + ) + now = datetime.datetime.now() ContainerFlowGenerationProperties.create( start_date=now, diff --git a/conflowgen/tests/previews/test_inbound_and_outbound_vehicle_capacity_preview.py b/conflowgen/tests/previews/test_inbound_and_outbound_vehicle_capacity_preview.py index f378e4cc..7e7e340d 100644 --- a/conflowgen/tests/previews/test_inbound_and_outbound_vehicle_capacity_preview.py +++ b/conflowgen/tests/previews/test_inbound_and_outbound_vehicle_capacity_preview.py @@ -3,7 +3,10 @@ import numpy as np +from conflowgen.domain_models.data_types.container_length import ContainerLength +from conflowgen.api.container_length_distribution_manager import ContainerLengthDistributionManager from conflowgen.domain_models.data_types.mode_of_transport import ModeOfTransport +from conflowgen.domain_models.distribution_models.container_length_distribution import ContainerLengthDistribution from conflowgen.domain_models.distribution_models.mode_of_transport_distribution import ModeOfTransportDistribution from conflowgen.domain_models.distribution_repositories.mode_of_transport_distribution_repository import \ ModeOfTransportDistributionRepository @@ -19,7 +22,8 @@ def setUp(self) -> None: self.sqlite_db = setup_sqlite_in_memory_db() self.sqlite_db.create_tables([ Schedule, - ModeOfTransportDistribution + ModeOfTransportDistribution, + ContainerLengthDistribution ]) now = datetime.datetime.now() ModeOfTransportDistributionRepository().set_mode_of_transport_distributions({ @@ -59,6 +63,17 @@ def setUp(self) -> None: ModeOfTransport.deep_sea_vessel: 0.15 } }) + + container_length_manager = ContainerLengthDistributionManager() + container_length_manager.set_container_length_distribution( # Set default container length distribution + { + ContainerLength.other: 0.001, + ContainerLength.twenty_feet: 0.4, + ContainerLength.forty_feet: 0.57, + ContainerLength.forty_five_feet: 0.029 + } + ) + self.preview = InboundAndOutboundVehicleCapacityPreview( start_date=now.date(), end_date=(now + datetime.timedelta(weeks=2)).date(), diff --git a/conflowgen/tests/previews/test_inbound_and_outbound_vehicle_capacity_preview_report.py b/conflowgen/tests/previews/test_inbound_and_outbound_vehicle_capacity_preview_report.py index 8008d18b..0148dbaf 100644 --- a/conflowgen/tests/previews/test_inbound_and_outbound_vehicle_capacity_preview_report.py +++ b/conflowgen/tests/previews/test_inbound_and_outbound_vehicle_capacity_preview_report.py @@ -1,7 +1,10 @@ import datetime +from conflowgen.domain_models.data_types.container_length import ContainerLength +from conflowgen.api.container_length_distribution_manager import ContainerLengthDistributionManager from conflowgen.application.models.container_flow_generation_properties import ContainerFlowGenerationProperties from conflowgen.domain_models.data_types.mode_of_transport import ModeOfTransport +from conflowgen.domain_models.distribution_models.container_length_distribution import ContainerLengthDistribution from conflowgen.domain_models.distribution_models.mode_of_transport_distribution import ModeOfTransportDistribution from conflowgen.domain_models.distribution_repositories.mode_of_transport_distribution_repository import \ ModeOfTransportDistributionRepository @@ -19,7 +22,8 @@ def setUp(self) -> None: self.sqlite_db.create_tables([ Schedule, ModeOfTransportDistribution, - ContainerFlowGenerationProperties + ContainerFlowGenerationProperties, + ContainerLengthDistribution ]) ModeOfTransportDistributionRepository().set_mode_of_transport_distributions({ ModeOfTransport.truck: { @@ -58,6 +62,17 @@ def setUp(self) -> None: ModeOfTransport.deep_sea_vessel: 0.15 } }) + + container_length_manager = ContainerLengthDistributionManager() + container_length_manager.set_container_length_distribution( # Set default container length distribution + { + ContainerLength.other: 0.001, + ContainerLength.twenty_feet: 0.4, + ContainerLength.forty_feet: 0.57, + ContainerLength.forty_five_feet: 0.029 + } + ) + now = datetime.datetime.now() ContainerFlowGenerationProperties.create( start_date=now, diff --git a/conflowgen/tests/previews/test_modal_split_preview__get_modal_split_for_hinterland.py b/conflowgen/tests/previews/test_modal_split_preview__get_modal_split_for_hinterland.py index e13eb84c..ea168ae4 100644 --- a/conflowgen/tests/previews/test_modal_split_preview__get_modal_split_for_hinterland.py +++ b/conflowgen/tests/previews/test_modal_split_preview__get_modal_split_for_hinterland.py @@ -1,7 +1,10 @@ import datetime import unittest +from conflowgen.domain_models.data_types.container_length import ContainerLength +from conflowgen.api.container_length_distribution_manager import ContainerLengthDistributionManager from conflowgen.domain_models.data_types.mode_of_transport import ModeOfTransport +from conflowgen.domain_models.distribution_models.container_length_distribution import ContainerLengthDistribution from conflowgen.domain_models.distribution_models.mode_of_transport_distribution import ModeOfTransportDistribution from conflowgen.domain_models.distribution_repositories.mode_of_transport_distribution_repository import \ ModeOfTransportDistributionRepository @@ -16,7 +19,8 @@ def setUp(self) -> None: self.sqlite_db = setup_sqlite_in_memory_db() self.sqlite_db.create_tables([ Schedule, - ModeOfTransportDistribution + ModeOfTransportDistribution, + ContainerLengthDistribution ]) now = datetime.datetime.now() ModeOfTransportDistributionRepository().set_mode_of_transport_distributions({ @@ -56,6 +60,17 @@ def setUp(self) -> None: ModeOfTransport.deep_sea_vessel: 0.15 } }) + + container_length_manager = ContainerLengthDistributionManager() + container_length_manager.set_container_length_distribution( # Set default container length distribution + { + ContainerLength.other: 0.001, + ContainerLength.twenty_feet: 0.4, + ContainerLength.forty_feet: 0.57, + ContainerLength.forty_five_feet: 0.029 + } + ) + self.preview = ModalSplitPreview( start_date=now.date(), end_date=(now + datetime.timedelta(weeks=2)).date(), diff --git a/conflowgen/tests/previews/test_modal_split_preview__get_transshipment.py b/conflowgen/tests/previews/test_modal_split_preview__get_transshipment.py index 2190bfa7..f6950b48 100644 --- a/conflowgen/tests/previews/test_modal_split_preview__get_transshipment.py +++ b/conflowgen/tests/previews/test_modal_split_preview__get_transshipment.py @@ -1,7 +1,10 @@ import datetime import unittest +from conflowgen.domain_models.data_types.container_length import ContainerLength +from conflowgen.api.container_length_distribution_manager import ContainerLengthDistributionManager from conflowgen.domain_models.data_types.mode_of_transport import ModeOfTransport +from conflowgen.domain_models.distribution_models.container_length_distribution import ContainerLengthDistribution from conflowgen.domain_models.distribution_models.mode_of_transport_distribution import ModeOfTransportDistribution from conflowgen.domain_models.distribution_repositories.mode_of_transport_distribution_repository import \ ModeOfTransportDistributionRepository @@ -16,7 +19,8 @@ def setUp(self) -> None: self.sqlite_db = setup_sqlite_in_memory_db() self.sqlite_db.create_tables([ Schedule, - ModeOfTransportDistribution + ModeOfTransportDistribution, + ContainerLengthDistribution ]) now = datetime.datetime.now() ModeOfTransportDistributionRepository().set_mode_of_transport_distributions({ @@ -56,6 +60,17 @@ def setUp(self) -> None: ModeOfTransport.deep_sea_vessel: 0.15 } }) + + container_length_manager = ContainerLengthDistributionManager() + container_length_manager.set_container_length_distribution( # Set default container length distribution + { + ContainerLength.other: 0.001, + ContainerLength.twenty_feet: 0.4, + ContainerLength.forty_feet: 0.57, + ContainerLength.forty_five_feet: 0.029 + } + ) + self.preview = ModalSplitPreview( start_date=now.date(), end_date=(now + datetime.timedelta(weeks=2)).date(), diff --git a/conflowgen/tests/previews/test_modal_split_preview_report.py b/conflowgen/tests/previews/test_modal_split_preview_report.py index 14ad1ae3..0c84cb43 100644 --- a/conflowgen/tests/previews/test_modal_split_preview_report.py +++ b/conflowgen/tests/previews/test_modal_split_preview_report.py @@ -1,7 +1,10 @@ import datetime +from conflowgen.domain_models.data_types.container_length import ContainerLength +from conflowgen.api.container_length_distribution_manager import ContainerLengthDistributionManager from conflowgen.application.models.container_flow_generation_properties import ContainerFlowGenerationProperties from conflowgen.domain_models.data_types.mode_of_transport import ModeOfTransport +from conflowgen.domain_models.distribution_models.container_length_distribution import ContainerLengthDistribution from conflowgen.domain_models.distribution_models.mode_of_transport_distribution import ModeOfTransportDistribution from conflowgen.domain_models.distribution_repositories.mode_of_transport_distribution_repository import \ ModeOfTransportDistributionRepository @@ -18,7 +21,8 @@ def setUp(self) -> None: self.sqlite_db.create_tables([ Schedule, ModeOfTransportDistribution, - ContainerFlowGenerationProperties + ContainerFlowGenerationProperties, + ContainerLengthDistribution ]) ModeOfTransportDistributionRepository().set_mode_of_transport_distributions({ ModeOfTransport.truck: { @@ -57,6 +61,17 @@ def setUp(self) -> None: ModeOfTransport.deep_sea_vessel: 0.15 } }) + + container_length_manager = ContainerLengthDistributionManager() + container_length_manager.set_container_length_distribution( # Set default container length distribution + { + ContainerLength.other: 0.001, + ContainerLength.twenty_feet: 0.4, + ContainerLength.forty_feet: 0.57, + ContainerLength.forty_five_feet: 0.029 + } + ) + now = datetime.datetime.now() ContainerFlowGenerationProperties.create( start_date=now, diff --git a/conflowgen/tests/previews/test_vehicle_capacity_exceeded_preview.py b/conflowgen/tests/previews/test_vehicle_capacity_exceeded_preview.py index 08eaaec1..6223bd39 100644 --- a/conflowgen/tests/previews/test_vehicle_capacity_exceeded_preview.py +++ b/conflowgen/tests/previews/test_vehicle_capacity_exceeded_preview.py @@ -3,7 +3,10 @@ import numpy as np +from conflowgen.domain_models.data_types.container_length import ContainerLength +from conflowgen.api.container_length_distribution_manager import ContainerLengthDistributionManager from conflowgen.domain_models.data_types.mode_of_transport import ModeOfTransport +from conflowgen.domain_models.distribution_models.container_length_distribution import ContainerLengthDistribution from conflowgen.domain_models.distribution_models.mode_of_transport_distribution import ModeOfTransportDistribution from conflowgen.domain_models.distribution_repositories.mode_of_transport_distribution_repository import \ ModeOfTransportDistributionRepository @@ -18,7 +21,8 @@ def setUp(self) -> None: self.sqlite_db = setup_sqlite_in_memory_db() self.sqlite_db.create_tables([ Schedule, - ModeOfTransportDistribution + ModeOfTransportDistribution, + ContainerLengthDistribution ]) now = datetime.datetime.now() ModeOfTransportDistributionRepository().set_mode_of_transport_distributions({ @@ -58,6 +62,17 @@ def setUp(self) -> None: ModeOfTransport.deep_sea_vessel: 0.15 } }) + + container_length_manager = ContainerLengthDistributionManager() + container_length_manager.set_container_length_distribution( # Set default container length distribution + { + ContainerLength.other: 0.001, + ContainerLength.twenty_feet: 0.4, + ContainerLength.forty_feet: 0.57, + ContainerLength.forty_five_feet: 0.029 + } + ) + self.preview = VehicleCapacityExceededPreview( start_date=now.date(), end_date=(now + datetime.timedelta(weeks=2)).date(), diff --git a/conflowgen/tests/previews/test_vehicle_capacity_exceeded_preview_report.py b/conflowgen/tests/previews/test_vehicle_capacity_exceeded_preview_report.py index 337d7d05..48e3e5b9 100644 --- a/conflowgen/tests/previews/test_vehicle_capacity_exceeded_preview_report.py +++ b/conflowgen/tests/previews/test_vehicle_capacity_exceeded_preview_report.py @@ -1,7 +1,10 @@ import datetime +from conflowgen.domain_models.data_types.container_length import ContainerLength +from conflowgen.api.container_length_distribution_manager import ContainerLengthDistributionManager from conflowgen.application.models.container_flow_generation_properties import ContainerFlowGenerationProperties from conflowgen.domain_models.data_types.mode_of_transport import ModeOfTransport +from conflowgen.domain_models.distribution_models.container_length_distribution import ContainerLengthDistribution from conflowgen.domain_models.distribution_models.mode_of_transport_distribution import ModeOfTransportDistribution from conflowgen.domain_models.distribution_repositories.mode_of_transport_distribution_repository import \ ModeOfTransportDistributionRepository @@ -19,7 +22,8 @@ def setUp(self) -> None: self.sqlite_db.create_tables([ Schedule, ModeOfTransportDistribution, - ContainerFlowGenerationProperties + ContainerFlowGenerationProperties, + ContainerLengthDistribution ]) ModeOfTransportDistributionRepository().set_mode_of_transport_distributions({ ModeOfTransport.truck: { @@ -58,6 +62,17 @@ def setUp(self) -> None: ModeOfTransport.deep_sea_vessel: 0.15 } }) + + container_length_manager = ContainerLengthDistributionManager() + container_length_manager.set_container_length_distribution( # Set default container length distribution + { + ContainerLength.other: 0.001, + ContainerLength.twenty_feet: 0.4, + ContainerLength.forty_feet: 0.57, + ContainerLength.forty_five_feet: 0.029 + } + ) + now = datetime.datetime.now() ContainerFlowGenerationProperties.create( start_date=now, diff --git a/docs/background.rst b/docs/background.rst index 1ccc0d0c..2c007579 100644 --- a/docs/background.rst +++ b/docs/background.rst @@ -185,8 +185,7 @@ While assessing the currently available alternatives, among others we have come - :cite:t:`briskorn2019generator` developed a test data generator that may be used to simulate yard crane container handling processes. - Their generic approach generates test examples of crane scheduling issues and is available at - :cite:`briskorn2019generator-software`. + Their generic approach generates test examples of crane scheduling issues. - A technique for the evaluation of quay crane scheduling models and solution methods is presented by :cite:t:`meisel2011unified`. diff --git a/docs/notebooks/data/prepared_dbs/demo_poc.sqlite b/docs/notebooks/data/prepared_dbs/demo_poc.sqlite index b24c124f..9037e85d 100644 --- a/docs/notebooks/data/prepared_dbs/demo_poc.sqlite +++ b/docs/notebooks/data/prepared_dbs/demo_poc.sqlite @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d89ebc53bcb3edd236c5ace15df8784edc5d34c4429d3f580def93d8b24fc2d +oid sha256:484567b0367711c6d367ef91e19865a738136171b255843fbb80fad251a3ed0f size 335872 diff --git a/docs/references.bib b/docs/references.bib index 052a9a7a..e6750acf 100644 --- a/docs/references.bib +++ b/docs/references.bib @@ -94,14 +94,6 @@ @Article{briskorn2019generator doi={10.1007/s00291-018-0529-z}, } -@online{briskorn2019generator-software, - author = {Wiehl, Andreas}, - title = {Instances Generator}, - howpublished = {\url{http://www.instances.de/dfg/}}, - note = {Accessed: 2022-07-20}, - year = {2018} -} - @article{meisel2011unified, title = {A unified approach for the evaluation of quay crane scheduling models and algorithms}, journal = {Computers \& Operations Research},