From b95d583a7b7e10987c8d38112207f9cf8126608b Mon Sep 17 00:00:00 2001 From: "Manser Patrick (MP-FV-APL-MEP)" Date: Thu, 10 Jun 2021 14:55:52 +0200 Subject: [PATCH] mobi plans model documentation --- README.md | 2 +- .../model_contribs/sbb_mobi_plans/README.md | 53 ++++++++++++++++--- .../model_contribs/sbb_mobi_plans/config.py | 39 ++++++++++++++ .../sbb_mobi_plans/matrix_cache.py | 2 +- .../scripts/activity_start_time_assigning.py | 25 +++++---- .../scripts/long_term_location_choice.py | 21 ++++---- .../sbb_mobi_plans/scripts/mode_choice.py | 19 ++++--- .../scripts/ownership_models.py | 10 ++++ .../sbb_mobi_plans/scripts/plan_building.py | 31 ++++++----- .../scripts/simulate_full_mobi_plans.py | 12 +++++ .../sbb_mobi_plans/simulator.py | 37 +------------ 11 files changed, 163 insertions(+), 88 deletions(-) diff --git a/README.md b/README.md index 6f5ada1..79fcddb 100644 --- a/README.md +++ b/README.md @@ -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). diff --git a/abmvisum/model_contribs/sbb_mobi_plans/README.md b/abmvisum/model_contribs/sbb_mobi_plans/README.md index fbea90b..1cdc660 100644 --- a/abmvisum/model_contribs/sbb_mobi_plans/README.md +++ b/abmvisum/model_contribs/sbb_mobi_plans/README.md @@ -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") diff --git a/abmvisum/model_contribs/sbb_mobi_plans/config.py b/abmvisum/model_contribs/sbb_mobi_plans/config.py index bc02297..4e490d4 100644 --- a/abmvisum/model_contribs/sbb_mobi_plans/config.py +++ b/abmvisum/model_contribs/sbb_mobi_plans/config.py @@ -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)) @@ -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', @@ -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" @@ -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 = [] @@ -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 = [] diff --git a/abmvisum/model_contribs/sbb_mobi_plans/matrix_cache.py b/abmvisum/model_contribs/sbb_mobi_plans/matrix_cache.py index f700d74..2d88616 100644 --- a/abmvisum/model_contribs/sbb_mobi_plans/matrix_cache.py +++ b/abmvisum/model_contribs/sbb_mobi_plans/matrix_cache.py @@ -1,6 +1,6 @@ class MatrixCache: """ - To write. + Possibility to store matrices in a Python cache outside of Visum. """ def __init__(self, logging): diff --git a/abmvisum/model_contribs/sbb_mobi_plans/scripts/activity_start_time_assigning.py b/abmvisum/model_contribs/sbb_mobi_plans/scripts/activity_start_time_assigning.py index aaf2cf4..98d6039 100644 --- a/abmvisum/model_contribs/sbb_mobi_plans/scripts/activity_start_time_assigning.py +++ b/abmvisum/model_contribs/sbb_mobi_plans/scripts/activity_start_time_assigning.py @@ -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 @@ -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) diff --git a/abmvisum/model_contribs/sbb_mobi_plans/scripts/long_term_location_choice.py b/abmvisum/model_contribs/sbb_mobi_plans/scripts/long_term_location_choice.py index 579e004..0d656a7 100644 --- a/abmvisum/model_contribs/sbb_mobi_plans/scripts/long_term_location_choice.py +++ b/abmvisum/model_contribs/sbb_mobi_plans/scripts/long_term_location_choice.py @@ -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 @@ -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 diff --git a/abmvisum/model_contribs/sbb_mobi_plans/scripts/mode_choice.py b/abmvisum/model_contribs/sbb_mobi_plans/scripts/mode_choice.py index 2660533..58d3bcd 100644 --- a/abmvisum/model_contribs/sbb_mobi_plans/scripts/mode_choice.py +++ b/abmvisum/model_contribs/sbb_mobi_plans/scripts/mode_choice.py @@ -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 @@ -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) diff --git a/abmvisum/model_contribs/sbb_mobi_plans/scripts/ownership_models.py b/abmvisum/model_contribs/sbb_mobi_plans/scripts/ownership_models.py index bfd3712..5e269e8 100644 --- a/abmvisum/model_contribs/sbb_mobi_plans/scripts/ownership_models.py +++ b/abmvisum/model_contribs/sbb_mobi_plans/scripts/ownership_models.py @@ -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) diff --git a/abmvisum/model_contribs/sbb_mobi_plans/scripts/plan_building.py b/abmvisum/model_contribs/sbb_mobi_plans/scripts/plan_building.py index 2fa829b..3c18f55 100644 --- a/abmvisum/model_contribs/sbb_mobi_plans/scripts/plan_building.py +++ b/abmvisum/model_contribs/sbb_mobi_plans/scripts/plan_building.py @@ -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 @@ -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 diff --git a/abmvisum/model_contribs/sbb_mobi_plans/scripts/simulate_full_mobi_plans.py b/abmvisum/model_contribs/sbb_mobi_plans/scripts/simulate_full_mobi_plans.py index 2c46faa..02e8d7f 100644 --- a/abmvisum/model_contribs/sbb_mobi_plans/scripts/simulate_full_mobi_plans.py +++ b/abmvisum/model_contribs/sbb_mobi_plans/scripts/simulate_full_mobi_plans.py @@ -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 @@ -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) diff --git a/abmvisum/model_contribs/sbb_mobi_plans/simulator.py b/abmvisum/model_contribs/sbb_mobi_plans/simulator.py index 7a4fddd..e251449 100644 --- a/abmvisum/model_contribs/sbb_mobi_plans/simulator.py +++ b/abmvisum/model_contribs/sbb_mobi_plans/simulator.py @@ -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): @@ -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')