Skip to content

Commit

Permalink
Merge pull request #7 from SchweizerischeBundesbahnen/feature/sbb_mod…
Browse files Browse the repository at this point in the history
…el_documenation

mobi plans model documentation
  • Loading branch information
manserpa authored Jun 10, 2021
2 parents 24b887f + b95d583 commit 5a9fd5a
Show file tree
Hide file tree
Showing 11 changed files with 163 additions and 88 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ We are very happy to include any motivated collaborator. Just `clone` or `fork`
- PTV Visum Webinar: [Activity Based Modelling with PTV Visum](https://www.youtube.com/watch?v=HvxDVKPmS-s)
- Scherr, W., Manser, P., Joshi, C., Frischknecht, N., & Métrailler, D. (2020). Towards agent-based travel demand simulation across all mobility choices – the role of balancing preferences and constraints. *European Journal of Transport and Infrastructure Research*, 20(4), 152-172. (https://doi.org/10.18757/ejtir.2020.20.4.4463).
- Scherr, W., Manser, P., Bützberger, P. (2020). SIMBA MOBI: Microscopic Mobility Simulation for Corporate Planning. *Transportation Research Procedia*, 49, 30-43. (https://doi.org/10.1016/j.trpro.2020.09.004).
- Hillel T., Pougala, J., Manser, P., Luethi, R., Scherr, W., Bierlaire, Michel (2020). Modelling mobility tool availability at a household and individual level; A case study of Switzerland. *Proceedings of the 9th Symposium of the European Association for Research in Transportation (HEART)*. (https://transp-or.epfl.ch/heart/2020/abstracts/HEART_2020_paper_138.pdf).
- Hillel T., Pougala, J., Manser, P., Luethi, R., Scherr, W., Bierlaire, M. (2020). Modelling mobility tool availability at a household and individual level; A case study of Switzerland. *Proceedings of the 9th Symposium of the European Association for Research in Transportation (HEART)*. (https://transp-or.epfl.ch/heart/2020/abstracts/HEART_2020_paper_138.pdf).



Expand Down
53 changes: 45 additions & 8 deletions abmvisum/model_contribs/sbb_mobi_plans/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,48 @@
MOBi.plans: SBBs' activity-based demand model
================

Todo: documentation
1. overview scripts
2. statistics
3. activities
4. procedure
5. example parameters

![Example Schedule](docs/abm_procedure.png "Example schedule in Visum")

## Available scripts

Full activity-based demand model procedure ``scripts\simulate_full_mobi_plans.py``, which contains all choice steps that are simulated in MOBi.plans.

Also, the individual choice steps are available as separate scripts:
- ``scripts\ownership_models.py``: simulation of mobility tool ownership choice
- ``scripts\long_term_location_choice.py``: simulation of long-term location choices (e.g., work place)
- ``scripts\plan_building.py``: simulation of activity generation as well as activity duration and destination choice in an iterative procedure based on time budgets
- ``scripts\mode_choice.py``: simulation of a subtour-based mode choice depending on mode-specific impedance parameters
- ``scripts\activity_start_time_assigning.py``: simulation of activity start time choice based on time of day distributions


## Switzerland scenario

MOBi.plans simulates every inhabitant of Switzerland who is older than 5 years. In the existing state (2017), the model has the following size:
- *Zones*: 8'000
- *Locations*: 2.2 Mio.
- *Households*: 3.8 Mio.
- *Persons*: 8.6 Mio.
- *Tours*: 12 Mio.
- *Activity executions*: 37 Mio.


## Activity types

MOBi.plans uses 9 activity types including the *home* and the primary activities *work* and *education* as shown in the following table:

![MOBi.plans activity types](docs/activity_types.png "Activity types as used in MOBi.plans")


## MOBi.plans ABM procedure

The figure below shows the full ABM procedure as simulated in MOBi.plans including all choice models and their segmentations:

![ABM procedure](docs/abm_procedure.png "ABM procedure")


## Choice parameters

SBB does not publish the individual choice parameters. The following two figures show two examples. The first one is a simple discrete discrete choice model (driving license ownership [true,false]), the second is an example for activity duration distributions.

![Simple discrete choice parameters](docs/simple_choice_model.png "Simple discrete choice parameters")

![Activity duration distribution](docs/activity_durations.png "Example Activity duration distribution")
39 changes: 39 additions & 0 deletions abmvisum/model_contribs/sbb_mobi_plans/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,39 @@ def get_UDA_IDs(container):


class Config(object):
"""
Central storage of all necessary parameters. The parameters come from Visum-internal table structures (at
the moment, parameters are defined as POI).
Also, it has the optional possibility to add the skim matrices into a Python cache (as defined in the
Visum Matrix structur).
Parameters:
Visum: Instance of the PTV Visum software.
logging: logger as initialized in the simulator
add_skims_to_cache: Possibility to load all skim matrices as defined in the config into the Python cache.
Attributes:
Visum: Instance of the PTV Visum software.
choice_models: dictionary of all the choice models as defined in the ABM procedure
impedance_expr = impedance expression (depending on activity type)
logging = possibility to log Visum messages
skim_matrices: dictionary cached skim matrices
"""
def __init__(self, Visum, logging, add_skims_to_cache=True):
self.Visum = Visum
self.choice_models = collections.defaultdict(list)
self.impedance_expr = {}
self.logging = logging
self.init_choice_models()

self.skim_matrices = MatrixCache(logging)
if add_skims_to_cache:
self.add_skims_to_cache()

def add_skims_to_cache(self):
"""
Loads the skims which are needed to run SBBs' model into the cache
"""
self.logging.info('loading all skim matrices to cache')
self.skim_matrices.add_skim_to_cache("car_travel_times_sym", VPH.GetMatrixRaw(self.Visum, 11))
self.skim_matrices.add_skim_to_cache("car_net_distance_sym", VPH.GetMatrixRaw(self.Visum, 12))
Expand All @@ -44,6 +66,13 @@ def add_skims_to_cache(self):
"pc_car"))[np.newaxis, :])

def init_choice_models(self):
"""
1. It goes through the Visum ABM procedure table and stores every segment in self.choice_models. Every
choice model can have multiple segments (e.g., age classes). The segments can be freely
customized in the procedure table
2. It saves optional impedance expressions as defined in the POI table. The expression can be functions
of all skims available in the cache.
"""
# get definitions of choice models with segments
poicat_lookup = dict(self.Visum.Net.POICategories.GetMultipleAttributes(['Code', 'No']))
attributes = ['ChoiceModel', 'Specification', 'ID', 'ResAttr', 'Filter', 'AddData', 'Active', 'MaxPlusOne',
Expand Down Expand Up @@ -82,6 +111,10 @@ def init_choice_models(self):
self.logging.info("No impedance params defined or they are corrupt")

def load_choice_para(self, choice_model):
"""
Loads choice parameters for specific model types. The standard case is a discrete choice model with
Betas that depend on a certain attribute.
"""
model_data = self.choice_models[choice_model]

is_destZone_choice = choice_model == "PrimLoc" or choice_model == "DestMajor" or choice_model == "SecDest"
Expand Down Expand Up @@ -153,6 +186,9 @@ def load_choice_para(self, choice_model):
return para

def load_act_dur_para(self, choice_model):
"""
Loads activity duration choice parameters
"""
model_data = self.choice_models[choice_model]

para = []
Expand All @@ -177,6 +213,9 @@ def load_act_dur_para(self, choice_model):
return para

def load_start_times_para(self, choice_model):
"""
Loads activity start time choice parameters
"""
model_data = self.choice_models[choice_model]

para = []
Expand Down
2 changes: 1 addition & 1 deletion abmvisum/model_contribs/sbb_mobi_plans/matrix_cache.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class MatrixCache:
"""
To write.
Possibility to store matrices in a Python cache outside of Visum.
"""

def __init__(self, logging):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
"""
This scripts performs the start time choice model within a Visum procedure.
It contains the following steps of MOBi.plans:
1. start times for each activity
Output are start- (and end-) times assigned to each activity execution and trip
!Important: This scripts resets some important information in the case no valid start times have been found
-> You cannot run it twice, the code will fail!
"""
import importlib
import sys
from pathlib import Path
Expand All @@ -22,6 +11,20 @@
importlib.reload(simulator)

if __name__ == '__main__':
"""
This script performs the start time choice model within a Visum procedure.
It contains the following steps of MOBi.plans:
1. start times for each activity
Output are start- (and end-) times assigned to each activity execution and trip
!Important: This scripts resets some important information in the case no valid start times have been found
-> You cannot run it twice, the code will fail!
Author:
Patrick Manser
"""
start_logging()

abm_simulation = simulator.MOBiPlansSimulator(Visum=Visum, add_skims_to_cache=False)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
"""
This scripts performs the long term location choice model within a Visum procedure.
It contains the following steps of MOBi.plans:
1. location choice with an IPF-algorithm
Output are long-term location keys (pointing to exact coordinates) for persons who are either employed (work place)
or in any kind of education (school)
"""
import importlib
import sys
from pathlib import Path
Expand All @@ -22,6 +13,18 @@
importlib.reload(simulator)

if __name__ == '__main__':
"""
This script performs the long term location choice model within a Visum procedure.
It contains the following steps of MOBi.plans:
1. location choice with an IPF-algorithm
Output are long-term location keys (pointing to exact coordinates) for persons who are either employed (work place)
or in any kind of education (school)
Author:
Patrick Manser
"""
start_logging()

# powerful machine is necessary to run this client, e.g. NALA at SBB
Expand Down
19 changes: 11 additions & 8 deletions abmvisum/model_contribs/sbb_mobi_plans/scripts/mode_choice.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
"""
This scripts performs the mode choice model within a Visum procedure.
It contains the following steps of MOBi.plans:
1. mode choice for tours and subtours
Output are modes assigned to each trip as well as updated travel times for each trip
"""
import importlib
import sys
from pathlib import Path
Expand All @@ -19,6 +11,17 @@
importlib.reload(simulator)

if __name__ == '__main__':
"""
This script performs the mode choice model within a Visum procedure.
It contains the following steps of MOBi.plans:
1. mode choice for tours and subtours
Output are modes assigned to each trip as well as updated travel times for each trip
Author:
Patrick Manser
"""
start_logging()

abm_simulation = simulator.MOBiPlansSimulator(Visum=Visum)
Expand Down
10 changes: 10 additions & 0 deletions abmvisum/model_contribs/sbb_mobi_plans/scripts/ownership_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@
importlib.reload(simulator)

if __name__ == '__main__':
"""
This script simulates mobility tool ownership choices. It contains the following choices:
1. driving license per adult
2. number of cars per household
3. public transport subscription for each person
Author:
Patrick Manser
"""

start_logging()

abm_simulation = simulator.MOBiPlansSimulator(Visum=Visum, add_skims_to_cache=False)
Expand Down
31 changes: 17 additions & 14 deletions abmvisum/model_contribs/sbb_mobi_plans/scripts/plan_building.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,3 @@
"""
This scripts performs the plan adjustment algorithm within a Visum procedure.
It contains the following steps of MOBi.plans:
1. generation models (tour, stop, subtour frequencies)
2. activity type
3. destinations of the secondary activities
4. durations of all activities
5. iterative process based on time budgets (travel, performing, total out-of-home)
Output are mobility plans containing tours and trips (incl. network distance) as well as
activity executions (incl. durations and exact coordinates)
"""

import importlib
import sys
from pathlib import Path
Expand Down Expand Up @@ -40,6 +26,23 @@
importlib.reload(simulator)

if __name__ == '__main__':
"""
This script performs the plan-building algorithm within a Visum procedure.
It contains the following steps of MOBi.plans:
1. generation models (tour, stop, subtour frequencies)
2. activity type
3. destinations of the secondary activities
4. durations of all activities
5. iterative process based on time budgets (travel, performing, total out-of-home)
Output are mobility plans containing tours and trips (incl. network distance) as well as
activity executions (incl. durations and exact coordinates)
Author:
Patrick Manser
"""

start_logging()

# powerful machine is necessary to run this client, e.g. NALA at SBB
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@
importlib.reload(simulator)

if __name__ == '__main__':
"""
This script performs all choice steps to simulate a full activity-based model. More information
about the individual steps can be found in the separate scripts.
Output is schedule for each person which is very precise and consistent in time and space.
Author:
Patrick Manser
"""

start_logging()

# powerful machine is necessary to run this client, e.g. NALA at SBB
Expand All @@ -36,6 +46,8 @@

abm_simulation = simulator.MOBiPlansSimulator(Visum=Visum)

abm_simulation.ownership_models()

abm_simulation.long_term_location_choice()

abm_simulation.plan_generation(time_budget_dict=time_budget_dict, out_of_home_budget=18.0)
Expand Down
37 changes: 1 addition & 36 deletions abmvisum/model_contribs/sbb_mobi_plans/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from .core.tour_frequency_choice import run_tour_frequency_choice
from .matrix_cache import MatrixCache

importlib.reload(choice_engine) # prevents Visum from caching methods
importlib.reload(choice_engine) # prevents Visum from caching imports


class MOBiPlansSimulator(StreamHandler):
Expand Down Expand Up @@ -238,41 +238,6 @@ def start_time_choice(self):
segments = self.config.load_start_times_para('StartTime')
run_start_time_choice(segments, self.Visum, logging, self.rand)

def railaccess_choiceset(self):
logging.info('--- railaccess choice set ---')
segments = self.config.load_choice_para('RailAccess')
subjects = self.Visum.Net.Persons
activities = sorted(list(set(self.Visum.Net.Activities.GetMultipleAttributes(["Name"]))))

for segment in segments:
logging.info(f'{segment["Specification"]}->{segment["ResAttr"]}')
betas = [b for (a, b) in zip(segment['AttrExpr'], segment['Beta']) if a != '0']
att_expr = [a for a in segment['AttrExpr'] if a != '0']
betas_act = [b for (a, b) in zip(segment['AttrExpr'], segment['Beta']) if a == '0']
acts = [b for (a, b) in zip(segment['AttrExpr'], segment['Comments']) if a == '0']
filtered_subjects = utilities.get_filtered_subjects(subjects, segment['Filter'])
if segment['ResAttr'][-4:] == "_act":
choice_per_subject = [0] * len(filtered_subjects)
for (activity, beta) in zip(acts, betas_act):
assert activity in activities, f"{segment['Specification']}, activity {activity} not defined"
bit = 2 ** activities.index(activity)
choice_per_subject = (choice_per_subject +
choice_engine.calc_binary_probabilistic_choice_per_subject(filtered_subjects,
['1'], [beta[0]],
segment['Choices'],
self.rand) * bit)
else:
choice_per_subject = choice_engine.calc_binary_probabilistic_choice_per_subject(filtered_subjects,
att_expr,
[b[0] for b in betas],
segment['Choices'],
self.rand)
result_attr = segment['ResAttr']
filtered_subjects.SetAllAttValues(result_attr, 0)
utilities.SetMulti(filtered_subjects, result_attr, choice_per_subject)

logging.info('%s set for %d objects' % (result_attr, len(choice_per_subject)))

def ownership_models(self):
logging.info('--- mobility tool ownership models ---')
segments = self.config.load_choice_para('MobilityTools')
Expand Down

0 comments on commit 5a9fd5a

Please sign in to comment.