initial commit
This commit is contained in:
commit
d8d96c9c61
|
|
@ -0,0 +1,138 @@
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
Copyright Lucas Javaudin - All Rights Reserved
|
||||||
|
Unauthorized copying of the files in this repository, via any medium is strictly prohibited
|
||||||
|
Proprietary and confidential
|
||||||
|
Written by Lucas Javaudin <me@lucasjavaudin.com>, 2021
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Minimal makefile for Sphinx documentation
|
||||||
|
#
|
||||||
|
|
||||||
|
# You can set these variables from the command line, and also
|
||||||
|
# from the environment for the first two.
|
||||||
|
SPHINXOPTS ?=
|
||||||
|
SPHINXBUILD ?= sphinx-build
|
||||||
|
SOURCEDIR = .
|
||||||
|
BUILDDIR = _build
|
||||||
|
|
||||||
|
# Put it first so that "make" without argument is like "make help".
|
||||||
|
help:
|
||||||
|
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
|
|
||||||
|
.PHONY: help Makefile
|
||||||
|
|
||||||
|
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||||
|
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||||
|
%: Makefile
|
||||||
|
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
Initializer
|
||||||
|
===========
|
||||||
|
|
||||||
|
To be completed
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
@startuml
|
||||||
|
|
||||||
|
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml
|
||||||
|
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
|
||||||
|
|
||||||
|
Person(contributor, "Contributor", "Developer improving or extending MetroSim")
|
||||||
|
|
||||||
|
System_Ext(metroweb, "MetroWeb", "Web-based user interface and Web API")
|
||||||
|
|
||||||
|
System_Boundary(metrosim, "MetroSim") {
|
||||||
|
Container(initializer, "Initializer", "Python", "Computes data needed by the simulator from raw input data")
|
||||||
|
Container(simulator, "Simulator", "Python", "Iteratively simulates trips until convergence is reached")
|
||||||
|
ContainerDb(files, "I/O Files", "CSV", "Input and output files")
|
||||||
|
}
|
||||||
|
|
||||||
|
Rel(initializer, files, "Reads raw input data, writes pre-processed data")
|
||||||
|
Rel(simulator, files, "Reads raw and pre-processed input data, writes output data")
|
||||||
|
|
||||||
|
Rel(contributor, initializer, "Runs / Develops")
|
||||||
|
Rel(contributor, simulator, "Runs / Develops")
|
||||||
|
Rel(contributor, files, "Writes raw input data")
|
||||||
|
|
||||||
|
Rel(metroweb, initializer, "Runs")
|
||||||
|
Rel(metroweb, simulator, "Runs")
|
||||||
|
Rel(metroweb, files, "Writes raw input data")
|
||||||
|
|
||||||
|
SHOW_DYNAMIC_LEGEND()
|
||||||
|
|
||||||
|
@enduml
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
@startuml
|
||||||
|
|
||||||
|
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml
|
||||||
|
|
||||||
|
Person_Ext(web_user, "Web User", "User with basic needs or few technical skills")
|
||||||
|
Person_Ext(api_user, "API User", "User with advanced needs and some technical skills")
|
||||||
|
Person(contributor, "Contributor", "Developer improving or extending MetroSim")
|
||||||
|
|
||||||
|
Boundary(metropolis, "Metropolis") {
|
||||||
|
System_Ext(metroweb, "MetroWeb", "Web-based user interface and Web API")
|
||||||
|
System(metrosim, "MetroSim", "Multi-modal dynamic traffic assignment simulator")
|
||||||
|
}
|
||||||
|
|
||||||
|
Rel(web_user, metroweb, "Writes input, runs simulations, reads output")
|
||||||
|
Rel(api_user, metroweb, "Makes requests")
|
||||||
|
Rel(contributor, metrosim, "Installs and modifies")
|
||||||
|
Rel(metroweb, metrosim, "Runs and reads output")
|
||||||
|
|
||||||
|
SHOW_DYNAMIC_LEGEND()
|
||||||
|
|
||||||
|
@enduml
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
Overview
|
||||||
|
========
|
||||||
|
|
||||||
|
.. uml:: metrosim_system_context.puml
|
||||||
|
:caption: Metrosim System Context
|
||||||
|
:width: 100 %
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
.. uml:: metrosim_container.puml
|
||||||
|
:caption: MetroSim Container Diagram
|
||||||
|
:width: 100 %
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
|
||||||
|
routes
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
Routes and Trip Segments
|
||||||
|
========================
|
||||||
|
|
||||||
|
In MetroSim, when an agent is traveling from an origin to a destination (optionally, with intermediate stops), he / she chooses between multiple routes to perform his / her trip.
|
||||||
|
A route describes the modes of transportation taken to travel from an origin to a destination.
|
||||||
|
Hence, in MetroSim, route choice is similar to mode choice.
|
||||||
|
|
||||||
|
More formally, a route is a combination of multiple trip segments, where each trip segment represents only one mode of transportation, allowing for inter-modal trips.
|
||||||
|
Routes can range from very simple (e.g., travel by car from O to D) to very complex (e.g., travel by car from O to I\ :sub:`1`, then take metro line 1 from I\ :sub:`1` to I\ :sub:`2`, then walk from I\ :sub:`2` to I\ :sub:`3`, then take bus line 5 from I\ :sub:`3` to D).
|
||||||
|
The figure below represents an example of route, with 2 segments.
|
||||||
|
The second segment is a public-transit segment containing 3 legs.
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\overbrace{
|
||||||
|
\mathbf{O}
|
||||||
|
\overbrace{\longrightarrow \text{Car} \longrightarrow}^{\text{segment 1}}
|
||||||
|
\mathbf{I_1}
|
||||||
|
\overbrace{
|
||||||
|
\underbrace{\longrightarrow \text{Metro (line 1)} \longrightarrow}_{\text{leg 1}}
|
||||||
|
\mathbf{I_2}
|
||||||
|
\underbrace{\longrightarrow \text{Walk} \longrightarrow}_{\text{leg 2}}
|
||||||
|
\mathbf{I_3}
|
||||||
|
\underbrace{\longrightarrow \text{Bus (line 5)} \longrightarrow}_{\text{leg 3}}
|
||||||
|
}^{\text{segment 2}}
|
||||||
|
\mathbf{D}
|
||||||
|
}^{\text{route}}
|
||||||
|
|
||||||
|
|
||||||
|
Trip Segments
|
||||||
|
-------------
|
||||||
|
|
||||||
|
MetroSim has three different types of trip segments.
|
||||||
|
|
||||||
|
Car segment
|
||||||
|
It represents a trip by car from a source node to a target node.
|
||||||
|
A car segment is only defined by its source and its target, i.e., there is no intermediate stop and the exact path taken is not determined a priori.
|
||||||
|
|
||||||
|
Public-transit segment
|
||||||
|
It represents a public-transit trip from an access stop to an egress stop.
|
||||||
|
A public-transit segment is defined by its access stop and its egress stop but also by the sequence of legs taken between the two stops, i.e., there can exist multiple public-transit segments between any two stops.
|
||||||
|
A public-transit segment can include walking legs to transfer from one line to another but it always starts and ends with a public-transit leg.
|
||||||
|
An example of public-transit segment is: take bus line 1 from stop A to stop B, then walk from stop B to stop C, then take metro line 3 from stop C to stop D.
|
||||||
|
|
||||||
|
Walking segment
|
||||||
|
It represents a walking trip from a source node to a target node.
|
||||||
|
|
||||||
|
|
||||||
|
Trip segments can be classified in three categories, according to the characteristics of the travel utility they imply.
|
||||||
|
|
||||||
|
"Constant" trip segment
|
||||||
|
Travel utility is time-invariant (e.g., walking segment).
|
||||||
|
|
||||||
|
"Continuous" trip segment
|
||||||
|
Travel utility is continuously time-dependent (e.g., car segment).
|
||||||
|
|
||||||
|
"Discrete" trip segment
|
||||||
|
Travel utility is discontinuously time-dependent (e.g., public-transit segment).
|
||||||
|
|
||||||
|
|
||||||
|
Route Type
|
||||||
|
----------
|
||||||
|
|
||||||
|
The route type depends on the type of its segments, according to the following rules:
|
||||||
|
|
||||||
|
- If at least one segment is discrete, then the route is *discrete*.
|
||||||
|
- If at least one segment is continuous but no segment is discrete, then the route is *continuous*.
|
||||||
|
- If all segments are constant, then the route is *constant*.
|
||||||
|
|
||||||
|
The route type is relevant when computing the expected utility of a route.
|
||||||
|
For discrete routes, the utility of all possible departure times is computed (at least partially).
|
||||||
|
For continuous routes, the utility is computed for a finite set of departure times.
|
||||||
|
For constant routes, the utility is computed only once.
|
||||||
|
See :doc:`/architecture/simulator/pre-day_model` for additional details.
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
Road Network Representation
|
||||||
|
===========================
|
||||||
|
|
||||||
|
In MetroSim, the road network is represented as a standard graph with nodes (representing intersections) and edges (representing roads).
|
||||||
|
|
||||||
|
|
||||||
|
Edges
|
||||||
|
-----
|
||||||
|
|
||||||
|
Input Values
|
||||||
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
|
In MetroSim, each edge must have the following attributes.
|
||||||
|
|
||||||
|
source
|
||||||
|
The starting node of the edge.
|
||||||
|
|
||||||
|
target
|
||||||
|
The arrival node of the edge.
|
||||||
|
|
||||||
|
lanes
|
||||||
|
The number of lanes on the edge.
|
||||||
|
|
||||||
|
speed
|
||||||
|
The base speed (or free-flow speed) of the edge (in kilometers per hour).
|
||||||
|
|
||||||
|
length
|
||||||
|
The length of the edge (in kilometers).
|
||||||
|
|
||||||
|
capacity
|
||||||
|
The capacity of the road (whose meaning depend on the congestion function).
|
||||||
|
|
||||||
|
congestion_function
|
||||||
|
A function that yields the average speed on the road, given the characteristics of the road and the current number of vehicles.
|
||||||
|
|
||||||
|
|
||||||
|
Congestion Function
|
||||||
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
TO BE DISCUSSED
|
||||||
|
|
||||||
|
|
||||||
|
Dynamic Attributes
|
||||||
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Each edge has a dynamic attribute to keep track of the current number of vehicles on the edge.
|
||||||
|
This attribute is updated every time a vehicle enters or exists the edge.
|
||||||
|
|
||||||
|
Also, for each edge, the average number of vehicles on the edge and the average travel time is computed, at regular recording intervals.
|
||||||
|
Consider recording interval :math:`T = [\underline{t}, \overline{t}]`.
|
||||||
|
Denote by :math:`\{t_1, t_2, \dots, t_m\}` the times at which an event occurred for the edge and denote by :math:`\{n_1, n_2, \dots, n_m\}` the number of vehicles on the edge that resulted from the event (with :math:`n_0` the number of vehicles before the first event).
|
||||||
|
Then, the average number of vehicles on the edge :math:`\bar{n}_T`, for recording interval :math:`T`, is computed as the following weighted average:
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\bar{n}_T = \frac{
|
||||||
|
n_0 \cdot (t_1 - \underline{t}) + n_1 \cdot (t_2 - t_1) + \cdots + n_{m-1} \cdot (t_m - t_{m-1}) + n_m \cdot (\overline{t} - t_m)
|
||||||
|
}{
|
||||||
|
\overline{t} - \underline{t}
|
||||||
|
}
|
||||||
|
|
||||||
|
The average travel time :math:`\bar{tt}_T` is computed from the congestion function of the edge, assuming that the number of vehicles is equal to the average :math:`\bar{n}_T`.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The average number of vehicles :math:`\bar{n}_T` can be computed dynamically, by updating the value each time an event occurs.
|
||||||
|
|
||||||
|
The average travel times are then used by the day-to-day model to computed historical travel times.
|
||||||
|
|
||||||
|
Both the average number of vehicles and average travel times are output of the simulator.
|
||||||
|
|
||||||
|
Nodes
|
||||||
|
-----
|
||||||
|
|
||||||
|
Input Values
|
||||||
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
|
In MetroSim, each node must have the following attributes.
|
||||||
|
|
||||||
|
upstream_edges
|
||||||
|
A list of edges whose target node is this node.
|
||||||
|
|
||||||
|
downstream_edges
|
||||||
|
A list of edges whose source node is this node.
|
||||||
|
|
||||||
|
priority_function
|
||||||
|
A function that yields the time taken to cross the intersection, in any direction, given the queue of vehicles at the intersection.
|
||||||
|
|
||||||
|
|
||||||
|
Priority Function
|
||||||
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The priority function is called in two situations:
|
||||||
|
|
||||||
|
1. A new vehicle enters the node.
|
||||||
|
2. An event *EdgePriority* is triggered for the current edge.
|
||||||
|
|
||||||
|
In both situations, the function takes as argument the current time and the current state of the edge (the queue of vehicles for each direction and some optional values such as the state of traffic lights).
|
||||||
|
In the first situation, the function also takes as argument the vehicle entering the node, as well as its upstream edge and its downstream edge.
|
||||||
|
The function returns a list of *VehicleExitsEdge* events, indicating that a given vehicle exits the edge at a given time, and an optional *EdgePriority* event, indicating when the function must be called again.
|
||||||
|
|
||||||
|
.. note:: **Example 1: Exogenous Turn Penalties**
|
||||||
|
|
||||||
|
In case of exogenous turn penalties, each vehicle reaching the node from upstream edge :math:`e_{in}` and going toward downstream edge :math:`e_{out}` incurs a time penalty of :math:`p(e_{in}, e_{out})` seconds.
|
||||||
|
Then, when a new vehicle enters the node, the priority function is called and returns a *VehicleExitsEdge* event that will trigger in :math:`p(e_{in}, e_{out})` seconds from now.
|
||||||
|
|
||||||
|
The priority function never returns a *EdgePriority* event and, thus, it is only called when a vehicle enters the node.
|
||||||
|
|
||||||
|
.. note:: **Example 2: Traffic Lights**
|
||||||
|
|
||||||
|
At any given time, the state of the edge contains:
|
||||||
|
|
||||||
|
- The queue of vehicles waiting at the edge, for each upstream and downstream edges.
|
||||||
|
- The state of the lights (which light is green).
|
||||||
|
|
||||||
|
When an event *EdgePriority* is triggerd, the priority function is called and the following happen:
|
||||||
|
|
||||||
|
- The lights are switched to the next state.
|
||||||
|
- For any upstream and downstream edges with green ligths and with a non-null queue, *VehicleExitsEdge* events are created to let the vehicles pass, in the order of the queue, with a correct timing (e.g., one vehicle every two seconds) and the vehicles are removed from the queue.
|
||||||
|
- If a queue gets empty, the exit time of the last vehicle is recorded, so that if any vehicle arrive while the lights are still green, the priority function can compute its exit time.
|
||||||
|
- An *EdgePriority* event is created at the time when the lights are expected to switch again.
|
||||||
|
|
||||||
|
When a vehicle enters the node, the priority function is called and two different cases can occur:
|
||||||
|
|
||||||
|
- If the lights are green and the queue is empty for the upstream and downstream edges of the vehicle, then the vehicle can pass and a *VehicleExitsEdge* event is created with the correct timing (accounting for potential vehicles currently crossing the node).
|
||||||
|
- Otherwise, the vehicle is added to the queue of vehicles waiting at the edge, for the given upstream and downstream edges.
|
||||||
|
|
||||||
|
In the route choice model, the crossing time of the node, from a given upstream edge to a subset of its downstream edges, is used to choose the actual downstream edge chosen.
|
||||||
|
By default, the historical crossing times are used but convergence could be faster if the priority function can return predicted crossing times, given the current state of the node.
|
||||||
|
|
||||||
|
|
||||||
|
Dynamic Attributes
|
||||||
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
When a vehicle exits an edge, the exit time is compared with its entry time to compute the crossing time of the edge.
|
||||||
|
Then, for each recording interval, the crossing times of all vehicles who *entered* the edge in this interval are averaged.
|
||||||
|
These crossing times are then used when computing the historical crossing times in the day-to-day model (needed for predicted travel times).
|
||||||
|
|
||||||
|
.. note:: This actually needs to be done only if crossing time is endogenous.
|
||||||
|
|
||||||
|
The number of vehicles who crossed the edge, for each upstream and downstream edges, is also computed, for each recording interval.
|
||||||
|
|
||||||
|
Both crossing times and number of vehicles are an output on the simulator.
|
||||||
|
|
||||||
|
|
||||||
|
Vehicle Types
|
||||||
|
-------------
|
||||||
|
|
||||||
|
TO BE DISCUSSED
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
Day-to-Day Model
|
||||||
|
================
|
||||||
|
|
||||||
|
To be completed
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
Events
|
||||||
|
======
|
||||||
|
|
@ -0,0 +1,453 @@
|
||||||
|
Pre-Day Model
|
||||||
|
=============
|
||||||
|
|
||||||
|
As its name suggests, the pre-day model runs before any agent start his / her trips.
|
||||||
|
In the pre-day model, the agents are planning their trips for the next day, given the expected network conditions.
|
||||||
|
Any decision taken in the pre-day model is susceptible to be changed during the day (see :doc:`within-day_model`).
|
||||||
|
|
||||||
|
.. uml:: pre-day_model_component.puml
|
||||||
|
:caption: Pre-Day Model Component Diagram
|
||||||
|
:width: 100 %
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Route Choice
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
The route choice function runs independently for each trip of each agent.
|
||||||
|
The function evaluates all the attractive routes of the agent for this trip.
|
||||||
|
The agent is planning to take the route with the best utility.
|
||||||
|
The planned departure time is the best departure time for the chosen route.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Before evaluating all the routes, the function sort the routes by decreasing utility (using utility guesses).
|
||||||
|
Then, the routes are evaluated in this order so that the best route is likely to be found early, and other routes can be skipped if the function knows that the utility will not be larger.
|
||||||
|
The utility guesses can be free-flow utilities (computed by the initializer) or the utilities computed during the previous iteration.
|
||||||
|
|
||||||
|
Route Evaluation
|
||||||
|
----------------
|
||||||
|
|
||||||
|
To evaluate the utility and departure time of a given route of an agent, the route choice function calls the route evaluation function.
|
||||||
|
This function runs differently for continuous-time or discrete-time routes.
|
||||||
|
|
||||||
|
Continuous-Time Routes
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Continuous-time routes are composed of constant or continuous trip segments (see :doc:`/architecture/overview/routes`).
|
||||||
|
For constant segments, MetroSim knows the segment travel-time, which is constant for the whole simulation period.
|
||||||
|
For continuous segments, MetroSim knows the segment travel-time for evenly spaced departure times.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
TODO: Add a reference to technical parameters page, where the argument used for the evenly spaced time intervals is defined.
|
||||||
|
|
||||||
|
When evaluating a continuous-time route of an agent, MetroSim first constructs a list of departure-times for which it will compute the expected travel utility (see TODO).
|
||||||
|
Then, MetroSim retrieves the travel time for the first segment of the route, for each departure time in this list
|
||||||
|
For all following segments (if any), MetroSim retrieves the travel time for this segment, for each departure time in the list, *accounting for the travel time of the previous segments*.
|
||||||
|
|
||||||
|
The following table represents this process.
|
||||||
|
The first column is the departure-time list of the agent.
|
||||||
|
The second column is the travel time for the first segment, for each departure time from column 1.
|
||||||
|
The third column is the departure time for the second segment (i.e., the sum of columns 1 and 2).
|
||||||
|
The fourth column is the travel time for the second segment, for each departure time from column 3.
|
||||||
|
The last column is the total travel time (i.e., the sum of columns 2 and 4).
|
||||||
|
|
||||||
|
========= ==================== ====================== ==================== ===================
|
||||||
|
Dep. time Travel time (seg. 1) Dep. time (for seg. 2) Travel time (seg. 2) Travel time (total)
|
||||||
|
========= ==================== ====================== ==================== ===================
|
||||||
|
7:00 0:20 7:20 0:10 0:30
|
||||||
|
7:30 0:21 7:51 0:13 0:34
|
||||||
|
7:45 0:23 8:08 0:14 0:37
|
||||||
|
7:50 0:25 8:15 0:14 0:39
|
||||||
|
7:55 0:25 8:20 0:13 0:38
|
||||||
|
8:00 0:27 8:27 0:13 0:40
|
||||||
|
8:05 0:27 8:32 0:13 0:40
|
||||||
|
8:10 0:26 8:36 0:12 0:38
|
||||||
|
8:15 0:25 8:40 0:12 0:37
|
||||||
|
8:30 0:22 8:52 0:11 0:33
|
||||||
|
9:00 0:20 9:20 0:10 0:30
|
||||||
|
========= ==================== ====================== ==================== ===================
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
TODO: Find a smart way to get time intervals for an agent (with a higher density closer to a guessed departure time). Explain it in the docs and add a reference here.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
With continuous segments, MetroSim can try to retrieve travel time at a departure time for which travel time is not recorded.
|
||||||
|
In this case, MetroSim approximates the travel time (see TODO).
|
||||||
|
|
||||||
|
|
||||||
|
Once MetroSim has computed total travel time for all departure times in the list, it can compute total utility using the following formula::
|
||||||
|
|
||||||
|
total_utility = travel_utility(travel_time) + schedule_utility(departure_time, travel_time)
|
||||||
|
|
||||||
|
The computation of travel utility is specific to the mode of transportation of the route.
|
||||||
|
Then, the function approximates the maximum of the total utility over the departure-time period.
|
||||||
|
It returns the approximate maximum total utility and the corresponding departure time.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
If a route combines multiple modes of transportation, then the travel utility and travel time of each part of the route are computed independently.
|
||||||
|
Total travel utility and total travel time of the route are simply the sum of the partial travel utilities and travel times.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
TODO: Discuss how stochasticity is implemented.
|
||||||
|
|
||||||
|
Discrete-Time Routes
|
||||||
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
For discrete-time routes, the function finds the departure time with the highest utility.
|
||||||
|
To do so, it evaluates all departure times one by one.
|
||||||
|
When calling the function to compute the utility of the route, the current best utility found for the trip (for any route) is passed as an argument so that the function called can stop prematurely if the utility can not be larger than the best utility.
|
||||||
|
Also, the departure times are evaluated in order such that best departure times are likely to be evaluated early.
|
||||||
|
Here is a pseudocode of the function ``get_route_utility(route, utility_ub)``::
|
||||||
|
|
||||||
|
dep_times = route.get_sorted_dep_times()
|
||||||
|
for dep_time in dep_times:
|
||||||
|
u = route.get_exp_utility(dep_time, agent, best_utility)
|
||||||
|
if u > best_utility:
|
||||||
|
best_utility = u
|
||||||
|
best_route = route
|
||||||
|
best_dep_time = dep_time
|
||||||
|
|
||||||
|
|
||||||
|
Departure-Time Choice in Metropolis 1
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
This section describes how departure-time choice is implemented in Metropolis 1 (for car commutes).
|
||||||
|
Recall that Metropolis 1 works with the :math:`\alpha-\beta-\gamma` model, where :math:`t^*` is either the desired arrival time or desired departure time (we assume below that it represents a desired arrival time).
|
||||||
|
|
||||||
|
Metropolis knows the travel times :math:`\{{tt}_1, \dots, {tt}_{10}\}` between origin and destination for 10 evenly spaced values of departure time :math:`\{{td}_1, \dots, {td}_{10}\}`.
|
||||||
|
The associated arrival times are computed as :math:`\{{ta}_1, \dots, {ta}_{10}\} = \{{td}_1+{tt}_1, \dots, {td}_{10}+{tt}_{10}\}`.
|
||||||
|
Metropolis also approximates the travel time such that arrival time is :math:`t^*` using a linear approximation:
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\hat{tt} = {tt}_{i-1} + (t^* - {ta}_{i-1}) \cdot \frac{{tt}_i - {tt}_{i-1}}{{ta}_{i} - {ta}_{i-1}},
|
||||||
|
|
||||||
|
where :math:`i` is such that :math:`{ta}_{i-1} < t^* < {ta}_i`.
|
||||||
|
The departure time which is such that the agent is expecting to arrive just on time is thus :math:`\hat{td} = t^*-\hat{tt}`.
|
||||||
|
The values :math:`\hat{td}`, :math:`\hat{tt}` and :math:`t^*` are added to the previous lists, which now contains 11 values.
|
||||||
|
|
||||||
|
.. plot:: plots/metro1_tt.py
|
||||||
|
|
||||||
|
Travel times as a function of arrival times
|
||||||
|
|
||||||
|
Then, Metropolis computes the exponential of the travel cost for each departure time, using the following formula:
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\exp(-c_i/\mu_{\text{dep}}) = \exp\left( -\frac{\alpha \cdot {tt}_i + {sc}_i}{\mu_{\text{dep}}} \right),
|
||||||
|
|
||||||
|
where
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
{sc}_i = \beta \left[t^*-{ta}_i\right]^+ + \gamma \left[{ta}_i-t^*\right]^+.
|
||||||
|
|
||||||
|
.. plot:: plots/metro1_c.py
|
||||||
|
|
||||||
|
Travel costs as a function of arrival times
|
||||||
|
|
||||||
|
.. plot:: plots/metro1_expc.py
|
||||||
|
|
||||||
|
Exponential of travel costs as a function of arrival times
|
||||||
|
|
||||||
|
Metropolis also computes the following values.
|
||||||
|
The marginal travel time (using linear approximation):
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\delta_i = \frac{\text{d} {tt}_i}{\text{d} {ta}_i} = \frac{{tt}_{i+1} - {tt}_i}{{ta}_{i+1} - {ta}_i}.
|
||||||
|
|
||||||
|
The marginal cost of arriving one minute later:
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
b_i = \frac{\text{d} c_i}{\text{d} {ta}_i} = \alpha \cdot \delta_i - \beta \cdot \mathbf{1}_{\{{ta}_i<t^*\}} + \gamma \cdot \mathbf{1}_{\{{ta}_i \geq t^*\}}.
|
||||||
|
|
||||||
|
This value:
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
a_i = c_i + b_i \cdot {ta}_i = \alpha ({tt}_i - {ta}_i \cdot \delta_i) + \beta \cdot t^* \cdot \mathbf{1}_{\{{ta}_i < t^*\}} - \gamma \cdot t^* \cdot \mathbf{1}_{\{{ta}_i \geq t^*\}}.
|
||||||
|
|
||||||
|
|
||||||
|
The individual chooses an arrival time.
|
||||||
|
His / her departure time is deduced using the expected travel time for the chosen arrival time.
|
||||||
|
|
||||||
|
The departure time is chosen in three steps:
|
||||||
|
|
||||||
|
#. The individual chooses an interval :math:`[{ta}_i, {ta}_{i+1})`.
|
||||||
|
#. The individual chooses an arrival time :math:`{ta}^* \in [{ta}_i, {ta}_{i+1})`.
|
||||||
|
#. The chosen departure time is deduced from :math:`{ta}^*`.
|
||||||
|
|
||||||
|
Step 1
|
||||||
|
""""""
|
||||||
|
|
||||||
|
For the first step, the probability to choose interval :math:`[{ta}_i, {ta}_{i+1})` is
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
P_i = \frac{ g_i }{ \sum_i g_i },
|
||||||
|
|
||||||
|
where
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
:label: approx1
|
||||||
|
|
||||||
|
g_i = \max\left( \frac{\exp(-c_i/\mu_{\text{dep}}) - \exp(-c_{i+1}/\mu_{\text{dep}})}{b_i}, 0 \right),
|
||||||
|
|
||||||
|
if :math:`b_i>0`, and
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
:label: approx2
|
||||||
|
|
||||||
|
g_i = \frac{\exp(-c_i/\mu_{\text{dep}}) \cdot ( {ta}_{i+1} - {ta}_i )}{\mu_{\text{dep}}},
|
||||||
|
|
||||||
|
otherwise.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Metropolis approximates the integral
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\int_{{ta}_i}^{{ta}_{i+1}} \exp(-c({ta}) / \mu_{\text{dep}}) \text{d}{ta}.
|
||||||
|
|
||||||
|
Assume that :math:`c({ta})` is piece-wise linear, i.e.,
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\frac{ \partial c }{ \partial {ta} }({ta}) = b_i, \quad \forall {ta} \in [{ta}_i, {ta}_{i+1}), \quad \forall i \in \{1, \dots, 11\}.
|
||||||
|
|
||||||
|
Then,
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\int_{{ta}_i}^{{ta}_{i+1}} \exp(-c({ta}) / \mu_{\text{dep}}) \text{d}{ta}
|
||||||
|
=
|
||||||
|
-\mu_{\text{dep}} \left[ \frac{ \exp\big( -c(ta) / \mu_{\text{dep}} \big) }{ b_i } \right]^{{ta}_{i+1}}_{{ta}_i},
|
||||||
|
|
||||||
|
and thus
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
\int_{{ta}_i}^{{ta}_{i+1}} \exp(-c({ta}) / \mu_{\text{dep}}) \text{d}{ta}
|
||||||
|
=
|
||||||
|
\mu_{\text{dep}}\frac{\exp(-c_i/\mu_{\text{dep}}) - \exp(-c_{i+1}/\mu_{\text{dep}})}{b_i},
|
||||||
|
|
||||||
|
hence equation :eq:`approx1`, where the factor :math:`\mu_{\text{dep}}` is discarded as it impacts similarly :math:`g_i` for all values of :math:`i`.
|
||||||
|
The :math:`\max` function ensures that :math:`g_i` is positive.
|
||||||
|
|
||||||
|
When :math:`b_i=0`, then the integral
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\int_{{ta}_i}^{{ta}_{i+1}} \exp(-c({ta}) / \mu_{\text{dep}}) \text{d}{ta}.
|
||||||
|
|
||||||
|
corresponds to the area of a rectangle of height :math:`\exp(-c_i/\mu_{\text{dep}})` and of width :math:`{ta}_{i+1} - {ta}_{i}`, hence equation :eq:`approx2`, where the multiplicative factor :math:`1/\mu_{\text{dep}}` is added to be consistent with the previous equation.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
Would it be better to approximate :math:`b_i` with
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\frac{{c}_{i+1} - {c}_i}{{ta}_{i+1} - {ta}_i}.
|
||||||
|
|
||||||
|
instead of
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\alpha \cdot \delta_i - \beta \cdot \mathbf{1}_{\{{td}_i<t^*\}} + \gamma \cdot \mathbf{1}_{\{{td}_i \geq t^*\}}.
|
||||||
|
|
||||||
|
|
||||||
|
Each individual has a value :math:`\epsilon \in [0, 1]` for his / her departure-time choice.
|
||||||
|
|
||||||
|
Let
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
F_i = \sum_{j<i} P_i.
|
||||||
|
|
||||||
|
|
||||||
|
Metropolis finds the value :math:`i^*` such that
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
F_{i^*} \leq \epsilon < F_{i^*+1}.
|
||||||
|
|
||||||
|
The departure time chosen by the individual will be in the interval :math:`[{ta}_{i^*}, {ta}_{i^*+1})`.
|
||||||
|
|
||||||
|
.. plot:: plots/metro1_cumprobs.py
|
||||||
|
|
||||||
|
Cumulative probabilities and interval choice
|
||||||
|
|
||||||
|
Step 2
|
||||||
|
""""""
|
||||||
|
|
||||||
|
Then, Metropolis computes an exact arrival time :math:`{ta}^*`, according to the following rules.
|
||||||
|
|
||||||
|
Let
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
A = \exp(-c_{i^*} / \mu_{\text{dep}}) - b_{i^*} \left(\epsilon-F_{i^*}\right) \sum_i g_i,
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
B = 1 - 2 \cdot b_{i^*} \cdot \left( \epsilon - F_{i^*} \right) \cdot \sum_i g_i / \exp(-c_{i^*} / \mu_{\text{dep}}).
|
||||||
|
|
||||||
|
|
||||||
|
**Case 1:**
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
{ta}^* = \frac{ - \mu_{\text{dep}} \cdot \log(A) - a_{i^*} }{ b_{i^*} }.
|
||||||
|
|
||||||
|
|
||||||
|
**Case 2:**
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
{ta}^* = {ta}_{i^*} + \frac{ \mu_{\text{dep}} }{ b_{i^*} } \left( 1 + \sqrt{B} \right).
|
||||||
|
|
||||||
|
|
||||||
|
**Case 3:**
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
{ta}^* = {ta}_{i^*} + (\epsilon - F_{i^*}) \sum_i g_i / \exp(-c_{i^*} / \mu_{\text{dep}}).
|
||||||
|
|
||||||
|
|
||||||
|
**Case 4:**
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
{ta}^* = {ta}_{i^*} + ({ta}_{i^*+1} - {ta}_{i^*})\frac{(\epsilon-F_{i^*})}{P_{i^*}}.
|
||||||
|
|
||||||
|
|
||||||
|
**Case 5:**
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
{ta}^* = {ta}_{i^*}.
|
||||||
|
|
||||||
|
|
||||||
|
**Case 6:**
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
{ta}^* = {ta}_{i^*+1}.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
ta_star = -1
|
||||||
|
if |b_i| > 0:
|
||||||
|
if A > 0:
|
||||||
|
# Case 1.
|
||||||
|
ta_star = ...
|
||||||
|
if (ta_star < ta_prev or ta_star > ta_next) and B > 0:
|
||||||
|
# Case 2.
|
||||||
|
ta_star = ...
|
||||||
|
else:
|
||||||
|
# Case 3.
|
||||||
|
ta_star = ...
|
||||||
|
if ta_star < ta_prev or ta_star > ta_next:
|
||||||
|
# Case 4.
|
||||||
|
ta_star = ...
|
||||||
|
if ta_star < ta_prev:
|
||||||
|
# Case 5.
|
||||||
|
ta_star = ...
|
||||||
|
else if ta_star > ta_next:
|
||||||
|
# Case 6.
|
||||||
|
ta_star = ...
|
||||||
|
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Let
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
q = \frac{\epsilon - F_{i^*}}{P_{i^*}}.
|
||||||
|
|
||||||
|
We want to find :math:`{ta}^*` such that
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
q = \frac{
|
||||||
|
\int_{{ta}_{i^*}}^{{ta}^*} \exp(-c(t) / \mu_{\text{dep}}) \text{d}t
|
||||||
|
}{
|
||||||
|
\int_{{ta}_{i^*}}^{{ta}_{i^*+1}} \exp(-c(t) / \mu_{\text{dep}}) / \text{d}t
|
||||||
|
}.
|
||||||
|
|
||||||
|
This expression simplifies to
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
c({ta}^*) = -\mu_{\text{dep}} \log \left\{ \exp(-c_{i^*}/\mu_{\text{dep}}) - q \big[ \exp(-c_{i^*}/\mu_{\text{dep}}) - \exp(-c_{i^*+1}/\mu_{\text{dep}}) \big] \right\}.
|
||||||
|
|
||||||
|
If we set :math:`{ta}^* = p {ta}_{i^*} + (1-p) {ta}_{i^*+1}`, then we have :math:`c({ta}^*) = p c_{i^*} + (1-p) c_{i^*+1}`, and thus
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
p = \frac{
|
||||||
|
-c_{i^*+1} - \mu_{\text{dep}} \log(A')
|
||||||
|
}{
|
||||||
|
c_{i^*} - c_{i^*+1}
|
||||||
|
},
|
||||||
|
|
||||||
|
where
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
A' = \exp(-c_{i^*}/\mu_{\text{dep}}) - q \big[ \exp(-c_{i^*}/\mu_{\text{dep}}) - \exp(-c_{i^*+1}/\mu_{\text{dep}}) \big].
|
||||||
|
|
||||||
|
Observe that
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
\exp(-c_{i^*}/\mu_{\text{dep}}) - \exp(-c_{i^*+1}/\mu_{\text{dep}}) \approx b_i g_i \approx b_i P_i \sum g_i,
|
||||||
|
|
||||||
|
and thus, we have :math:`A=A'`.
|
||||||
|
|
||||||
|
Then,
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
{ta}^* = \frac{
|
||||||
|
-c_{i^*+1} - \mu_{\text{dep}} \log(A)
|
||||||
|
}{
|
||||||
|
c_{i^*} - c_{i^*+1}
|
||||||
|
}
|
||||||
|
(t_{i^*} - t_{i^*+1})
|
||||||
|
- t_{i^*+1},
|
||||||
|
|
||||||
|
which can be approximated with
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
{ta}^* = \frac{ - \mu_{\text{dep}} \log(A) }{ b_{i^*} } - \frac{c_{i^*+1}}{ b_{i^*} } - {ta}_{i^*+1}.
|
||||||
|
|
||||||
|
Finally, using the approximation
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
- \frac{c_{i^*+1}}{ b_{i^*} } - {ta}_{i^*+1}
|
||||||
|
\approx
|
||||||
|
- \frac{c_{i^*}}{ b_{i^*} } - {ta}_{i^*}
|
||||||
|
=
|
||||||
|
- \frac{a_{i^*}}{b_{i^*}},
|
||||||
|
|
||||||
|
we get
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
{ta}^* = \frac{ - \mu_{\text{dep}} \log(A) - a_{i^*} }{ b_{i^*} },
|
||||||
|
|
||||||
|
which corresponds to **Case 1**.
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
@startuml
|
||||||
|
|
||||||
|
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
|
||||||
|
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml
|
||||||
|
|
||||||
|
Container(manager, "Manager", "Python", "Converts input data to Python models, initializes variables, runs the main loop")
|
||||||
|
ContainerQueue(events, "Events", "Python", "Decisions taken by agents (e.g., departure from origin, boarding and alighting, turn at intersections)")
|
||||||
|
|
||||||
|
Person(contributor, "Contributor", "Developer improving or extending MetroSim")
|
||||||
|
|
||||||
|
ContainerDb(network_conditions, "Predicted Network Conditions", "Python", "Predicted values for relevant network conditions (e.g., travel time by car, vehicle occupancy)")
|
||||||
|
|
||||||
|
Container_Boundary(preday_model, "Pre-Day Model") {
|
||||||
|
Component(mode_choice, "Route Choice", "Python", "Computes chosen route and associated departure time")
|
||||||
|
Component(route_evaluation, "Route Evaluation", "Python", "Computes the chosen departure time for a given route and associated utility")
|
||||||
|
Component(car, "Car", "Python", "Defines how utility of car routes is computed")
|
||||||
|
Component(public_transit, "Public Transit", "Python", "Defines how utility of public-transit routes is computed")
|
||||||
|
Component(generic_mode, "Generic Mode", "Python", "Defines how utility of a given mode is computed")
|
||||||
|
}
|
||||||
|
|
||||||
|
Rel(manager, mode_choice, "Calls")
|
||||||
|
Rel(mode_choice, events, "Writes")
|
||||||
|
|
||||||
|
Rel(contributor, generic_mode, "Develops")
|
||||||
|
|
||||||
|
Rel(mode_choice, route_evaluation, "Requests utility and departure time")
|
||||||
|
|
||||||
|
Rel(route_evaluation, car, "Requests utility")
|
||||||
|
Rel(route_evaluation, public_transit, "Requests utility")
|
||||||
|
Rel(route_evaluation, generic_mode, "Requests utility")
|
||||||
|
|
||||||
|
Rel(car, network_conditions, "Reads")
|
||||||
|
Rel(public_transit, network_conditions, "Reads")
|
||||||
|
Rel(generic_mode, network_conditions, "Reads")
|
||||||
|
|
||||||
|
SHOW_DYNAMIC_LEGEND()
|
||||||
|
|
||||||
|
@enduml
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
Simulator
|
||||||
|
=========
|
||||||
|
|
||||||
|
.. uml:: simulator_container.puml
|
||||||
|
:caption: Simulaton Container Diagram
|
||||||
|
:width: 100 %
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
|
||||||
|
pre-day_model
|
||||||
|
within-day_model
|
||||||
|
day-to-day_model
|
||||||
|
events
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
@startuml
|
||||||
|
|
||||||
|
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
|
||||||
|
|
||||||
|
ContainerDb(input_files, "Input Files", "CSV", "Agents characteristics, network description, technical parameters, etc.")
|
||||||
|
ContainerDb(output_files, "Output Files", "CSV", "Aggregate, network-specific and agent-specific results")
|
||||||
|
|
||||||
|
Container_Boundary(simulator, "Simulator") {
|
||||||
|
Container(manager, "Manager", "Python", "Converts input data to Python models, initializes variables, runs the main loop")
|
||||||
|
Container(preday_model, "Pre-Day Model", "Python", "Computes the decisions of each agent, made before the day starts (e.g., departure-time choice, mode choice)")
|
||||||
|
Container(withinday_model, "Within-Day Model", "Python", "Simulates the decisions of each agent and the network conditions through an event-based model")
|
||||||
|
Container(daytoday_model, "Day-to-Day Model", "Python", "Computes the learning process, checks convergence, writes results")
|
||||||
|
ContainerQueue(events, "Events", "Python", "Decisions taken by agents (e.g., departure from origin, boarding and alighting, turn at intersections)")
|
||||||
|
}
|
||||||
|
|
||||||
|
Rel_Up(manager, input_files, "Reads")
|
||||||
|
Rel(manager, preday_model, "Calls")
|
||||||
|
Rel(manager, withinday_model, "Calls")
|
||||||
|
Rel(manager, daytoday_model, "Calls")
|
||||||
|
Rel(preday_model, events, "Writes")
|
||||||
|
Rel(withinday_model, events, "Reads and writes")
|
||||||
|
Rel_Down(daytoday_model, output_files, "Writes")
|
||||||
|
|
||||||
|
SHOW_DYNAMIC_LEGEND()
|
||||||
|
|
||||||
|
@enduml
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
Within-Day Model
|
||||||
|
================
|
||||||
|
|
||||||
|
The within-day model simulates the movements of vehicles and individuals in the network, for a single day.
|
||||||
|
It is an event-based model that triggers event according to the time of the day.
|
||||||
|
|
||||||
|
This page lists all events in MetroSim.
|
||||||
|
|
||||||
|
|
||||||
|
PVReachesNode
|
||||||
|
--------------
|
||||||
|
|
||||||
|
This event signals that a private vehicle reaches a node, at a given time.
|
||||||
|
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
# Configuration file for the Sphinx documentation builder.
|
||||||
|
#
|
||||||
|
# This file only contains a selection of the most common options. For a full
|
||||||
|
# list see the documentation:
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||||
|
|
||||||
|
# -- Path setup --------------------------------------------------------------
|
||||||
|
|
||||||
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
|
#
|
||||||
|
# import os
|
||||||
|
# import sys
|
||||||
|
# sys.path.insert(0, os.path.abspath('.'))
|
||||||
|
|
||||||
|
|
||||||
|
# -- Project information -----------------------------------------------------
|
||||||
|
|
||||||
|
project = 'MetroSim'
|
||||||
|
copyright = '2021, Metropolis Team'
|
||||||
|
author = 'Metropolis Team'
|
||||||
|
version = 'alpha'
|
||||||
|
release = 'alpha'
|
||||||
|
|
||||||
|
# -- General configuration ---------------------------------------------------
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be
|
||||||
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||||
|
# ones.
|
||||||
|
extensions = [
|
||||||
|
'sphinxcontrib.plantuml',
|
||||||
|
'sphinx_rtd_theme',
|
||||||
|
'matplotlib.sphinxext.plot_directive',
|
||||||
|
]
|
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
templates_path = ['_templates']
|
||||||
|
|
||||||
|
# List of patterns, relative to source directory, that match files and
|
||||||
|
# directories to ignore when looking for source files.
|
||||||
|
# This pattern also affects html_static_path and html_extra_path.
|
||||||
|
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||||
|
|
||||||
|
nitpicky = True
|
||||||
|
numfig = True
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output -------------------------------------------------
|
||||||
|
|
||||||
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
|
# a list of builtin themes.
|
||||||
|
#
|
||||||
|
html_theme = 'sphinx_rtd_theme'
|
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
|
html_static_path = ['_static']
|
||||||
|
|
||||||
|
html_theme_options = {
|
||||||
|
'logo_only': False,
|
||||||
|
'display_version': True,
|
||||||
|
'prev_next_buttons_location': 'both',
|
||||||
|
'style_external_links': True,
|
||||||
|
'vcs_pageview_mode': '',
|
||||||
|
# 'style_nav_header_background': 'red',
|
||||||
|
# Toc options
|
||||||
|
'collapse_navigation': False,
|
||||||
|
'sticky_navigation': True,
|
||||||
|
'navigation_depth': 4,
|
||||||
|
'includehidden': True,
|
||||||
|
'titles_only': False
|
||||||
|
}
|
||||||
|
|
||||||
|
plantuml_output_format = 'svg_img'
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for LaTeX output -------------------------------------------------
|
||||||
|
|
||||||
|
latex_theme = 'howto'
|
||||||
|
|
||||||
|
plantuml_latex_output_format = 'pdf'
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
Building
|
||||||
|
========
|
||||||
|
|
||||||
|
To be completed
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
Versioning
|
||||||
|
==========
|
||||||
|
|
||||||
|
To be completed
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
Glossary
|
||||||
|
========
|
||||||
|
|
||||||
|
To be completed
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
To be completed
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
Introduction
|
||||||
|
============
|
||||||
|
|
||||||
|
To be completed
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
Quickstart
|
||||||
|
==========
|
||||||
|
|
||||||
|
To be completed
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
.. MetroSim documentation master file, created by
|
||||||
|
sphinx-quickstart on Wed Feb 10 16:14:39 2021.
|
||||||
|
You can adapt this file completely to your liking, but it should at least
|
||||||
|
contain the root `toctree` directive.
|
||||||
|
|
||||||
|
MetroSim, the simulator for Metropolis: Developer Documentation
|
||||||
|
================================================================
|
||||||
|
|
||||||
|
MetroSim is a dynamic traffic-assignment simulator, developed for Metropolis.
|
||||||
|
|
||||||
|
MetroSim simulates the mode choice, departure-time choice and route choice of commuters in large-scale scenarios.
|
||||||
|
MetroSim can be used in conjunction with the MetroWeb interface or as a standalone program (for debugging and development purposes).
|
||||||
|
|
||||||
|
The present documentation describes precisely how MetroSim works.
|
||||||
|
It is intended at developers willing to improve or extend it.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:caption: Getting Started
|
||||||
|
|
||||||
|
/getting-started/introduction
|
||||||
|
/getting-started/installation
|
||||||
|
/getting-started/quickstart
|
||||||
|
/getting-started/glossary
|
||||||
|
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:caption: Architecture
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
/architecture/overview/overview
|
||||||
|
/architecture/initializer
|
||||||
|
/architecture/simulator/simulator
|
||||||
|
/architecture/road_network/road_network
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:caption: References
|
||||||
|
|
||||||
|
/references/changelog
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:caption: Contributing
|
||||||
|
|
||||||
|
/contributing/building
|
||||||
|
/contributing/versioning
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
@ECHO OFF
|
||||||
|
|
||||||
|
pushd %~dp0
|
||||||
|
|
||||||
|
REM Command file for Sphinx documentation
|
||||||
|
|
||||||
|
if "%SPHINXBUILD%" == "" (
|
||||||
|
set SPHINXBUILD=sphinx-build
|
||||||
|
)
|
||||||
|
set SOURCEDIR=.
|
||||||
|
set BUILDDIR=_build
|
||||||
|
|
||||||
|
if "%1" == "" goto help
|
||||||
|
|
||||||
|
%SPHINXBUILD% >NUL 2>NUL
|
||||||
|
if errorlevel 9009 (
|
||||||
|
echo.
|
||||||
|
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||||
|
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||||
|
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||||
|
echo.may add the Sphinx directory to PATH.
|
||||||
|
echo.
|
||||||
|
echo.If you don't have Sphinx installed, grab it from
|
||||||
|
echo.http://sphinx-doc.org/
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:help
|
||||||
|
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||||
|
|
||||||
|
:end
|
||||||
|
popd
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
from metro1_data import tas, costs
|
||||||
|
|
||||||
|
# Update matplotlib parameters.
|
||||||
|
params = {'text.usetex': True,
|
||||||
|
'figure.dpi': 200,
|
||||||
|
'font.size': 14,
|
||||||
|
'font.serif': [],
|
||||||
|
'font.sans-serif': [],
|
||||||
|
'font.monospace': [],
|
||||||
|
'axes.labelsize': 16,
|
||||||
|
'axes.titlesize': 18,
|
||||||
|
'axes.linewidth': .6,
|
||||||
|
'legend.fontsize': 14,
|
||||||
|
'xtick.labelsize': 12,
|
||||||
|
'ytick.labelsize': 12,
|
||||||
|
'font.family': 'serif'}
|
||||||
|
plt.rcParams.update(params)
|
||||||
|
|
||||||
|
plt.figure(figsize=(7, 5))
|
||||||
|
|
||||||
|
plt.plot(tas, costs, 'o-')
|
||||||
|
|
||||||
|
idx = 3
|
||||||
|
plt.plot([tas[idx], tas[idx+1], tas[idx+1]],
|
||||||
|
[costs[idx], costs[idx], costs[idx+1]],
|
||||||
|
linestyle='dashed', color='black')
|
||||||
|
plt.annotate('$b_i$', ((tas[idx]+tas[idx+1])/2, (costs[idx]+costs[idx+1])/2),
|
||||||
|
va='top', ha='left')
|
||||||
|
|
||||||
|
plt.xlabel('$ta$')
|
||||||
|
plt.ylabel('$c$')
|
||||||
|
plt.xlim(tas[0], tas[-1])
|
||||||
|
# plt.ylim(bottom=0)
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.show()
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
from metro1_data import tas, cum_probs
|
||||||
|
|
||||||
|
# Update matplotlib parameters.
|
||||||
|
params = {'text.usetex': True,
|
||||||
|
'figure.dpi': 200,
|
||||||
|
'font.size': 14,
|
||||||
|
'font.serif': [],
|
||||||
|
'font.sans-serif': [],
|
||||||
|
'font.monospace': [],
|
||||||
|
'axes.labelsize': 16,
|
||||||
|
'axes.titlesize': 18,
|
||||||
|
'axes.linewidth': .6,
|
||||||
|
'legend.fontsize': 14,
|
||||||
|
'xtick.labelsize': 12,
|
||||||
|
'ytick.labelsize': 12,
|
||||||
|
'font.family': 'serif'}
|
||||||
|
plt.rcParams.update(params)
|
||||||
|
|
||||||
|
plt.figure(figsize=(7, 5))
|
||||||
|
|
||||||
|
plt.step(tas, cum_probs, 'o-', color='blue', where='post')
|
||||||
|
|
||||||
|
idx = 3
|
||||||
|
epsilon = (cum_probs[idx] + cum_probs[idx+1]) / 2
|
||||||
|
ta_star = tas[idx+1]
|
||||||
|
plt.plot([tas[0], ta_star], [epsilon, epsilon],
|
||||||
|
linestyle='dashed', color='black')
|
||||||
|
plt.annotate(r'$\epsilon$', (tas[0], epsilon), va='center', ha='right')
|
||||||
|
|
||||||
|
plt.xlabel('$ta$')
|
||||||
|
plt.ylabel('$F$')
|
||||||
|
plt.xlim(tas[0], tas[-1])
|
||||||
|
plt.ylim(bottom=0)
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.show()
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
t_star = 8 + 50/60
|
||||||
|
tas = np.linspace(7.0, 10.0, 10)
|
||||||
|
tts = np.array([15.0, 17.0, 17.0, 21.0, 23.0, 29.0, 27.0, 23.0, 19.0, 15.0])
|
||||||
|
if t_star not in tas:
|
||||||
|
t_star_id = np.argmax(tas > t_star)
|
||||||
|
tas = np.insert(tas, t_star_id, t_star)
|
||||||
|
t_star_tt = (tts[t_star_id-1] + tts[t_star_id]) / 2
|
||||||
|
tts = np.insert(tts, t_star_id, t_star_tt)
|
||||||
|
|
||||||
|
alpha = 15
|
||||||
|
beta = 5
|
||||||
|
gamma = 20
|
||||||
|
schedule_costs = (
|
||||||
|
beta * (t_star - tas) * (t_star > tas)
|
||||||
|
+ gamma * (tas - t_star) * (tas > t_star)
|
||||||
|
)
|
||||||
|
costs = alpha * tts / 60 + schedule_costs
|
||||||
|
|
||||||
|
mu = 1
|
||||||
|
exp_costs = np.exp(-costs / mu)
|
||||||
|
|
||||||
|
probs = list()
|
||||||
|
for i in range(len(tas)-1):
|
||||||
|
m = min(exp_costs[i], exp_costs[i+1])
|
||||||
|
M = max(exp_costs[i], exp_costs[i+1])
|
||||||
|
prob = (tas[i+1] - tas[i]) * (m + (M-m) / 2)
|
||||||
|
probs.append(prob)
|
||||||
|
probs = np.array(probs) / np.sum(probs)
|
||||||
|
probs = np.insert(probs, 0, 0)
|
||||||
|
cum_probs = np.cumsum(probs)
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
from metro1_data import tas, exp_costs
|
||||||
|
|
||||||
|
# Update matplotlib parameters.
|
||||||
|
params = {'text.usetex': True,
|
||||||
|
'figure.dpi': 200,
|
||||||
|
'font.size': 14,
|
||||||
|
'font.serif': [],
|
||||||
|
'font.sans-serif': [],
|
||||||
|
'font.monospace': [],
|
||||||
|
'axes.labelsize': 16,
|
||||||
|
'axes.titlesize': 18,
|
||||||
|
'axes.linewidth': .6,
|
||||||
|
'legend.fontsize': 14,
|
||||||
|
'xtick.labelsize': 12,
|
||||||
|
'ytick.labelsize': 12,
|
||||||
|
'font.family': 'serif'}
|
||||||
|
plt.rcParams.update(params)
|
||||||
|
|
||||||
|
plt.figure(figsize=(7, 5))
|
||||||
|
|
||||||
|
plt.plot(tas, exp_costs, 'o-', color='blue')
|
||||||
|
|
||||||
|
idx = 3
|
||||||
|
plt.fill([tas[idx], tas[idx], tas[idx+1], tas[idx+1]],
|
||||||
|
[0, exp_costs[idx], exp_costs[idx+1], 0],
|
||||||
|
color='blue', alpha=.3)
|
||||||
|
|
||||||
|
plt.xlabel('$ta$')
|
||||||
|
plt.ylabel(r'$\exp(-c/\mu)$')
|
||||||
|
plt.xlim(tas[0], tas[-1])
|
||||||
|
plt.ylim(bottom=0)
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.show()
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
from metro1_data import tas, tts, t_star, t_star_tt
|
||||||
|
|
||||||
|
# Update matplotlib parameters.
|
||||||
|
params = {'text.usetex': True,
|
||||||
|
'figure.dpi': 200,
|
||||||
|
'font.size': 14,
|
||||||
|
'font.serif': [],
|
||||||
|
'font.sans-serif': [],
|
||||||
|
'font.monospace': [],
|
||||||
|
'axes.labelsize': 16,
|
||||||
|
'axes.titlesize': 18,
|
||||||
|
'axes.linewidth': .6,
|
||||||
|
'legend.fontsize': 14,
|
||||||
|
'xtick.labelsize': 12,
|
||||||
|
'ytick.labelsize': 12,
|
||||||
|
'font.family': 'serif'}
|
||||||
|
plt.rcParams.update(params)
|
||||||
|
|
||||||
|
plt.figure(figsize=(7, 5))
|
||||||
|
|
||||||
|
plt.plot(tas, tts, 'o-')
|
||||||
|
|
||||||
|
plt.plot([tas[0], t_star, t_star], [t_star_tt, t_star_tt, 0],
|
||||||
|
linestyle='dashed', color='black')
|
||||||
|
plt.annotate('$t^*$', (t_star, 0), (t_star, -.5), va='top', ha='center')
|
||||||
|
plt.annotate(r'$\hat{tt}$', (tas[0], t_star_tt), (tas[0]-.05, t_star_tt),
|
||||||
|
va='center', ha='right')
|
||||||
|
|
||||||
|
idx = 3
|
||||||
|
plt.plot([tas[idx], tas[idx+1], tas[idx+1]], [tts[idx], tts[idx], tts[idx+1]],
|
||||||
|
linestyle='dashed', color='black')
|
||||||
|
plt.annotate(r'$\delta_i$', ((tas[idx]+tas[idx+1])/2, (tts[idx]+tts[idx+1])/2),
|
||||||
|
va='top', ha='left')
|
||||||
|
|
||||||
|
plt.xlabel('$ta$')
|
||||||
|
plt.ylabel('$tt$')
|
||||||
|
plt.xlim(tas[0], tas[-1])
|
||||||
|
plt.ylim(bottom=0)
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.show()
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
Changelog
|
||||||
|
=========
|
||||||
|
|
||||||
|
To be completed
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
"""The simulator of the Metropolis2 project."""
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
here = pathlib.Path(__file__).parent.resolve()
|
||||||
|
|
||||||
|
# Get the long description from the README file
|
||||||
|
long_description = (here / 'README.txt').read_text(encoding='utf-8')
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='metrosim',
|
||||||
|
version='0.1',
|
||||||
|
description='The simulator of the Metropolis2 project',
|
||||||
|
long_description=long_description,
|
||||||
|
long_description_content_type='text/x-rst',
|
||||||
|
url='https://git.lucasjavaudin.com/LucasJavaudin/MetroSim',
|
||||||
|
author='Lucas Javaudin',
|
||||||
|
author_email='me@lucasjavaudin.com',
|
||||||
|
classifiers=[
|
||||||
|
'Development Status :: 1 - Planning',
|
||||||
|
'Intended Audience :: Developers',
|
||||||
|
'Intended Audience :: Science/Research',
|
||||||
|
'Topic :: Scientific/Engineering',
|
||||||
|
'License :: Other/Proprietary License',
|
||||||
|
'Programming Language :: Python :: 3',
|
||||||
|
'Programming Language :: Python :: 3.9',
|
||||||
|
'Programming Language :: Python :: 3 :: Only',
|
||||||
|
],
|
||||||
|
keywords='transportation, dynamic traffic assignment, metropolis',
|
||||||
|
packages=['metrosim'],
|
||||||
|
python_requires='>=3.9, <4',
|
||||||
|
install_requires=[
|
||||||
|
'numpy',
|
||||||
|
],
|
||||||
|
extras_require={
|
||||||
|
'dev': [],
|
||||||
|
'test': [],
|
||||||
|
},
|
||||||
|
package_data={
|
||||||
|
'metrosim': ['simple_simulation/*'],
|
||||||
|
},
|
||||||
|
project_urls={
|
||||||
|
'Source': 'https://git.lucasjavaudin.com/LucasJavaudin/MetroSim/',
|
||||||
|
'Bug Reports': 'https://git.lucasjavaudin.com/LucasJavaudin/MetroSim/issues',
|
||||||
|
},
|
||||||
|
)
|
||||||
Loading…
Reference in New Issue