Skip to content

Commit

Permalink
algorithm modification for transshipment
Browse files Browse the repository at this point in the history
  • Loading branch information
Shubhangi Gupta committed May 16, 2024
1 parent 1abeefe commit ce1a3a8
Show file tree
Hide file tree
Showing 19 changed files with 174 additions and 23 deletions.
2 changes: 1 addition & 1 deletion conflowgen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
from conflowgen.domain_models.data_types.storage_requirement import StorageRequirement

# List of functions
from conflowgen.logging.logging import setup_logger
from conflowgen.log.log import setup_logger
from conflowgen.analyses import run_all_analyses
from conflowgen.previews import run_all_previews

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def __init__(self):

def get_report_as_text(self, **kwargs) -> str:
"""
The report as a text is represented as a table suitable for logging.
The report as a text is represented as a table suitable for log.
It uses a human-readable formatting style.
For the exact interpretation of the parameter, check
:meth:`.ContainerDwellTimeAnalysis.get_container_dwell_times`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def get_report_as_text(
self, **kwargs
) -> str:
"""
The report as a text is represented as a table suitable for logging.
The report as a text is represented as a table suitable for log.
It uses a human-readable formatting style.
Keyword Args:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def __init__(self):

def get_report_as_text(self, **kwargs) -> str:
"""
The report as a text is represented as a table suitable for logging. It uses a human-readable formatting style.
The report as a text is represented as a table suitable for log. It uses a human-readable formatting style.
Keyword Args:
initial_vehicle_type (:obj:`typing.Any`): Either ``"all"``, a single vehicle of type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def __init__(self):

def get_report_as_text(self, **kwargs) -> str:
"""
The report as a text is represented as a table suitable for logging. It uses a human-readable formatting style.
The report as a text is represented as a table suitable for log. It uses a human-readable formatting style.
Keyword Args:
vehicle_type (:py:obj:`Any`): Either ``"scheduled vehicles"``, a single vehicle of type
Expand Down
2 changes: 1 addition & 1 deletion conflowgen/analyses/modal_split_analysis_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def get_report_as_text(
self, **kwargs
) -> str:
"""
The report as a text is represented as a table suitable for logging. It uses a human-readable formatting style.
The report as a text is represented as a table suitable for log. It uses a human-readable formatting style.
Keyword Args:
start_date (datetime.datetime):
Expand Down
2 changes: 1 addition & 1 deletion conflowgen/analyses/yard_capacity_analysis_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def __init__(self):

def get_report_as_text(self, **kwargs) -> str:
"""
The report as a text is represented as a table suitable for logging. It uses a human-readable formatting style.
The report as a text is represented as a table suitable for log. It uses a human-readable formatting style.
Keyword Args:
storage_requirement: Either a single storage requirement of type :class:`.StorageRequirement` or a whole
Expand Down
14 changes: 13 additions & 1 deletion conflowgen/api/container_flow_generation_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ def set_properties(
start_date: datetime.date,
end_date: datetime.date,
name: typing.Optional[str] = None,
transportation_buffer: typing.Optional[float] = None
transportation_buffer: typing.Optional[float] = None,
ramp_up_period: typing.Optional[datetime.timedelta] = None,
ramp_down_period: typing.Optional[datetime.timedelta] = None,
) -> None:
"""
Args:
Expand All @@ -38,7 +40,12 @@ def set_properties(
name: The name of the generated synthetic container flow which helps to distinguish different scenarios.
transportation_buffer: Determines how many percent more of the inbound journey capacity is used at most to
transport containers on the outbound journey.
ramp_up_period: The period at the beginning during which operations gradually increases to full capacity.
This simulates the initial phase where container flow and terminal activities are scaled up.
ramp_down_period: The period at the end during which operations gradually decrease from full capacity.
This simulates the final phase where container flow and terminal activities are scaled down.
"""

properties = self.container_flow_generation_properties_repository.get_container_flow_generation_properties()

if name is not None:
Expand All @@ -47,6 +54,9 @@ def set_properties(
properties.start_date = start_date
properties.end_date = end_date

properties.ramp_up_period = ramp_up_period.total_seconds() / 86400 # in days as float
properties.ramp_down_period = ramp_down_period.total_seconds() / 86400 # in days as float

if transportation_buffer is not None:
properties.transportation_buffer = transportation_buffer

Expand All @@ -67,6 +77,8 @@ def get_properties(self) -> typing.Dict[str, typing.Union[str, datetime.date, fl
'start_date': properties.start_date,
'end_date': properties.end_date,
'transportation_buffer': properties.transportation_buffer,
'ramp_up_period': properties.ramp_up_period,
'ramp_down_period': properties.ramp_down_period,
}

def container_flow_data_exists(self) -> bool:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ class ContainerFlowGenerationProperties(BaseModel):
help_text="The last day of the generated container flow"
)

ramp_up_period= FloatField(
default=0,
help_text="Number of days for the ramp-up period"
)

ramp_down_period = FloatField(
default=0,
help_text="Number of days for the ramp-down period"
)

generated_at = DateTimeField(
default=lambda: datetime.datetime.now().replace(microsecond=0),
help_text="The date the these properties have been created"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import datetime
import enum
import logging
import typing
from typing import Dict, List, Callable, Type

from conflowgen.domain_models.container import Container
Expand All @@ -7,12 +10,19 @@
from conflowgen.domain_models.vehicle import LargeScheduledVehicle, AbstractLargeScheduledVehicle


class _Direction(enum.Enum):
inbound = 0
outbound = 1


class LargeScheduledVehicleRepository:

ignored_capacity = ContainerLength.get_teu_factor(ContainerLength.other)

def __init__(self):
self.transportation_buffer = None
self.ramp_up_period_end = None
self.ramp_down_period_start = None
self.free_capacity_for_outbound_journey_buffer: Dict[Type[AbstractLargeScheduledVehicle], float] = {}
self.free_capacity_for_inbound_journey_buffer: Dict[Type[AbstractLargeScheduledVehicle], float] = {}
self.logger = logging.getLogger("conflowgen")
Expand All @@ -21,6 +31,14 @@ def set_transportation_buffer(self, transportation_buffer: float):
assert -1 < transportation_buffer
self.transportation_buffer = transportation_buffer

def set_ramp_up_and_down_times(
self,
ramp_up_period_end: typing.Optional[datetime.datetime],
ramp_down_period_start: typing.Optional[datetime.datetime]
):
self.ramp_up_period_end = ramp_up_period_end
self.ramp_down_period_start = ramp_down_period_start

def reset_cache(self):
self.free_capacity_for_outbound_journey_buffer = {}
self.free_capacity_for_inbound_journey_buffer = {}
Expand Down Expand Up @@ -87,7 +105,8 @@ def get_free_capacity_for_inbound_journey(self, vehicle: Type[AbstractLargeSched
free_capacity_in_teu = self._get_free_capacity_in_teu(
vehicle=vehicle,
maximum_capacity=total_moved_capacity_for_inbound_transportation_in_teu,
container_counter=self._get_number_containers_for_inbound_journey
container_counter=self._get_number_containers_for_inbound_journey,
direction=_Direction.inbound
)
self.free_capacity_for_inbound_journey_buffer[vehicle] = free_capacity_in_teu
return free_capacity_in_teu
Expand Down Expand Up @@ -115,7 +134,8 @@ def get_free_capacity_for_outbound_journey(self, vehicle: Type[AbstractLargeSche
free_capacity_in_teu = self._get_free_capacity_in_teu(
vehicle=vehicle,
maximum_capacity=total_moved_capacity_for_onward_transportation_in_teu,
container_counter=self._get_number_containers_for_outbound_journey
container_counter=self._get_number_containers_for_outbound_journey,
direction=_Direction.outbound
)
self.free_capacity_for_outbound_journey_buffer[vehicle] = free_capacity_in_teu
return free_capacity_in_teu
Expand All @@ -125,7 +145,8 @@ def get_free_capacity_for_outbound_journey(self, vehicle: Type[AbstractLargeSche
def _get_free_capacity_in_teu(
vehicle: Type[AbstractLargeScheduledVehicle],
maximum_capacity: int,
container_counter: Callable[[Type[AbstractLargeScheduledVehicle], ContainerLength], int]
container_counter: Callable[[Type[AbstractLargeScheduledVehicle], ContainerLength], int],
direction: _Direction
) -> float:
loaded_20_foot_containers = container_counter(vehicle, ContainerLength.twenty_feet)
loaded_40_foot_containers = container_counter(vehicle, ContainerLength.forty_feet)
Expand All @@ -148,6 +169,13 @@ def _get_free_capacity_in_teu(
f"loaded_40_foot_containers: {loaded_40_foot_containers}, " \
f"loaded_45_foot_containers: {loaded_45_foot_containers} and " \
f"loaded_other_containers: {loaded_other_containers}"

arrival_time: datetime.datetime = vehicle.large_scheduled_vehicle.scheduled_arrival
if direction == _Direction.outbound and arrival_time < ramp_up_period_end:

Check notice on line 174 in conflowgen/domain_models/repositories/large_scheduled_vehicle_repository.py

View check run for this annotation

codefactor.io / CodeFactor

conflowgen/domain_models/repositories/large_scheduled_vehicle_repository.py#L174

Undefined variable 'ramp_up_period_end' (undefined-variable)
free_capacity_in_teu = maximum_capacity * 0.1
elif direction == _Direction.inbound and arrival_time >= ramp_down_period_start:

Check notice on line 176 in conflowgen/domain_models/repositories/large_scheduled_vehicle_repository.py

View check run for this annotation

codefactor.io / CodeFactor

conflowgen/domain_models/repositories/large_scheduled_vehicle_repository.py#L176

Undefined variable 'ramp_down_period_start' (undefined-variable)
free_capacity_in_teu = maximum_capacity * 0.1

return free_capacity_in_teu

@classmethod
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ def _update_generation_properties_and_distributions(self):
self.container_flow_end_date: datetime.date = container_flow_generation_properties.end_date
assert self.container_flow_start_date < self.container_flow_end_date

ramp_up_period = container_flow_generation_properties.ramp_up_period
ramp_down_period = container_flow_generation_properties.ramp_down_period
self.ramp_up_period_end = self.container_flow_start_date + datetime.timedelta(days=ramp_up_period)
self.ramp_down_period_start = self.container_flow_end_date - datetime.timedelta(days=ramp_down_period)
assert self.ramp_up_period_end <= self.ramp_down_period_start

self.transportation_buffer: float = container_flow_generation_properties.transportation_buffer
assert -1 < self.transportation_buffer

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import datetime
import logging
import math
import typing
from typing import Tuple, List, Dict, Type, Sequence

import numpy as np
Expand Down Expand Up @@ -42,10 +43,13 @@ def __init__(self):

def reload_properties(
self,
transportation_buffer: float
transportation_buffer: float,
ramp_up_period_end: typing.Optional[datetime.datetime],

Check notice on line 47 in conflowgen/flow_generator/large_scheduled_vehicle_for_onward_transportation_manager.py

View check run for this annotation

codefactor.io / CodeFactor

conflowgen/flow_generator/large_scheduled_vehicle_for_onward_transportation_manager.py#L47

Unused argument 'ramp_up_period_end' (unused-argument)
ramp_down_period_start: typing.Optional[datetime.datetime],

Check notice on line 48 in conflowgen/flow_generator/large_scheduled_vehicle_for_onward_transportation_manager.py

View check run for this annotation

codefactor.io / CodeFactor

conflowgen/flow_generator/large_scheduled_vehicle_for_onward_transportation_manager.py#L48

Unused argument 'ramp_down_period_start' (unused-argument)
):
assert -1 < transportation_buffer
self.schedule_repository.set_transportation_buffer(transportation_buffer)

self.logger.debug(f"Using transportation buffer of {transportation_buffer} when choosing the departing "
f"vehicles that adhere a schedule.")

Expand Down
Empty file added conflowgen/log/__init__.py
Empty file.
85 changes: 85 additions & 0 deletions conflowgen/log/log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import datetime
import logging
import os
import sys
from typing import Optional

from conflowgen.tools import docstring_parameter

LOGGING_DEFAULT_DIR = os.path.abspath(
os.path.join(
os.path.dirname(os.path.realpath(__file__)),
os.pardir,
"data",
"logs"
)
)

# noinspection SpellCheckingInspection
DEFAULT_LOGGING_FORMAT_STRING: str = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'


@docstring_parameter(DEFAULT_LOGGING_FORMAT_STRING=DEFAULT_LOGGING_FORMAT_STRING)
def setup_logger(
logging_directory: Optional[str] = None,
format_string: Optional[str] = None
) -> logging.Logger:
"""
This sets up the default logger with the name 'conflowgen'.
Several classes and functions use the same logger to inform the user about the current progress.
This is just a convenience function, you can easily set up your own logger that uses the same name.
See e.g.
https://docs.python.org/3/howto/logging.html#configuring-logging
for how to set up your own logger.
Args:
logging_directory:
The path of the directory where to store log files.
Defaults to ``<project root>/data/logs/``.
format_string:
The format string to use.
See e.g.
https://docs.python.org/3/library/logging.html#logrecord-attributes
for how to create your own format string.
Defaults to ``{DEFAULT_LOGGING_FORMAT_STRING}``.
Returns:
The set-up logger instance.
"""
if format_string is None:
format_string = DEFAULT_LOGGING_FORMAT_STRING

if logging_directory is None:
logging_directory = LOGGING_DEFAULT_DIR

time_prefix = str(datetime.datetime.now()).replace(":", "-").replace(" ", "--").split(".", maxsplit=1)[0]

formatter = logging.Formatter(format_string, datefmt="%d.%m.%Y %H:%M:%S %z")

logger = logging.getLogger("conflowgen")
logger.setLevel(logging.DEBUG)

stream_handlers = [handler for handler in logger.handlers if isinstance(handler, logging.StreamHandler)]
if any(handler.stream == sys.stdout for handler in stream_handlers):
logger.warning("Duplicate StreamHandler streaming to sys.stdout detected. "
"Skipping adding another StreamHandler.")
else:
stream_handler = logging.StreamHandler(stream=sys.stdout)
stream_handler.setLevel(logging.DEBUG)
stream_handler.setFormatter(formatter)
logger.addHandler(stream_handler)

if not os.path.isdir(logging_directory):
logger.debug(f"Creating log directory at {logging_directory}")
os.makedirs(logging_directory, exist_ok=True)
path_to_log_file = os.path.join(
logging_directory,
time_prefix + ".log"
)
logger.debug(f"Creating log file at {path_to_log_file}")
file_handler = logging.FileHandler(path_to_log_file)
file_handler.setFormatter(formatter)
file_handler.setLevel(logging.DEBUG)
logger.addHandler(file_handler)

return logger
2 changes: 1 addition & 1 deletion conflowgen/reporting/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def reload(self):
@abc.abstractmethod
def get_report_as_text(self, **kwargs) -> str:
"""
The report as a text is represented as a table suitable for logging.
The report as a text is represented as a table suitable for log.
It uses a human-readable formatting style.
The additional keyword arguments are passed to the analysis instance in case it accepts them.
Expand Down
2 changes: 1 addition & 1 deletion conflowgen/reporting/output_style.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def display_explanation(self, text: str) -> None:
class DisplayAsPlainText(DisplayAsMarkupLanguage):
"""
With this style, the output is simply returned in a plain manner.
This is, e.g., helpful when logging the text.
This is, e.g., helpful when log the text.
"""

DESIRED_LINE_LENGTH = 80 # doc: The console width used for wrapping output to new lines. This is not mandatory.
Expand Down
16 changes: 9 additions & 7 deletions conflowgen/tests/analyses/test_run_all_analyses.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import datetime
import unittest
import unittest.mock
from unittest import mock

from conflowgen.analyses import run_all_analyses
from conflowgen.application.models.container_flow_generation_properties import ContainerFlowGenerationProperties
Expand All @@ -25,8 +24,11 @@ def test_with_no_data(self):
run_all_analyses()
self.assertEqual(len(context.output), 35)

def test_with_no_data_as_graph(self):
with unittest.mock.patch('matplotlib.pyplot.show'):
with self.assertLogs('conflowgen', level='INFO') as context:
run_all_analyses(as_text=False, as_graph=True, static_graphs=True)
self.assertEqual(len(context.output), 27)
@mock.patch('matplotlib.pyplot.show')
#@mock.patch('conflowgen.reporting.auto_reporter.AutoReporter.present_reports')
def test_with_no_data_as_graph(self, mock_pyplot):

Check notice on line 29 in conflowgen/tests/analyses/test_run_all_analyses.py

View check run for this annotation

codefactor.io / CodeFactor

conflowgen/tests/analyses/test_run_all_analyses.py#L29

Unused argument 'mock_pyplot' (unused-argument)
# with unittest.mock.patch("matplotlib.pyplot.show"):
with self.assertLogs('conflowgen', level='INFO') as context:
print("Before run_all_analyses")
run_all_analyses(as_text=False, as_graph=True, static_graphs=True)
self.assertEqual(len(context.output), 27)
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ class MockedProperties:
start_date = datetime.date(2030, 1, 1)
end_date = datetime.date(2030, 12, 31)
transportation_buffer = 0.2
ramp_up_period = 10.0
ramp_down_period = 5.0
minimum_dwell_time_of_import_containers_in_hours = 3
minimum_dwell_time_of_export_containers_in_hours = 4
minimum_dwell_time_of_transshipment_containers_in_hours = 5
Expand All @@ -77,7 +79,9 @@ class MockedProperties:
'name': "my test data",
'start_date': datetime.date(2030, 1, 1),
'end_date': datetime.date(2030, 12, 31),
'transportation_buffer': 0.2
'transportation_buffer': 0.2,
'ramp_up_period': 10.0,
'ramp_down_period': 5.0,
}

with unittest.mock.patch.object(
Expand Down
Loading

0 comments on commit ce1a3a8

Please sign in to comment.