From df2738a1b977f1316f2b9fcc6bb5bd38e73d59df Mon Sep 17 00:00:00 2001 From: Kai Nagel Date: Thu, 13 Apr 2023 10:41:08 +0200 Subject: [PATCH 001/106] suggestion for first steps --- .../ParkingCorrectionMultithreadedModule.java | 47 ++++++++++++++++ .../run/SubtourModeChoiceInclParking.java | 54 +++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 src/main/java/org/matsim/run/ParkingCorrectionMultithreadedModule.java create mode 100644 src/main/java/org/matsim/run/SubtourModeChoiceInclParking.java diff --git a/src/main/java/org/matsim/run/ParkingCorrectionMultithreadedModule.java b/src/main/java/org/matsim/run/ParkingCorrectionMultithreadedModule.java new file mode 100644 index 00000000..f5300144 --- /dev/null +++ b/src/main/java/org/matsim/run/ParkingCorrectionMultithreadedModule.java @@ -0,0 +1,47 @@ +package org.matsim.run; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.core.config.groups.GlobalConfigGroup; +import org.matsim.core.population.algorithms.PlanAlgorithm; +import org.matsim.core.replanning.modules.AbstractMultithreadedModule; +import org.matsim.core.router.TripStructureUtils; + +import java.util.List; + +class ParkingCorrectionMultithreadedModule extends AbstractMultithreadedModule { + private static final Logger log = LogManager.getLogger(ParkingCorrectionMultithreadedModule.class ); + + public ParkingCorrectionMultithreadedModule( GlobalConfigGroup globalConfigGroup ){ + super( globalConfigGroup ); + } + @Override public PlanAlgorithm getPlanAlgoInstance(){ + return new PlanAlgorithm(){ + @Override public void run( Plan plan ){ + + List trips = TripStructureUtils.getTrips( plan.getPlanElements() ); + + for( TripStructureUtils.Trip trip : trips ){ + if ( isRouted(trip ) ) { + continue;; + } + // else check if parking is affected etc. + + trip.getOriginActivity(); + + trip.getDestinationActivity(); + + Plan newPlan = null ; + + // ... + + + plan = newPlan ; /// does not work + + } + + } + }; + } +} diff --git a/src/main/java/org/matsim/run/SubtourModeChoiceInclParking.java b/src/main/java/org/matsim/run/SubtourModeChoiceInclParking.java new file mode 100644 index 00000000..d169ec9e --- /dev/null +++ b/src/main/java/org/matsim/run/SubtourModeChoiceInclParking.java @@ -0,0 +1,54 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * copyright : (C) 2008 by the members listed in the COPYING, * + * LICENSE and WARRANTY file. * + * email : info at matsim dot org * + * * + * *********************************************************************** * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * See also COPYING, LICENSE and WARRANTY file * + * * + * *********************************************************************** */ + +package org.matsim.run; + +import javax.inject.Inject; +import javax.inject.Provider; +import org.matsim.core.config.groups.GlobalConfigGroup; +import org.matsim.core.config.groups.SubtourModeChoiceConfigGroup; +import org.matsim.core.population.algorithms.PermissibleModesCalculator; +import org.matsim.core.replanning.PlanStrategy; +import org.matsim.core.replanning.PlanStrategyImpl; +import org.matsim.core.replanning.PlanStrategyImpl.Builder; +import org.matsim.core.replanning.modules.ReRoute; +import org.matsim.core.replanning.selectors.RandomPlanSelector; +import org.matsim.core.router.TripRouter; +import org.matsim.core.utils.timing.TimeInterpretation; +import org.matsim.facilities.ActivityFacilities; + +public class SubtourModeChoiceInclParking implements Provider { + + @Inject private Provider tripRouterProvider; + @Inject private GlobalConfigGroup globalConfigGroup; + @Inject private SubtourModeChoiceConfigGroup subtourModeChoiceConfigGroup; + @Inject private ActivityFacilities facilities; + @Inject private PermissibleModesCalculator permissibleModesCalculator; + @Inject private TimeInterpretation timeInterpretation; + + @Override + public PlanStrategy get() { + PlanStrategyImpl.Builder builder = new Builder(new RandomPlanSelector<>()); + builder.addStrategyModule(new org.matsim.core.replanning.modules.SubtourModeChoice(globalConfigGroup, subtourModeChoiceConfigGroup, permissibleModesCalculator)); + builder.addStrategyModule( new ParkingCorrectionMultithreadedModule( globalConfigGroup ) ); + builder.addStrategyModule(new ReRoute(facilities, tripRouterProvider, globalConfigGroup, timeInterpretation)); + return builder.build(); + } + +} From 5c4b0e30eaa522cf7c9aad570290ed57287463b0 Mon Sep 17 00:00:00 2001 From: Kai Nagel Date: Thu, 13 Apr 2023 10:53:18 +0200 Subject: [PATCH 002/106] suggestion for first steps --- .../ParkingCorrectionMultithreadedModule.java | 42 +++++++++++++++---- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/matsim/run/ParkingCorrectionMultithreadedModule.java b/src/main/java/org/matsim/run/ParkingCorrectionMultithreadedModule.java index f5300144..c8bd780b 100644 --- a/src/main/java/org/matsim/run/ParkingCorrectionMultithreadedModule.java +++ b/src/main/java/org/matsim/run/ParkingCorrectionMultithreadedModule.java @@ -2,19 +2,28 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.TransportMode; import org.matsim.api.core.v01.population.Plan; +import org.matsim.api.core.v01.population.PlanElement; +import org.matsim.api.core.v01.population.Population; +import org.matsim.api.core.v01.population.PopulationFactory; import org.matsim.core.config.groups.GlobalConfigGroup; +import org.matsim.core.population.PopulationUtils; import org.matsim.core.population.algorithms.PlanAlgorithm; import org.matsim.core.replanning.modules.AbstractMultithreadedModule; +import org.matsim.core.router.TripRouter; import org.matsim.core.router.TripStructureUtils; +import java.util.ArrayList; import java.util.List; class ParkingCorrectionMultithreadedModule extends AbstractMultithreadedModule { private static final Logger log = LogManager.getLogger(ParkingCorrectionMultithreadedModule.class ); + private final PopulationFactory pf; - public ParkingCorrectionMultithreadedModule( GlobalConfigGroup globalConfigGroup ){ + public ParkingCorrectionMultithreadedModule( GlobalConfigGroup globalConfigGroup, PopulationFactory pf ){ super( globalConfigGroup ); + this.pf = pf; } @Override public PlanAlgorithm getPlanAlgoInstance(){ return new PlanAlgorithm(){ @@ -22,22 +31,39 @@ public ParkingCorrectionMultithreadedModule( GlobalConfigGroup globalConfigGroup List trips = TripStructureUtils.getTrips( plan.getPlanElements() ); - for( TripStructureUtils.Trip trip : trips ){ - if ( isRouted(trip ) ) { + for( TripStructureUtils.Trip oldTrip : trips ){ + if ( isRouted(oldTrip ) ) { continue;; } // else check if parking is affected etc. - trip.getOriginActivity(); + oldTrip.getOriginActivity(); - trip.getDestinationActivity(); + oldTrip.getDestinationActivity(); - Plan newPlan = null ; - // ... + List newTrip = new ArrayList<>(); +// newTrip.add( oldTrip.getOriginActivity() ); - plan = newPlan ; /// does not work + newTrip.add( pf.createLeg( TransportMode.walk ) ); + + newTrip.add( pf.createInteractionActivityFromLinkId( ... ) ); + + newTrip .add( pf.createLeg( TransportMode.car ); + + newTrip.add( pf.createInteractionActivityFromLinkId( ... ) ); + + newTrip.add( pf.createLeg( )) + +// newTrip.add( oldTrip.getDestinationActivity() ); + + + TripRouter.insertTrip( + plan, + oldTrip.getOriginActivity(), + newTrip, + oldTrip.getDestinationActivity() ); } From 22bb72f1da9836324512fa19fad4aa64a29ffb11 Mon Sep 17 00:00:00 2001 From: simei94 Date: Thu, 13 Apr 2023 17:52:50 +0200 Subject: [PATCH 003/106] filter network --- .../run/LinkAttributeNetworkLinkFilter.java | 25 +++++++++++++ .../ParkingCorrectionMultithreadedModule.java | 37 ++++++++++++++++--- 2 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/matsim/run/LinkAttributeNetworkLinkFilter.java diff --git a/src/main/java/org/matsim/run/LinkAttributeNetworkLinkFilter.java b/src/main/java/org/matsim/run/LinkAttributeNetworkLinkFilter.java new file mode 100644 index 00000000..6ec13354 --- /dev/null +++ b/src/main/java/org/matsim/run/LinkAttributeNetworkLinkFilter.java @@ -0,0 +1,25 @@ +package org.matsim.run; + +import org.matsim.api.core.v01.network.Link; +import org.matsim.core.network.filter.NetworkLinkFilter; + +public class LinkAttributeNetworkLinkFilter implements NetworkLinkFilter { + + private final String attributeName; + private final String attribute; + + LinkAttributeNetworkLinkFilter(String attributeName, String attribute) { + + this.attributeName = attributeName; + this.attribute = attribute; + } + + + @Override + public boolean judgeLink(Link l) { + + + + return false; + } +} diff --git a/src/main/java/org/matsim/run/ParkingCorrectionMultithreadedModule.java b/src/main/java/org/matsim/run/ParkingCorrectionMultithreadedModule.java index c8bd780b..7bcead2e 100644 --- a/src/main/java/org/matsim/run/ParkingCorrectionMultithreadedModule.java +++ b/src/main/java/org/matsim/run/ParkingCorrectionMultithreadedModule.java @@ -1,29 +1,54 @@ package org.matsim.run; +import com.google.inject.Inject; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.population.Plan; import org.matsim.api.core.v01.population.PlanElement; -import org.matsim.api.core.v01.population.Population; import org.matsim.api.core.v01.population.PopulationFactory; import org.matsim.core.config.groups.GlobalConfigGroup; -import org.matsim.core.population.PopulationUtils; +import org.matsim.core.network.filter.NetworkFilterManager; import org.matsim.core.population.algorithms.PlanAlgorithm; import org.matsim.core.replanning.modules.AbstractMultithreadedModule; import org.matsim.core.router.TripRouter; import org.matsim.core.router.TripStructureUtils; +import playground.vsp.simpleParkingCostHandler.ParkingCostConfigGroup; import java.util.ArrayList; import java.util.List; class ParkingCorrectionMultithreadedModule extends AbstractMultithreadedModule { + + @Inject + Scenario scenario; + + @Inject + ParkingCostConfigGroup parkingCostConfigGroup; + + private static final Logger log = LogManager.getLogger(ParkingCorrectionMultithreadedModule.class ); private final PopulationFactory pf; public ParkingCorrectionMultithreadedModule( GlobalConfigGroup globalConfigGroup, PopulationFactory pf ){ super( globalConfigGroup ); this.pf = pf; + + NetworkFilterManager managerResidential = new NetworkFilterManager(scenario.getNetwork(), scenario.getConfig().network()); + managerResidential.addLinkFilter(new LinkAttributeNetworkLinkFilter(parkingCostConfigGroup.getResidentialParkingFeeAttributeName(), "0.")); + + Network nonResidentialParkingNetwork = managerResidential.applyFilters(); + + NetworkFilterManager managerShop = new NetworkFilterManager(scenario.getNetwork(), scenario.getConfig().network()); + //this is kind of ugly, but otherwise we would need a shp file here to filter for the links with shopping garages -sme0423 + //maybe put this into a facility instead of networkAttribute + managerShop.addLinkFilter(new LinkAttributeNetworkLinkFilter("shoppingGarage", "true")); + + Network shoppingNetwork = managerShop.applyFilters(); + + } @Override public PlanAlgorithm getPlanAlgoInstance(){ return new PlanAlgorithm(){ @@ -32,9 +57,11 @@ public ParkingCorrectionMultithreadedModule( GlobalConfigGroup globalConfigGroup List trips = TripStructureUtils.getTrips( plan.getPlanElements() ); for( TripStructureUtils.Trip oldTrip : trips ){ - if ( isRouted(oldTrip ) ) { - continue;; - } + //we dont need to check if the trip is routed because only plans affected by SMC will come here + // -> only plans with non-routed trips +// if ( isRouted(oldTrip ) ) { +// continue;; +// } // else check if parking is affected etc. oldTrip.getOriginActivity(); From a144c12984ba500bcc376e4ed84a5f06e91e27f4 Mon Sep 17 00:00:00 2001 From: rakow Date: Mon, 17 Apr 2023 12:30:37 +0200 Subject: [PATCH 004/106] Add examples dependency --- pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pom.xml b/pom.xml index c9c9e3e9..6e890c44 100644 --- a/pom.xml +++ b/pom.xml @@ -98,6 +98,13 @@ test + + org.matsim + matsim-examples + ${matsim.version} + test + + org.matsim.contrib drt From fba9c3e14109ccbff3d006a7bf5ccdf995d2240e Mon Sep 17 00:00:00 2001 From: mkreuschner <64031262+mkreuschner@users.noreply.github.com> Date: Thu, 13 Apr 2023 10:20:36 +0200 Subject: [PATCH 005/106] Add files via upload --- src/main/R/masteranalyse.R | 767 ++++++++++++++++++------------------- src/main/R/masterscript.R | 136 ++++--- 2 files changed, 450 insertions(+), 453 deletions(-) diff --git a/src/main/R/masteranalyse.R b/src/main/R/masteranalyse.R index 39f26418..41224397 100644 --- a/src/main/R/masteranalyse.R +++ b/src/main/R/masteranalyse.R @@ -1,125 +1,336 @@ #### reading shp files #### -RegionShape <- st_read(region_shp_path, crs=25832) #study area -CityShape <- st_read(city_shp_path, crs=25832) #city of Leipzig -#ScenarioShape <- st_read(scenario_shp_path, crs=25832)#scenario area +RegionShape <- st_read(region_shp_path, crs=CRS) #study area +CityShape <- st_read(city_shp_path, crs=CRS) #city of Leipzig +AreaShape <- st_read(area_shp_path, crs=CRS)#scenario area print("#### Shapes geladen! ####") #### reading trips/legs files #### -## Trips Files -baseTripsTable <- readTripsTable(pathToMATSimOutputDirectory = base_trips_path) -scenarioTripsTable <- readTripsTable(pathToMATSimOutputDirectory = scenario_trips_path) +## Trip File +scenarioTripsTable <- readTripsTable(pathToMATSimOutputDirectory = paste0(scenario_run_path,list.files(path = scenario_run_path, pattern = "output_trips"))) print("#### Trips geladen! ####") -## Legs Files -baseLegsTable <- read_delim(base_legs_path, delim= ";", n_max = 3000 ) -scenariolegsTable <- read_delim(scenario_legs_path, delim= ";", n_max = 3000) +## Leg Files +scenariolegsTable <- read_delim(paste0(scenario_run_path,list.files(path = scenario_run_path, pattern = "output_legs")), delim= ";", n_max = 3000) print("#### Legs geladen! ####") ## Filters -base_trips_region <- filterByRegion(baseTripsTable,RegionShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -base_trips_city <- filterByRegion(baseTripsTable,CityShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -scenario_trips_region <- filterByRegion(scenarioTripsTable,RegionShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -scenario_trips_city <- filterByRegion(scenarioTripsTable,CityShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) - +scenario_trips_region <- filterByRegion(scenarioTripsTable,RegionShape,crs=CRS,start.inshape = TRUE,end.inshape = TRUE) +scenario_trips_city <- filterByRegion(scenarioTripsTable,CityShape,crs=CRS,start.inshape = TRUE,end.inshape = TRUE) +scenario_trips_area <- filterByRegion(scenarioTripsTable,AreaShape,crs=CRS,start.inshape = TRUE,end.inshape = TRUE) print("#### Trips gefiltert! ####") - -base_legs_region <- filterByRegion(baseLegsTable,RegionShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -base_legs_city <- filterByRegion(baseLegsTable,CityShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -scenario_legs_region <- filterByRegion(scenariolegsTable,RegionShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -scenario_legs_city <- filterByRegion(scenariolegsTable,CityShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) - +scenario_legs_region <- filterByRegion(scenariolegsTable,RegionShape,crs=CRS,start.inshape = TRUE,end.inshape = TRUE) +scenario_legs_city <- filterByRegion(scenariolegsTable,CityShape,crs=CRS,start.inshape = TRUE,end.inshape = TRUE) +scenario_legs_area <- filterByRegion(scenariolegsTable,AreaShape,crs=CRS,start.inshape = TRUE,end.inshape = TRUE) print("#### Legs gefiltert! ####") #### reading persons #### - -base_persons <- read_delim(base_persons_path, delim= ";") -scenario_persons <- read_delim(scenario_persons_path, delim= ";") +scenario_persons <- read_delim(paste0(scenario_run_path,list.files(path = scenario_run_path, pattern = "output_persons")), delim = ";") #### 0. Parameters #### #BREAKING DIFFERENT DISTANCES IN M breaks = c(0, 1000, 2000, 5000, 10000, 20000, Inf) - +breaks2 = c(0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000, 11000, 12000, 13000, 14000, 15000, 16000, 17000, 18000, 19000, 20000, Inf) #NAMES OF THE CASES cases <- c("base", "scenario") -#### #1.1 Average Distance - trips based#### +#### #1.1 Modal Split - trips based - main mode (count) #### + +if (x_ms_trips_count == 1){ + + modal_split_trips_main_mode <- function(x){ + x %>% + count(main_mode) %>% + mutate(percent = 100*n/sum(n)) + } + + ms_main_mode_trips_scenarioCity <- modal_split_trips_main_mode(scenario_trips_city) + ms_main_mode_trips_ScenarioCity <- t(ms_main_mode_trips_scenarioCity) + colnames(ms_main_mode_trips_ScenarioCity) <- ms_main_mode_trips_ScenarioCity[1, ] + write.csv(ms_main_mode_trips_ScenarioCity,file = paste0(outputDirectoryScenario,"/ms_main_mode_trips_city.modestats.csv")) + + ms_main_mode_trips_scenarioRegion <- modal_split_trips_main_mode(scenario_trips_region) + ms_main_mode_trips_ScenarioRegion <- t(ms_main_mode_trips_scenarioRegion) + colnames(ms_main_mode_trips_ScenarioRegion) <- ms_main_mode_trips_ScenarioRegion[1, ] + write.csv(ms_main_mode_trips_scenarioRegion,file = paste0(outputDirectoryScenario,"/ms_main_mode_trips_region.modestats.csv")) +} + +#### #1.2 Modal Split - trips based - distance #### +if (x_ms_trips_distance == 1){ + modal_split_trips_distance <- function(x){ + x %>% + group_by(main_mode) %>% + summarise(distance = sum(traveled_distance)) %>% + mutate(percent = round(100*distance/sum(distance),2)) + } + ms_dist_trips_scenarioCity <- modal_split_trips_distance(scenario_trips_city) + ms_dist_trips_ScenarioCity <- t(ms_dist_trips_scenarioCity) + colnames(ms_dist_trips_ScenarioCity) <- ms_dist_trips_ScenarioCity[1, ] + write.csv(ms_dist_trips_ScenarioCity,file = paste0(outputDirectoryScenario,"/ms_dist_trips_city.modestats.csv")) + + ms_dist_trips_scenarioRegion <- modal_split_trips_distance(scenario_trips_region) + ms_dist_trips_ScenarioRegion <- t(ms_dist_trips_scenarioRegion) + colnames(ms_dist_trips_ScenarioRegion) <- ms_dist_trips_ScenarioRegion[1, ] + write.csv(ms_dist_trips_scenarioRegion,file = paste0(outputDirectoryScenario,"/ms_dist_trips_region.modestats.csv")) +} + + +#### #1.3 Modal Split - legs based - main mode (count) #### +if (x_ms_legs_count == 1){ + modal_split_legs_mode <- function(x){ + x %>% + mutate(distance_cut = cut(distance, breaks = breaks, + labels = c("<1000m", "1 - 2km", "2 - 5km", "5 - 10km", "10 - 20km", ">20km" ))) %>% + group_by(distance_cut) %>% + count(mode) %>% + mutate(percent = 100*n/sum(n)) + } + ms_mode_legs_scenarioCity <- modal_split_legs_mode(scenario_trips_city) + ms_mode_legs_ScenarioCity <- t(ms_mode_legs_scenarioCity) + colnames(ms_mode_legs_ScenarioCity) <- ms_mode_legs_ScenarioCity[1, ] + write.csv(ms_mode_legs_ScenarioCity,file = paste0(outputDirectoryScenario,"/ms_mode_legs_city.modestats.csv")) + + ms_mode_legs_scenarioRegion <- modal_split_legs_mode(scenario_trips_region) + ms_mode_legs_ScenarioRegion <- t(ms_mode_legs_scenarioRegion) + colnames(ms_mode_legs_ScenarioRegion) <- ms_mode_legs_ScenarioRegion[1, ] + write.csv(ms_mode_legs_scenarioRegion,file = paste0(outputDirectoryScenario,"/ms_mode_legs_region.modestats.csv")) +} + +#### #1.4 Modal Split - legs based - distance #### +if (x_ms_legs_distance == 1){ + modal_split_legs_distance <- function(x){ + x %>% + group_by(mode) %>% + summarise(distance = sum(distance)) %>% + mutate(percent = round(100*distance/sum(distance),2)) + } + ms_dist_legs_scenarioCity <- modal_split_legs_distance(scenario_trips_city) + ms_dist_legs_ScenarioCity <- t(ms_dist_legs_scenarioCity) + colnames(ms_dist_legs_ScenarioCity) <- ms_dist_legs_ScenarioCity[1, ] + write.csv(ms_dist_legs_ScenarioCity,file = paste0(outputDirectoryScenario,"/ms_dist_legs_city.modestats.csv")) + + ms_dist_legs_scenarioRegion <- modal_split_legs_distance(scenario_trips_region) + ms_dist_legs_ScenarioRegion <- t(ms_dist_legs_scenarioRegion) + colnames(ms_dist_legs_ScenarioRegion) <- ms_dist_legs_ScenarioRegion[1, ] + write.csv(ms_dist_legs_scenarioRegion,file = paste0(outputDirectoryScenario,"/ms_dist_legs_region.modestats.csv")) +} + +#### #2.1 Sankey Modal Shift #### + +if (x_sankey_diagram == 1){ + sankey_dataframe <- function(x, y){ + inner_join(x, y, by = "trip_id") %>% + select(trip_id, main_mode.x, longest_distance_mode.x, main_mode.y, longest_distance_mode.y) %>% + group_by(main_mode.x, main_mode.y) %>% + summarise(Freq = n()) + } + + #Base Case > Policy Case CITY + Base_city_to_Scenario_city <- sankey_dataframe(base_trips_city, scenario_trips_city) + + sankey_city <- alluvial(Base_city_to_Scenario_city[1:2], + freq= Base_case_city_to_Scenario_city$Freq, + border = NA, + axis_labels = c("Base Case", "Scenario Case")) + + sankey_city <- as_tibble(t(sankey_city)) + write.csv(sankey_city, file = paste0(outputDirectoryBase,"/sankey_city.csv")) + + #Base Case > Policy Case REGION + Base_city_to_Scenario_city <- sankey_dataframe(base_trips_region, scenario_trips_region) + + sankey_region <- alluvial(Base_region_to_Scenario_region[1:2], + freq= Base_case_region_to_Scenario_region$Freq, + border = NA, + axis_labels = c("Base Case", "Scenario Case")) + + sankey_region <- as_tibble(t(sankey_region)) + write.csv(sankey_region, file = paste0(outputDirectoryBase,"/sankey_region.csv")) +} + +#### #3.1 Average Traveled Distance - trips based#### if (x_average_traveled_distance_trips == 1){ + + avg_trav_distance_trips_by_mode <- function(x){ + x %>% + group_by(main_mode) %>% + summarise_at(vars(traveled_distance), list(name=mean)) + } #calculation - avg_trav_dist_trips_base_region <- mean(base_trips_region$traveled_distance) - avg_trav_dist_trips_base_city <- mean(base_trips_city$traveled_distance) - avg_trav_dist_trips_scenario_region <- mean(scenario_trips_region$traveled_distance) - avg_trav_dist_trips_scenario_city <- mean(scenario_trips_city$traveled_distance) - avg_eucl_dist_trips_base_region <- mean(base_trips_region$euclidean_distance) - avg_eucl_dist_trips_base_city <- mean(base_trips_city$euclidean_distance) - avg_eucl_dist_trips_scenario_region <- mean(scenario_trips_region$euclidean_distance) - avg_eucl_dist_trips_scenario_city <- mean(scenario_trips_city$euclidean_distance) - - #join to one table - avg_dist_trips_city <- c(avg_trav_dist_trips_base_city,avg_trav_dist_trips_scenario_city, - avg_eucl_dist_trips_base_city,avg_eucl_dist_trips_scenario_city) - - avg_dist_trips_city <- data.frame(cases, avg_dist_trips_city) - avg_dist_trips_region <- c(avg_trav_dist_trips_base_region,avg_trav_dist_trips_scenario_region, - avg_eucl_dist_trips_base_region,avg_eucl_dist_trips_scenario_region) - - avg_dist_trips_region <- data.frame(cases, avg_dist_trips_region) + avg_trav_dist_trips_scenario_network <- avg_trav_distance_trips_by_mode(scenarioTripsTable) + avg_trav_dist_trips_scenario_region <- avg_trav_distance_trips_by_mode(scenario_trips_region) + avg_trav_dist_trips_scenario_city <- avg_trav_distance_trips_by_mode(scenario_trips_city) + #write table - write.csv(avg_dist_trips_city, file = paste0(outputDirectoryGeneral,"/avg_dist_trips_city.csv")) - write.csv(avg_dist_trips_region, file = paste0(outputDirectoryGeneral,"/avg_dist_trips_region.csv")) + write.csv(avg_trav_dist_trips_scenario_network, file = paste0(outputDirectoryScenario,"/avg_trav_dist_trips_network.csv")) + write.csv(avg_trav_dist_trips_scenario_region, file = paste0(outputDirectoryScenario,"/avg_trav_dist_trips_region.csv")) + write.csv(avg_trav_dist_trips_scenario_city, file = paste0(outputDirectoryScenario,"/avg_trav_dist_trips_city.csv")) } -#### #1.2 Average Distance - legs based##### - -if (x_average_traveled_distance_legs == 1) { +#### #3.2 Average Euclidean Distance - trips based#### +if (x_average_euclidean_distance_trips == 1){ + + avg_eucl_distance_trips_by_mode <- function(x){ + x %>% + group_by(main_mode) %>% + #summarise_at(vars(traveled_distance), list(name=mean)) + summarise_at(vars(euclidean_distance), list(name=mean)) + } #calculation - avg_trav_dist_legs_base_region <- mean(base_legs_region$distance) - avg_trav_dist_legs_base_city <- mean(base_legs_city$distance) - avg_trav_dist_legs_scenario_region <- mean(scenario_legs_region$distance) - avg_trav_dist_legs_scenario_city <- mean(scenario_legs_city$distance) - #join to one table - avg_dist_legs_city <- c(avg_trav_dist_legs_base_city,avg_trav_dist_legs_scenario_city) + avg_eucl_dist_trips_scenario_network <- avg_eucl_distance_trips_by_mode(scenarioTripsTable) + avg_eucl_dist_trips_scenario_region <- avg_eucl_distance_trips_by_mode(scenario_trips_region) + avg_eucl_dist_trips_scenario_city <- avg_eucl_distance_trips_by_mode(scenario_trips_city) + #write table + write.csv(avg_eucl_dist_trips_scenario_network, file = paste0(outputDirectoryScenario,"/avg_eucl_dist_trips_network.csv")) + write.csv(avg_eucl_dist_trips_scenario_region, file = paste0(outputDirectoryScenario,"/avg_eucl_dist_trips_region.csv")) + write.csv(avg_eucl_dist_trips_scenario_city, file = paste0(outputDirectoryScenario,"/avg_eucl_dist_trips_city.csv")) +} +#### #3.3 Personen KM - trips based #### +if (x_personen_km_trips == 1){ + personen_km_trips <- function (x){ + x %>% + filter(main_mode!="freight") %>% + group_by(main_mode) %>% + summarise(pers_km = sum(traveled_distance)/1000) + + } + pkm_trips_scenario_city <- personen_km_trips(scenario_trips_city) + pkm_trips_scenario_region <- personen_km_trips(scenario_trips_region) + pkm_trips_scenario_network <- personen_km_trips(scenarioTripsTable) - avg_dist_legs_city <- data.frame(cases, avg_dist_legs_city) - avg_dist_legs_region <- c(avg_trav_dist_legs_base_region,avg_trav_dist_legs_scenario_region) + write.csv(pkm_trips_scenario_city, file = paste0(outputDirectoryScenario,"/trips_city_pkm.csv")) + write.csv(pkm_trips_scenario_region, file = paste0(outputDirectoryScenario,"/trips_region_pkm.csv")) + write.csv(pkm_trips_scenario_network, file = paste0(outputDirectoryScenario,"/trips_network_pkm.csv")) - avg_dist_legs_region <- data.frame(cases, avg_dist_legs_region) - #write table - write.csv(avg_dist_legs, file = paste0(outputDirectoryGeneral,"/avg_dist_legs.csv")) } -#### #1.3 Average Travel Time - trips based ##### +#### #3.4 Average Traveled Distance - legs based##### +if (x_average_traveled_distance_legs == 1){ + + avg_trav_distance_legs_by_mode <- function(x){ + x %>% + group_by(main_mode) %>% + summarise_at(vars(traveled_distance), list(name=mean)) + #summarise_at(vars(euclidean_distance), list(name=mean)) + } + #calculation + avg_trav_dist_legs_scenario_network <- avg_trav_distance_legs_by_mode(scenarioLegsTable) + avg_trav_dist_legs_scenario_region <- avg_trav_distance_legs_by_mode(scenario_legs_region) + avg_trav_dist_legs_scenario_city <- avg_trav_distance_legs_by_mode(scenario_legs_city) + #write table + write.csv(avg_trav_dist_legs_scenario_network, file = paste0(outputDirectoryScenario,"/avg_trav_dist_legs_network.csv")) + write.csv(avg_trav_dist_legs_scenario_region, file = paste0(outputDirectoryScenario,"/avg_trav_dist_legs_region.csv")) + write.csv(avg_trav_dist_legs_scenario_city, file = paste0(outputDirectoryScenario,"/avg_trav_dist_legs_city.csv")) +} +#### #3.5 Average Euclidean Distance - legs based##### +if (x_average_euclidean_distance_legs == 1){ + + avg_eucl_distance_legs_by_mode <- function(x){ + x %>% + group_by(main_mode) %>% + summarise_at(vars(traveled_distance), list(name=mean)) + #summarise_at(vars(euclidean_distance), list(name=mean)) + } + #calculation + avg_eucl_dist_legs_scenario_network <- avg_eucl_distance_legs_by_mode(scenarioLegsTable) + avg_eucl_dist_legs_scenario_region <- avg_eucl_distance_legs_by_mode(scenario_legs_region) + avg_eucl_dist_legs_scenario_city <- avg_eucl_distance_legs_by_mode(scenario_legs_city) + #write table + write.csv(avg_eucl_dist_legs_scenario_network, file = paste0(outputDirectoryScenario,"/avg_eucl_dist_legs_network.csv")) + write.csv(avg_eucl_dist_legs_scenario_region, file = paste0(outputDirectoryScenario,"/avg_eucl_dist_legs_region.csv")) + write.csv(avg_eucl_dist_legs_scenario_city, file = paste0(outputDirectoryScenario,"/avg_eucl_dist_legs_city.csv")) +} +#### #3.6 Personen KM - legs based #### +if (x_personen_km_legs == 1){ + personen_km_legs <- function (x){ + x %>% + group_by(mode) %>% + summarise(pers_km = sum(distance)/1000) + + } + pkm_legs_scenario_city <- personen_km_legs(scenario_legs_city) + pkm_legs_scenario_region <- personen_km_legs(scenario_legs_region) + pkm_legs_scenario_network <- personen_km_legs(scenarioLegsTable) -if (x_average_time_trips == 1){ + write.csv(pkm_legs_scenario_city, file = paste0(outputDirectoryScenario,"/legs_city_pkm.csv")) + write.csv(pkm_legs_scenario_region, file = paste0(outputDirectoryScenario,"/legs_region_pkm.csv")) + write.csv(pkm_legs_scenario_network, file = paste0(outputDirectoryScenario,"/legs_network_pkm.csv")) + +} +#### #4.1 Average Travel Time - trips based ##### +if (x_average_traveled_distance_trips == 1){ + + avg_time_trips_by_mode <- function(x){ + x %>% + group_by(main_mode) %>% + summarise_at(vars(trav_time), list(name=mean)) + } #calculation - avg_time_trips_base_city <- mean(base_trips_city$trav_time) - avg_time_trips_scenario_city <- mean(scenario_trips_city$trav_time) - avg_time_trips_base_region <- mean(base_trips_region$trav_time) - avg_time_trips_scenario_region <- mean(scenario_trips_region$trav_time) - #join to one table - avg_time_trips <- c(avg_time_trips_base_city,avg_time_trips_scenario_city, - avg_time_trips_base_region,avg_time_trips_scenario_region) + avg_time_trips_scenario_network <- avg_time_trips_by_mode(scenarioTripsTable) + avg_time_trips_scenario_region <- avg_time_trips_by_mode(scenario_trips_region) + avg_time_trips_scenario_city <- avg_time_trips_by_mode(scenario_trips_city) #write table - avg_time_trips <- data.frame(cases, avg_time_trips) - write.csv(avg_time_trips, file = paste0(outputDirectoryGeneral,"/avg_time_trips.csv")) + write.csv(avg_time_trips_scenario_network, file = paste0(outputDirectoryScenario,"/avg_time_trips_network.csv")) + write.csv(avg_time_trips_scenario_region, file = paste0(outputDirectoryScenario,"/avg_time_trips_region.csv")) + write.csv(avg_time_trips_scenario_city, file = paste0(outputDirectoryScenario,"/avg_time_trips_city.csv")) } -#### #1.4 Average Travel Time - legs based ##### -if (x_average_time_legs == 1){ - avg_time_legs_base_city <- mean(base_legs_city$trav_time) - avg_time_legs_scenario_city <- mean(scenario_legs_city$trav_time) - avg_time_legs_base_region <- mean(base_legs_region$trav_time) - avg_time_legs_scenario_region <- mean(scenario_legs_region$trav_time) +#### #4.2 Personen Stunden - trips based #### +if (x_personen_h_trips == 1){ + personen_stunden_trips <- function (x){ + x %>% + filter(main_mode!="freight") %>% + group_by(mode) %>% + summarise(personen_stunden_trips = (sum(trav_time)) + } + ph_trips_scenario_city <- personen_km_trips(scenario_trips_city) + ph_trips_scenario_region <- personen_km_trips(scenario_trips_region) + ph_trips_scenario_network <- personen_km_trips(scenarioTripsTable) + + write.csv(ph_trips_scenario_city, file = paste0(outputDirectoryScenario,"/trips_city_ph.csv")) + write.csv(ph_trips_scenario_region, file = paste0(outputDirectoryScenario,"/trips_region_ph.csv")) + write.csv(ph_trips_scenario_network, file = paste0(outputDirectoryScenario,"/trips_network_ph.csv")) +} + +#### #4.3 Average Travel Time - legs based ##### + +if (x_average_traveled_distance_legs == 1){ - avg_time_legs <- c(avg_time_legs_base,avg_time_legs_scenario, - avg_time_legs_base_region,avg_time_legs_scenario_region) - avg_time_legs <- data.frame(cases, avg_time_legs) - write.csv(avg_time_legs, file = paste0(outputDirectoryGeneral,"/avg_time_legs.csv"))} + avg_time_legs_by_mode <- function(x){ + x %>% + group_by(main_mode) %>% + summarise_at(vars(trav_time), list(name=mean)) + } + #calculation + avg_time_legs_scenario_network <- avg_time_legs_by_mode(scenarioLegsTable) + avg_time_legs_scenario_region <- avg_time_legs_by_mode(scenario_legs_region) + avg_time_legs_scenario_city <- avg_time_legs_by_mode(scenario_legs_city) + #write table + write.csv(avg_time_legs_scenario_network, file = paste0(outputDirectoryScenario,"/avg_time_legs_network.csv")) + write.csv(avg_time_legs_scenario_region, file = paste0(outputDirectoryScenario,"/avg_time_legs_region.csv")) + write.csv(avg_time_legs_scenario_city, file = paste0(outputDirectoryScenario,"/avg_time_legs_city.csv")) +} + +#### #4.4 Personen Stunden - legs based #### +if (x_personen_h_legs == 1){ + personen_stunden_legs <- function (x){ + x %>% + group_by(mode) %>% + summarise(personen_stunden_legs = (sum(trav_time)) + } + ph_legs_scenario_city <- personen_stunden_legs(scenario_legs_city) + ph_legs_scenario_region <- personen_stunden_legs(scenario_legs_region) + ph_legs_scenario_network <- personen_stunden_legs(scenarioLegsTable) -#### #1.5 Average Speed #### + write.csv(ph_legs_scenario_city, file = paste0(outputDirectoryScenario,"/legs_city_ph.csv")) + write.csv(ph_legs_scenario_region, file = paste0(outputDirectoryScenario,"/legs_region_ph.csv")) + write.csv(ph_legs_scenario_network, file = paste0(outputDirectoryScenario,"/legs_network_ph.csv")) + +} +#### #5.1 Average Speed #### if (x_average_traveled_speed_trips == 1){ # x) function avg_trav_distance <- function(x){ @@ -137,32 +348,23 @@ avg_trav_time <- function(x){ summarise(avgTime_s = mean(hour(trav_time)*3600 + minute(trav_time) *60 + second(trav_time) )) %>% column_to_rownames(var = "main_mode") } - -avg_trav_dist_base_city <- avg_trav_distance(base_trips_city) -avg_trav_dist_base_region <- avg_trav_distance(base_trips_region) avg_trav_dist_scenario_city <- avg_trav_distance(scenario_trips_city) avg_trav_dist_scenario_region <- avg_trav_distance(scenario_trips_region) - -avg_trav_time_base_city <- avg_trav_time(base_trips_city) -avg_trav_time_base_region <- avg_trav_time(base_trips_region) +avg_trav_dist_scenario_network <-avg_trav_distance(scenarioTripsTable) avg_trav_time_scenario_city <- avg_trav_time(scenario_trips_city) avg_trav_time_scenario_region <- avg_trav_time(scenario_trips_region) - -avg_trav_speed_base_city = avg_trav_dist_base_city/avg_trav_time_base_city*3.6 #km/h -avg_trav_speed_base_region = avg_trav_dist_base_region/avg_trav_time_base_region*3.6 #km/h +avg_trav_time_scenario_network <- avg_trav_time(scenarioTripsTable) avg_trav_speed_scenario_city = avg_trav_dist_scenario_city/avg_trav_time_scenario_city*3.6 #km/h avg_trav_speed_scenario_region = avg_trav_dist_scenario_region/avg_trav_time_scenario_region*3.6 #km/h +avg_trav_speed_scenario_network = avg_trav_dist_scenario_network/avg_trav_time_scenario_network*3.6 - -avg_trav_speed <- data.frame(X = c("base_city","base_region","scenario_city","scenario_region"), - TRAV = c(avg_trav_dist_base_city,avg_trav_dist_base_region,avg_trav_dist_scenario_city,avg_trav_dist_scenario_region), - TIME= c(avg_trav_time_base_city,avg_trav_time_base_region,avg_trav_time_scenario_city,avg_trav_time_scenario_region), - SPEED = c(avg_trav_speed_base_city,avg_trav_speed_base_region,avg_trav_speed_scenario_city,avg_trav_speed_scenario_region)) - -write.csv(avg_trav_speed, file = paste0(outputDirectoryGeneral,"/avg_trav_speed.csv")) +#write tables +write.csv(avg_trav_speed_scenario_network, file = paste0(outputDirectoryScenario,"/avg_trav_speed_trips_network.csv")) +write.csv(avg_trav_speed_scenario_region, file = paste0(outputDirectoryScenario,"/avg_trav_speed_trips_region.csv")) +write.csv(avg_trav_speed_scenario_city, file = paste0(outputDirectoryScenario,"/avg_trav_speed_trips_city.csv")) } -#### #1.6 Average Beeline Speed #### +#### #5.2 Average Beeline Speed #### if (x_average_beeline_speed_trips == 1){ # x) function @@ -183,322 +385,83 @@ avg_trav_time <- function(x){ column_to_rownames(var = "main_mode") } # average beeline distance and average travel time -avg_beeline_dist_base_city <- avg_beeline_distance(base_trips_city) -avg_beeline_dist_base_region <- avg_beeline_distance(base_trips_region) avg_beeline_dist_scenario_city <- avg_beeline_distance(scenario_trips_city) avg_beeline_dist_scenario_region <- avg_beeline_distance(scenario_trips_region) -avg_trav_time_base_city <- avg_trav_time(base_trips_city) -avg_trav_time_base_region <- avg_trav_time(base_trips_region) +avg_beeline_dist_scenario_network <- avg_beeline_distance(scenarioTripsTable) avg_trav_time_scenario_city <- avg_trav_time(scenario_trips_city) avg_trav_time_scenario_region <- avg_trav_time(scenario_trips_region) +avg_trav_time_scenario_network <- avg_trav_time(scenarioTripsTable) # average beeline speed -avg_beeline_speed_base_city = avg_beeline_dist_base_city/avg_trav_time_base_city*3.6 #km/h -avg_beeline_speed_base_region = avg_beeline_dist_base_region/avg_trav_time_base_region*3.6 -avg_beeline_speed_scenario_city = avg_beeline_dist_scenario_city/avg_trav_time_scenario_city*3.6 -avg_beeline_speed_scenario_region = avg_beeline_dist_scenario_region/avg_trav_time_scenario_region*3.6 - -avg_beeline_speed <- data.frame(X = c("base_city","base_region","scenario_city","scenario_region"), - TRAV = c(avg_beeline_dist_base_city,avg_beeline_dist_base_region,avg_beeline_dist_scenario_city,avg_beeline_dist_scenario_region), - TIME= c(avg_trav_time_base_city,avg_trav_time_base_region,avg_trav_time_scenario_city,avg_trav_time_scenario_region), - SPEED = c(avg_beeline_speed_base_city,avg_beeline_speed_base_region,avg_beeline_speed_scenario_city,avg_beeline_speed_scenario_region)) - -write.csv(avg_beeline_speed, file = paste0(outputDirectoryGeneral,"/avg_beeline_speed.csv")) +avg_beeline_speed_scenario_city = avg_beeline_dist_scenario_city/avg_trav_time_scenario_city*3.6 #km/h +avg_beeline_speed_scenario_region = avg_beeline_dist_scenario_region/avg_trav_time_scenario_region*3.6 #km/h +avg_beeline_speed_scenario_network = avg_beeline_dist_scenario_network/avg_trav_time_scenario_network*3.6 +#write tables +write.csv(avg_beeline_speed_scenario_network, file = paste0(outputDirectoryScenario,"/avg_bee_speed_trips_network.csv")) +write.csv(avg_beeline_speed_scenario_region, file = paste0(outputDirectoryScenario,"/avg_bee_speed_trips_region.csv")) +write.csv(avg_beeline_speed_scenario_city, file = paste0(outputDirectoryScenario,"/avg_bee_speed_trips_city.csv")) } -#### #2.1 Execution Scores Winner-Loser #### - -if (x_winner_loser == 1){ -base_scenario_persons <- inner_join(base_persons, scenario_persons, by= "person") %>% - select(person, executed_score.x, executed_score.y, income.x, sex.x, age.x, carAvail.x, first_act_x.x, first_act_y.x) %>% - mutate(score_change = format((executed_score.y - executed_score.x), scientific = FALSE), person = as.character(person)) - -home_trips <- baseTripsTable %>% - filter(grepl("home", start_activity_type)) %>% - distinct(person, .keep_all = TRUE) %>% - select(person, start_link, start_x, start_y) - -base_scenario_persons <- full_join(base_scenario_persons, home_trips, by = "person") %>% - mutate(home_x = ifelse(is.na(start_x), first_act_x.x, start_x), - home_y = ifelse(is.na(start_y), first_act_y.x, start_y)) %>% - select(person, executed_score.x, executed_score.y, score_change, income.x, sex.x, age.x, carAvail.x, home_x, home_y) - -write.csv(base_scenario_persons, file = paste0(outputDirectoryGeneral,"/ScoreTable.csv")) - - -TotalNumberRegionBase <- nrow(CitizenRegionBase) -MaxScoreRegionBase <- max(CitizenRegionBase$executed_score) -MinScoreRegionBase <- min(CitizenRegionBase$executed_score) -AvgScoreRegionBase <- mean(CitizenRegionBase$executed_score) - -#CitizenCityBase -TotalNumberCityBase <- nrow(CitizenCityBase) -MaxScoreCityBase <- max(CitizenCityBase$executed_score) -MinScoreCityBase <- min(CitizenCityBase$executed_score) -AvgScoreCityBase <- mean(CitizenCityBase$executed_score) -#left_join(CitizenCityBase , BaseTripsCity, by = person, type = "left", match = "all") - -#CitizenRegionScenario -TotalNumberegionScenario <- nrow(CitizenRegionScenario) -MaxScoreRegionScenario <- max(CitizenRegionScenario$executed_score) -MinScoreRegionScenario <- min(CitizenRegionScenario$executed_score) -AvgScoreRegionScenario <- mean(CitizenRegionScenario$executed_score) -#left_join(CitizenRegionScenario , ScenarioTripsRegion, by = person, type = "left", match = "all") - -#CitizenCityScenario -TotalNumberCityScenario <- nrow(CitizenCityScenario) -MaxScoreCityScenario <- max(CitizenCityScenario$executed_score) -MinScoreCityScenario <- min(CitizenCityScenario$executed_score) -AvgScoreCityScenario <- mean(CitizenCityScenario$executed_score) -#left_join(CitizenCityScenario , ScenarioTripsCity, by = person, type = "left", match = "all") - - -MaxScoreTable <- tibble(AREA= c("City","Region"), base = c(MaxScoreCityBase,MaxScoreRegionBase), scenario = c(MaxScoreCityScenario,MaxScoreRegionScenario)) -write.csv(MaxScoreTable, file = paste0(outputDirectoryGeneral,"/maxScoreTable.csv")) -MinScoreTable <- tibble(AREA= c("City","Region"), base = c(MinScoreCityBase,MinScoreRegionBase), scenario = c(MinScoreCityScenario,MinScoreRegionScenario)) -write.csv(MinScoreTable, file = paste0(outputDirectoryGeneral,"/minScoreTable.csv")) -AvgScoreTable <- tibble(AREA= c("City","Region"), base = c(AvgScoreCityBase,AvgScoreRegionBase), scenario = c(AvgScoreCityScenario,AvgScoreRegionScenario)) -write.csv(AvgScoreTable, file = paste0(outputDirectoryGeneral,"/AvgScoreTable.csv")) - -} -#### #3.1 Personen KM - trips based #### -if (x_personen_km_trips==1){ - personen_km_trips <- function (x){ - x %>% - filter(main_mode!="freight") %>% - summarise(pers_km = (sum(traveled_distance)/length(unique(person)))/1000) - } - - pkm_trips_base_city <- personen_km_trips(base_trips_city) - pkm_trips_base_region <- personen_km_trips(base_trips_region) - pkm_trips_scenario_city <- personen_km_trips(scenario_trips_city) - pkm_trips_scenario_region <- personen_km_trips(scenario_trips_region) - pkm_trips <- bind_rows("base_city" = pkm_trips_base_city, - "base_region" = pkm_trips_base_region, - "scenario_city" = pkm_trips_scenario_city, - "scenario_region" = pkm_trips_scenario_region, - .id = "case") - - write.csv(pkm_trips, file = paste0(outputDirectoryGeneral,"/pkm_trips.csv")) -} - -#### #3.2 Personen KM - legs based #### -if (x_personen_km_legs == 1){ - personen_km_legs <- function (x){ - x %>% - summarise(pers_km = (sum(distance)/length(unique(person)))/1000) - - } - - pkm_legs_base_city <- personen_km_legs(base_legs_city) - pkm_legs_base_region <- personen_km_legs(base_legs_region) - pkm_legs_scenario_city <- personen_km_legs(scenario_legs_city) - pkm_legs_scenario_region <- personen_km_legs(scenario_legs_region) - pkm_legs <- bind_rows("base_city" = pkm_legs_base_city, - "base_region" = pkm_legs_base_region, - "scenario_city" = pkm_legs_scenario_city, - "scenario_region" = pkm_legs_scenario_region, - .id = "case") - - write.csv(pkm_legs, file = paste0(outputDirectoryGeneral,"/pkm_legs.csv")) -} - - -#### #3.3 Personen Stunden - trips based #### -if (x_personen_h_trips == 1){ - personen_stunden_trips <- function (x){ - x %>% - filter(main_mode!="freight") %>% - summarise(personen_stunden_trips = (sum(trav_time)/length(unique(person)))) - } - - ph_trips_base_city <- personen_stunden_trips(base_trips_city) - ph_trips_base_region <- personen_stunden_trips(base_trips_region) - ph_trips_scenario_city <- personen_stunden_trips(scenario_trips_city) - ph_trips_scenario_region <- personen_stunden_trips(scenario_trips_region) - - ph_trips <- bind_rows("base_city" = ph_trips_base_city, - "base_region" = ph_trips_base_region, - "scenario_city" = ph_trips_scenario_city, - "scenario_region" = ph_trips_scenario_region, - .id = "case") - - write.csv(ph_trips, file = paste0(outputDirectoryGeneral,"/ph_trips.csv")) - -} - -#### #3.4 Personen Stunden - legs based #### -if (x_personen_h_legs == 1){ - personen_stunden_legs <- function (x){ - x %>% - summarise(personen_stunden_legs = (sum(trav_time)/length(unique(person)))) - } - - ph_legs_base_city <- personen_stunden_legs(base_legs_city) - ph_legs_base_region <- personen_stunden_legs(base_legs_region) - ph_legs_scenario_city <- personen_stunden_legs(scenario_legs_city) - ph_legs_scenario_region <- personen_stunden_legs(scenario_legs_region) - - ph_legs <- bind_rows("base_city" = ph_legs_base_city, - "base_region" = ph_legs_base_region, - "scenario_city" = ph_legs_scenario_city, - "scenario_region" = ph_legs_scenario_region, - .id = "case") - - write.csv(ph_legs, file = paste0(outputDirectoryGeneral,"/ph_legs.csv")) - -} - - -#### #4.1 Modal Split - trips based - main mode (count) #### - -if (x_ms_trips_count == 1){ - - modal_split_trips_main_mode <- function(x){ - x %>% - count(main_mode) %>% - mutate(percent = 100*n/sum(n)) - } - - - ms_main_mode_trips_baseCity <- modal_split_trips_main_mode(base_trips_city) - ms_main_mode_trips_scenarioCity <- modal_split_trips_main_mode(scenario_trips_city) - - ms_main_mode_trips_city <- bind_rows("base" = ms_main_mode_trips_baseCity, - "scenario" = ms_main_mode_trips_scenarioCity, - .id = "case") - - write.csv(ms_main_mode_trips_city,file = paste0(outputDirectoryGeneral,"/ms_main_mode_trips_city.csv")) - - ms_main_mode_trips_baseRegion <- modal_split_trips_main_mode(base_trips_region) - ms_main_mode_trips_scenarioRegion <- modal_split_trips_main_mode(scenario_trips_region) - - ms_main_mode_trips_region <- bind_rows("base" = ms_main_mode_trips_baseRegion, - "scenario" = ms_main_mode_trips_scenarioRegion, - .id = "case") - - write.csv(ms_main_mode_trips_region,file = paste0(outputDirectoryGeneral,"/ms_main_mode_trips_region.csv")) -} - -#### #4.2 Modal Split - trips based - distance #### -if (x_ms_trips_distance == 1){ - modal_split_trips_distance <- function(x){ - x %>% - group_by(main_mode) %>% - summarise(distance = sum(traveled_distance)) %>% - mutate(percent = round(100*distance/sum(distance),2)) - } - - ms_dist_trips_baseCity <- modal_split_trips_distance(base_trips_city) - ms_dist_trips_scenarioCity <- modal_split_trips_distance(scenario_trips_city) - - ms_dist_trips_city <- bind_rows("base" = ms_dist_trips_baseCity, - "scenario" = ms_dist_trips_scenarioCity, - .id = "case") - write.csv(ms_dist_trips_city,file = paste0(outputDirectoryGeneral,"/ms_dist_trips_city.csv")) - - ms_dist_trips_baseRegion <- modal_split_trips_distance(base_trips_region) - ms_dist_trips_scenarioRegion<- modal_split_trips_distance(scenario_trips_region) - - ms_dist_trips_region <- bind_rows("base" = ms_dist_trips_baseRegion, - "scenario" = ms_dist_trips_scenarioRegion, - .id = "case") - write.csv(ms_dist_trips_region,file = paste0(outputDirectoryGeneral,"/ms_dist_trips_region.csv")) -} - - -#### #4.3 Modal Split - legs based - main mode (count) #### -if (x_ms_legs_count == 1){ - modal_split_legs_mode <- function(x){ - x %>% - mutate(distance_cut = cut(distance, breaks = breaks, - labels = c("<1000m", "1 - 2km", "2 - 5km", "5 - 10km", "10 - 20km", ">20km" ))) %>% - group_by(distance_cut) %>% - count(mode) %>% - mutate(percent = 100*n/sum(n)) - } - - ms_mode_legs_baseCity <- modal_split_legs_mode(base_legs_city) - ms_mode_legs_scenarioCity <- modal_split_legs_mode(scenario_legs_city) - - - ms_mode_legs_city <- bind_rows("base" = ms_mode_legs_baseCity, - "scenario" = ms_mode_legs_scenarioCity, - .id = "case") - write.csv(ms_mode_legs_city,file = paste0(outputDirectoryGeneral,"/ms_mode_legs_city.csv")) - - ms_mode_legs_baseRegion <- modal_split_legs_mode(base_legs_region) - ms_mode_legs_scenarioRegion <- modal_split_legs_mode(scenario_legs_region) - - - ms_mode_legs_region <- bind_rows("base" = ms_mode_legs_baseRegion, - "scenario" = ms_mode_legs_scenarioRegion, - .id = "case") - write.csv(ms_mode_legs_region,file = paste0(outputDirectoryGeneral,"/ms_mode_legs_region.csv")) -} - -#### #4.4 Modal Split - legs based - distance #### -if (x_ms_legs_distance == 1){ - modal_split_legs_distance <- function(x){ - x %>% - group_by(mode) %>% - summarise(distance = sum(distance)) %>% - mutate(percent = round(100*distance/sum(distance),2)) - } - - ms_dist_legs_baseCity <- modal_split_legs_distance(base_legs_city) - ms_dist_legs_scenarioCity <- modal_split_legs_distance(scenario_legs_city) - - ms_dist_legs_city <- bind_rows("base"= ms_dist_legs_baseCity, - "scenario" = ms_dist_legs_scenarioCity, - .id = "case") - write.csv(ms_dist_legs_city,file = paste0(outputDirectoryGeneral,"/ms_dist_legs_city.csv")) - - ms_dist_legs_baseRegion <- modal_split_legs_distance(base_legs_region) - ms_dist_legs_scenarioRegion <- modal_split_legs_distance(scenario_legs_region) - - ms_dist_legs_region <- bind_rows("base"= ms_dist_legs_baseRegion, - "scenario" = ms_dist_legs_scenarioRegion, - .id = "case") - write.csv(ms_dist_legs_region,file = paste0(outputDirectoryGeneral,"/ms_dist_legs_region.csv")) -} - -#### #5.1 Sankey #### - -if (x_sankey_diagram == 1){ -sankey_dataframe <- function(x, y){ - inner_join(x, y, by = "trip_id") %>% - select(trip_id, main_mode.x, longest_distance_mode.x, main_mode.y, longest_distance_mode.y) %>% - group_by(main_mode.x, main_mode.y) %>% - summarise(Freq = n()) -} - -#Base Case > Policy Case CITY -Base_city_to_Scenario_city <- sankey_dataframe(base_trips_city, scenario_trips_city) - -sankey_city <- alluvial(Base_city_to_Scenario_city[1:2], - freq= Base_case_city_to_Scenario_city$Freq, - border = NA, - axis_labels = c("Base Case", "Scenario Case")) - -sankey_city <- as_tibble(t(sankey_city)) -write.csv(sankey_city, file = paste0(outputDirectoryGeneral,"/sankey_city.csv")) - -#Base Case > Policy Case REGION -Base_city_to_Scenario_city <- sankey_dataframe(base_trips_region, scenario_trips_region) - -sankey_region <- alluvial(Base_region_to_Scenario_region[1:2], - freq= Base_case_region_to_Scenario_region$Freq, - border = NA, - axis_labels = c("Base Case", "Scenario Case")) - -sankey_region <- as_tibble(t(sankey_region)) -write.csv(sankey_region, file = paste0(outputDirectoryGeneral,"/sankey_region.csv")) -} #### #6.1 Emissions #### if (x_emissions == 1){ } #### #7.1 Traffic #### if (x_traffic == 1){ } +#### #8.1 Execution Scores Winner-Loser #### + +if (x_winner_loser == 1){ + base_scenario_persons <- inner_join(base_persons, scenario_persons, by= "person") %>% + select(person, executed_score.x, executed_score.y, income.x, sex.x, age.x, carAvail.x, first_act_x.x, first_act_y.x) %>% + mutate(score_change = format((executed_score.y - executed_score.x), scientific = FALSE), person = as.character(person)) + + home_trips <- baseTripsTable %>% + filter(grepl("home", start_activity_type)) %>% + distinct(person, .keep_all = TRUE) %>% + select(person, start_link, start_x, start_y) + + base_scenario_persons <- full_join(base_scenario_persons, home_trips, by = "person") %>% + mutate(home_x = ifelse(is.na(start_x), first_act_x.x, start_x), + home_y = ifelse(is.na(start_y), first_act_y.x, start_y)) %>% + select(person, executed_score.x, executed_score.y, score_change, income.x, sex.x, age.x, carAvail.x, home_x, home_y) + + write.csv(base_scenario_persons, file = paste0(outputDirectoryBase,"/ScoreTable.csv")) + + + AgentsInNetwork <- nrow(base_scenario_persons) + MaxScoreNetworkBase <- max(base_scenario_persons$executed_score.x) + MinScoreNetworkBase <- min(base_scenario_persons$executed_score.x) + AvgScoreNetworkBase <- mean(base_scenario_persons$executed_score.x) + MaxScoreNetworkScenario <- max(base_scenario_persons$executed_score.y) + MinScoreNetworkScenario <- min(base_scenario_persons$executed_score.y) + AvgScoreNetworkScenario <- mean(base_scenario_persons$executed_score.y) + BiggestLoserNetwork <- min(base_scenario_persons$score_change) + GoodOrBadForNetwork = AvgScoreNetworkScenario - AvgScoreNetworkBase + BiggestWinnerNetwork <- max(base_scenario_persons$score_change) + + AgentsInRegion <- nrow(base_scenario_persons) + MaxScoreRegionBase <- max(base_scenario_persons$executed_score.x) + MinScoreRegionBase <- min(base_scenario_persons$executed_score.x) + AvgScoreRegionBase <- mean(base_scenario_persons$executed_score.x) + MaxScoreRegionScenario <- max(base_scenario_persons$executed_score.y) + MinScoreRegionScenario <- min(base_scenario_persons$executed_score.y) + AvgScoreRegionScenario <- mean(base_scenario_persons$executed_score.y) + BiggestLoserRegion <- min(base_scenario_persons$score_change) + GoodOrBadForRegion = AvgScoreRegionScenario - AvgScoreRegionBase + BiggestWinnerRegion <- max(base_scenario_persons$score_change) + + AgentsInCity <- nrow(base_scenario_persons) + MaxScoreCityBase <- max(base_scenario_persons$executed_score.x) + MinScoreCityBase <- min(base_scenario_persons$executed_score.x) + AvgScoreCityBase <- mean(base_scenario_persons$executed_score.x) + MaxScoreCityScenario <- max(base_scenario_persons$executed_score.y) + MinScoreCityScenario <- min(base_scenario_persons$executed_score.y) + AvgScoreCityScenario <- mean(base_scenario_persons$executed_score.y) + BiggestLoserCity <- min(base_scenario_persons$score_change) + GoodOrBadForCity = AvgScoreCityScenario - AvgScoreCityBase + BiggestWinnerCity <- max(base_scenario_persons$score_change) + + +} + diff --git a/src/main/R/masterscript.R b/src/main/R/masterscript.R index 565cc96a..0f6e7a0e 100644 --- a/src/main/R/masterscript.R +++ b/src/main/R/masterscript.R @@ -15,38 +15,48 @@ library(networkD3) library(alluvial) library(ggalluvial) library(stringr) +library(data.table) print("#### Libraries geladen! ####") +################################################################################ CASES #### please put (1=yes/0=no) for analyses +#base-case = 0 +#carfree-area-90 = 0 +#carfree-area-95 = 0 +#carfree-area-99 = 0 +#drt-outskirts = 0 +#drt-whole-city = 0 +#slow-speed-absolute = 0 +#slow-speed-relative = 0 +#combined_scenarioA = 0 ################################################################################ INPUT #### -workingDirectory <- "D:/VSP_Berlin/Leipzig/Car_free_areas" -setwd(workingDirectory) -################################################################################ fill out paths -runID = "Leipzig_speed_20relativ_25pct" -network <- "base/leipzig-25pct-base.output_network.xml.gz" -base_persons_path <- "matsim_output/base/leipzig-flexa-25pct-scaledFleet-base_noDepot.output_persons.csv.gz" -base_trips_path <- "matsim_output/base/leipzig-flexa-25pct-scaledFleet-base_noDepot.output_trips.csv.gz" -base_legs_path <- "matsim_output/base/leipzig-flexa-25pct-scaledFleet-base_noDepot.output_legs.csv.gz" -#base_traffic <- "traffic_output_base.csv" -#base_emission <- "" -scenario_persons_path <- "matsim_output/carfree90pct/leipzig-flexa-25pct-scaledFleet-carfree90pct_noDepot.output_persons.csv.gz" -scenario_trips_path <- "20relativ/leipzig-drt-20relativ-25pct.output_trips.csv.gz" -scenario_legs_path <- "20relativ/leipzig-drt-20relativ-25pct.output_legs.csv.gz" -#scenario_traffic <- "..." -#scenario_emission <- "..." - - -region_shp_path <- "../shapefiles/Leipzig_puffer.shp" -city_shp_path <- "../shapefiles/Leipzig_stadt.shp" +publicSVN = "/Users/mkreuschnervsp/Desktop/git/public-svn/matsim/scenarios/countries/de/leipzig/projects/namav/" +#local = /Users/mkreuschnervsp/Desktop/VSP_projects/02_NaMAV + +runID = "carfree-area-90" +network <- paste(publicSVN,"base-case/leipzig-25pct-base.output_network.xml.gz") +CRS <- 25832 + +scenario_run_path <- paste(publicSVN,runID) + +#base path nur für Sankey und Winner/Loser Analysis +base_run_path <- "/Users/mkreuschnervsp/Desktop/git/public-svn/matsim/scenarios/countries/de/leipzig/projects/namav/base-case/" + + +region_shp_path <- "/Users/mkreuschnervsp/Desktop/VSP_projects/02_NaMAV/R/shapefiles/Leipzig_puffer.shp" +city_shp_path <- "/Users/mkreuschnervsp/Desktop/VSP_projects/02_NaMAV/R/shapefiles/Leipzig_stadt.shp" +area_shp_path <- "/Users/mkreuschnervsp/Desktop/VSP_projects/02_NaMAV/R/shapefiles/Zonen90_update.shp" + print("#### Inputspath definiert! ####") ################################################################################ OUTPUT #### -outputDirectoryGeneral <- paste(workingDirectory, "/output_general", sep = "") # the plots are going to be saved here -if(!file.exists(outputDirectoryGeneral)){ +outputDirectoryBase <- "/Users/mkreuschnervsp/Desktop/VSP_projects/02_NaMAV/R/base-analysis-R" # the plots are going to be saved here +if(!file.exists(outputDirectoryBase)){ print("creating analysis sub-directory") - dir.create(outputDirectoryGeneral) + dir.create(outputDirectoryBase) } - -outputDirectoryScenario <- paste(workingDirectory, "/output_scenario", sep = "") # the plots are going to be saved here +#/Users/mkreuschnervsp/Desktop/git/public-svn/matsim/scenarios/countries/de/leipzig/projects/namav/",runID,"/analysis/analysis-R +outputDirectoryScenario <- "/Users/mkreuschnervsp/Desktop/VSP_projects/02_NaMAV/R/policy-analysis-R" + #paste(scenario_run_path, "analysis/analysis-R", sep = "") # the plots are going to be saved here if(!file.exists(outputDirectoryScenario)){ print("creating analysis sub-directory") dir.create(outputDirectoryScenario) @@ -54,43 +64,67 @@ if(!file.exists(outputDirectoryScenario)){ print("#### Output folder geladen! ####") ################################################################################ ANALYSIS #### - -x_average_traveled_distance_trips = 1 -x_average_traveled_distance_legs = 0 - -x_average_time_trips = 1 -x_average_time_legs = 0 - -x_average_traveled_speed_trips = 1 -x_average_beeline_speed_trips = 1 - -x_personen_km_trips = 0 -x_personen_km_legs = 1 - -x_personen_h_trips = 0 -x_personen_h_legs = 1 - -x_ms_trips_count = 1 -x_ms_trips_distance = 1 - -x_ms_legs_count = 0 -x_ms_legs_distance = 0 +# PLEASE put (1=yes/0=no) for certain analysis + +#### #1.1 Modal Split COUNTS - trips based + x_ms_trips_count = 1 +#### #1.2 Modal Split DISTANCE - trips based + x_ms_trips_distance = 1 +#### #1.3 Modal Split COUNTS- legs based + x_ms_legs_count = 0 +#### #1.4 Modal Split DISTANCE - legs based + x_ms_legs_distance = 0 + +#### #2.1 Modal Shift - trips based + x_sankey_diagram = 1 + +#### #3.1 Distances TRAVELED - trips based + x_average_traveled_distance_trips = 1 +#### #3.2 Distances EUCLIDEAN - trips based + x_average_euclidean_distance_trips = 1 +#### #3.3 PKM - trips based + x_personen_km_trips = 0 +#### #3.4 Distances TRAVELED - legs based + x_average_traveled_distance_legs = 0 +#### #3.5 Distances EUCLIDEAN - legs based + x_average_euclidean_distance_legs = 0 +#### #3.6 PKM - legs based + x_personen_km_legs = 1 + +#### #4.1 Time Traveled - trips based + x_average_time_trips = 1 +#### #4.2 Time Traveled - legs based + x_average_time_legs = 0 +#### #4.3 ph - trips based + x_personen_h_trips = 0 +#### #4.4 ph - legs based + x_personen_h_legs = 1 + +#### #5.1 Speed TRAVELED - trips based + x_average_traveled_speed_trips = 1 +#### #5.2 Speed BEELINE - trips based + x_average_beeline_speed_trips = 1 + +#### #6.1 Traffic Volumes + #x_traffic = 0 + +#### #7.1 Emissions Analysis + #x_emissions = 0 + +#### #8.1 Winner/Loser Analysis + x_winner_loser = 1 #x_distance_distribution_trips = 1 #x_distance_distribution_legs = 0 -x_sankey_diagram = 1 -#x_emissions = 0 -#x_traffic = 0 - -x_winner_loser = 1 + print("#### Auswahl getroffen! ####") ################################################################################ SOURCE #### -source("masteranalyse2.R") +source("/Users/mkreuschnervsp/Desktop/R_Studio/mastersolver.R") print("#### Masterscript fertig! ####") From e453f38515cf4a51ea15cc57f1c8dd6355da7ce6 Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Mon, 17 Apr 2023 15:16:39 +0200 Subject: [PATCH 006/106] commit test idea --- .../org/matsim/run/ChessboardParkingTest.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/test/java/org/matsim/run/ChessboardParkingTest.java diff --git a/src/test/java/org/matsim/run/ChessboardParkingTest.java b/src/test/java/org/matsim/run/ChessboardParkingTest.java new file mode 100644 index 00000000..417fa885 --- /dev/null +++ b/src/test/java/org/matsim/run/ChessboardParkingTest.java @@ -0,0 +1,35 @@ +package org.matsim.run; + + +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.matsim.api.core.v01.Scenario; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.network.NetworkUtils; +import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.examples.ExamplesUtils; +import org.matsim.run.prepare.NetworkOptions; +import org.matsim.testcases.MatsimTestUtils; + +import java.io.IOException; + +public class ChessboardParkingTest { + @Rule + public MatsimTestUtils utils = new MatsimTestUtils(); + + + @Test + public final void runParkingChessboardTest() throws IOException { + String inputPath = String.valueOf(ExamplesUtils.getTestScenarioURL("chessboard")); + + Config config = ConfigUtils.loadConfig(inputPath + "/config.xml"); + Scenario scenario = ScenarioUtils.loadScenario(config); + NetworkOptions networkOptions = new NetworkOptions(); + + // help + networkOptions.prepare(scenario.getNetwork()); + + } +} From 050d95da2308aaf9c65926bd8bb344f03c70ba83 Mon Sep 17 00:00:00 2001 From: simei94 Date: Tue, 18 Apr 2023 15:13:47 +0200 Subject: [PATCH 007/106] create sample population --- .../org/matsim/run/ChessboardParkingTest.java | 88 ++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/matsim/run/ChessboardParkingTest.java b/src/test/java/org/matsim/run/ChessboardParkingTest.java index 417fa885..066a0fa9 100644 --- a/src/test/java/org/matsim/run/ChessboardParkingTest.java +++ b/src/test/java/org/matsim/run/ChessboardParkingTest.java @@ -4,14 +4,20 @@ import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; +import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.population.*; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; +import org.matsim.core.controler.Controler; import org.matsim.core.network.NetworkUtils; +import org.matsim.core.population.PopulationUtils; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.examples.ExamplesUtils; import org.matsim.run.prepare.NetworkOptions; import org.matsim.testcases.MatsimTestUtils; +import playground.vsp.openberlinscenario.cemdap.output.ActivityTypes; import java.io.IOException; @@ -21,15 +27,95 @@ public class ChessboardParkingTest { @Test - public final void runParkingChessboardTest() throws IOException { + public final void runChessboardParkingTest() throws IOException { String inputPath = String.valueOf(ExamplesUtils.getTestScenarioURL("chessboard")); Config config = ConfigUtils.loadConfig(inputPath + "/config.xml"); + config.controler().setLastIteration(1); + config.controler().setOutputDirectory("output/chessboardParkingTest/"); + config.global().setNumberOfThreads(1); + config.qsim().setNumberOfThreads(1); + Scenario scenario = ScenarioUtils.loadScenario(config); + Population population = PopulationUtils.createPopulation(config); + + createExampleParkingPopulation(population); + + + + + NetworkOptions networkOptions = new NetworkOptions(); // help networkOptions.prepare(scenario.getNetwork()); + + Controler controler = new Controler(scenario); + + + + } + + final void createExampleParkingPopulation(Population population) { + + PopulationFactory factory = population.getFactory(); + + Leg carLeg = factory.createLeg(TransportMode.car); + + Person residentInResidentialArea = factory.createPerson(Id.createPersonId("residentInResidentialArea")); + + Plan plan1 = factory.createPlan(); + //maybe we also need to set start / end times for the activities.. + plan1.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("80"))); + plan1.addLeg(carLeg); + plan1.addActivity(factory.createActivityFromLinkId(ActivityTypes.EDUCATION, Id.createLinkId("81"))); + residentInResidentialArea.addPlan(plan1); + residentInResidentialArea.getAttributes().putAttribute("parkingType", "residential"); + + Person residentOutsideResidentialArea = factory.createPerson(Id.createPersonId("residentOutsideResidentialArea")); + + Plan plan2 = factory.createPlan(); + plan2.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("35"))); + plan2.addLeg(carLeg); + plan2.addActivity(factory.createActivityFromLinkId(ActivityTypes.WORK, Id.createLinkId("31"))); + residentOutsideResidentialArea.addPlan(plan2); + residentOutsideResidentialArea.getAttributes().putAttribute("parkingType", "residential"); + + Person nonResidentInResidentialAreaNoShop = factory.createPerson(Id.createPersonId("nonResidentInResidentialAreaNoShop")); + + Plan plan3 = factory.createPlan(); + plan3.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("41"))); + plan3.addLeg(carLeg); + plan3.addActivity(factory.createActivityFromLinkId(ActivityTypes.WORK, Id.createLinkId("45"))); + nonResidentInResidentialAreaNoShop.addPlan(plan3); + nonResidentInResidentialAreaNoShop.getAttributes().putAttribute("parkingType", "non-residential"); + + Person nonResidentInResidentialAreaShop = factory.createPerson(Id.createPersonId("nonResidentInResidentialAreaShop")); + + Plan plan4 = factory.createPlan(); + plan4.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("40"))); + plan4.addLeg(carLeg); + plan4.addActivity(factory.createActivityFromLinkId(ActivityTypes.SHOPPING, Id.createLinkId("135"))); + nonResidentInResidentialAreaShop.addPlan(plan4); + nonResidentInResidentialAreaNoShop.getAttributes().putAttribute("parkingType", "non-residential"); + + Person nonResidentOutsideResidentialArea = factory.createPerson(Id.createPersonId("nonResidentOutsideResidentialArea")); + + Plan plan5 = factory.createPlan(); + plan5.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("64"))); + plan5.addLeg(carLeg); + plan5.addActivity(factory.createActivityFromLinkId(ActivityTypes.LEISURE, Id.createLinkId("66"))); + nonResidentOutsideResidentialArea.addPlan(plan5); + nonResidentOutsideResidentialArea.getAttributes().putAttribute("parkingType", "non-residential"); + + //residential area is maximum including the following edges (square): 124-126, 34-36, 178-180, 88-90 + + population.addPerson(residentInResidentialArea); + population.addPerson(residentOutsideResidentialArea); + population.addPerson(nonResidentOutsideResidentialArea); + population.addPerson(nonResidentInResidentialAreaNoShop); + population.addPerson(nonResidentInResidentialAreaShop); + } } From 2a3320cf3003422dcb12cf9dd8b2ace3c91722f0 Mon Sep 17 00:00:00 2001 From: Kai Nagel Date: Sat, 22 Apr 2023 11:17:06 +0200 Subject: [PATCH 008/106] first version that might structurally work. Still failing because of issues with data model --- .../ParkingCorrectionMultithreadedModule.java | 200 ++++---- .../run/SubtourModeChoiceInclParking.java | 2 +- .../org/matsim/run/prepare/LeipzigUtils.java | 20 + .../org/matsim/run/ChessboardParkingTest.java | 460 +++++++++++++++--- 4 files changed, 509 insertions(+), 173 deletions(-) create mode 100644 src/main/java/org/matsim/run/prepare/LeipzigUtils.java diff --git a/src/main/java/org/matsim/run/ParkingCorrectionMultithreadedModule.java b/src/main/java/org/matsim/run/ParkingCorrectionMultithreadedModule.java index 7bcead2e..826a1290 100644 --- a/src/main/java/org/matsim/run/ParkingCorrectionMultithreadedModule.java +++ b/src/main/java/org/matsim/run/ParkingCorrectionMultithreadedModule.java @@ -1,100 +1,100 @@ -package org.matsim.run; - -import com.google.inject.Inject; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.matsim.api.core.v01.Scenario; -import org.matsim.api.core.v01.TransportMode; -import org.matsim.api.core.v01.network.Network; -import org.matsim.api.core.v01.population.Plan; -import org.matsim.api.core.v01.population.PlanElement; -import org.matsim.api.core.v01.population.PopulationFactory; -import org.matsim.core.config.groups.GlobalConfigGroup; -import org.matsim.core.network.filter.NetworkFilterManager; -import org.matsim.core.population.algorithms.PlanAlgorithm; -import org.matsim.core.replanning.modules.AbstractMultithreadedModule; -import org.matsim.core.router.TripRouter; -import org.matsim.core.router.TripStructureUtils; -import playground.vsp.simpleParkingCostHandler.ParkingCostConfigGroup; - -import java.util.ArrayList; -import java.util.List; - -class ParkingCorrectionMultithreadedModule extends AbstractMultithreadedModule { - - @Inject - Scenario scenario; - - @Inject - ParkingCostConfigGroup parkingCostConfigGroup; - - - private static final Logger log = LogManager.getLogger(ParkingCorrectionMultithreadedModule.class ); - private final PopulationFactory pf; - - public ParkingCorrectionMultithreadedModule( GlobalConfigGroup globalConfigGroup, PopulationFactory pf ){ - super( globalConfigGroup ); - this.pf = pf; - - NetworkFilterManager managerResidential = new NetworkFilterManager(scenario.getNetwork(), scenario.getConfig().network()); - managerResidential.addLinkFilter(new LinkAttributeNetworkLinkFilter(parkingCostConfigGroup.getResidentialParkingFeeAttributeName(), "0.")); - - Network nonResidentialParkingNetwork = managerResidential.applyFilters(); - - NetworkFilterManager managerShop = new NetworkFilterManager(scenario.getNetwork(), scenario.getConfig().network()); - //this is kind of ugly, but otherwise we would need a shp file here to filter for the links with shopping garages -sme0423 - //maybe put this into a facility instead of networkAttribute - managerShop.addLinkFilter(new LinkAttributeNetworkLinkFilter("shoppingGarage", "true")); - - Network shoppingNetwork = managerShop.applyFilters(); - - - } - @Override public PlanAlgorithm getPlanAlgoInstance(){ - return new PlanAlgorithm(){ - @Override public void run( Plan plan ){ - - List trips = TripStructureUtils.getTrips( plan.getPlanElements() ); - - for( TripStructureUtils.Trip oldTrip : trips ){ - //we dont need to check if the trip is routed because only plans affected by SMC will come here - // -> only plans with non-routed trips -// if ( isRouted(oldTrip ) ) { -// continue;; -// } - // else check if parking is affected etc. - - oldTrip.getOriginActivity(); - - oldTrip.getDestinationActivity(); - - - List newTrip = new ArrayList<>(); - -// newTrip.add( oldTrip.getOriginActivity() ); - - newTrip.add( pf.createLeg( TransportMode.walk ) ); - - newTrip.add( pf.createInteractionActivityFromLinkId( ... ) ); - - newTrip .add( pf.createLeg( TransportMode.car ); - - newTrip.add( pf.createInteractionActivityFromLinkId( ... ) ); - - newTrip.add( pf.createLeg( )) - -// newTrip.add( oldTrip.getDestinationActivity() ); - - - TripRouter.insertTrip( - plan, - oldTrip.getOriginActivity(), - newTrip, - oldTrip.getDestinationActivity() ); - - } - - } - }; - } -} +//package org.matsim.run; +// +//import com.google.inject.Inject; +//import org.apache.logging.log4j.LogManager; +//import org.apache.logging.log4j.Logger; +//import org.matsim.api.core.v01.Scenario; +//import org.matsim.api.core.v01.TransportMode; +//import org.matsim.api.core.v01.network.Network; +//import org.matsim.api.core.v01.population.Plan; +//import org.matsim.api.core.v01.population.PlanElement; +//import org.matsim.api.core.v01.population.PopulationFactory; +//import org.matsim.core.config.groups.GlobalConfigGroup; +//import org.matsim.core.network.filter.NetworkFilterManager; +//import org.matsim.core.population.algorithms.PlanAlgorithm; +//import org.matsim.core.replanning.modules.AbstractMultithreadedModule; +//import org.matsim.core.router.TripRouter; +//import org.matsim.core.router.TripStructureUtils; +//import playground.vsp.simpleParkingCostHandler.ParkingCostConfigGroup; +// +//import java.util.ArrayList; +//import java.util.List; +// +//class ParkingCorrectionMultithreadedModule extends AbstractMultithreadedModule { +// +// @Inject +// Scenario scenario; +// +// @Inject +// ParkingCostConfigGroup parkingCostConfigGroup; +// +// +// private static final Logger log = LogManager.getLogger(ParkingCorrectionMultithreadedModule.class ); +// private final PopulationFactory pf; +// +// public ParkingCorrectionMultithreadedModule( GlobalConfigGroup globalConfigGroup, PopulationFactory pf ){ +// super( globalConfigGroup ); +// this.pf = pf; +// +// NetworkFilterManager managerResidential = new NetworkFilterManager(scenario.getNetwork(), scenario.getConfig().network()); +// managerResidential.addLinkFilter(new LinkAttributeNetworkLinkFilter(parkingCostConfigGroup.getResidentialParkingFeeAttributeName(), "0.")); +// +// Network nonResidentialParkingNetwork = managerResidential.applyFilters(); +// +// NetworkFilterManager managerShop = new NetworkFilterManager(scenario.getNetwork(), scenario.getConfig().network()); +// //this is kind of ugly, but otherwise we would need a shp file here to filter for the links with shopping garages -sme0423 +// //maybe put this into a facility instead of networkAttribute +// managerShop.addLinkFilter(new LinkAttributeNetworkLinkFilter("shoppingGarage", "true")); +// +// Network shoppingNetwork = managerShop.applyFilters(); +// +// +// } +// @Override public PlanAlgorithm getPlanAlgoInstance(){ +// return new PlanAlgorithm(){ +// @Override public void run( Plan plan ){ +// +// List trips = TripStructureUtils.getTrips( plan.getPlanElements() ); +// +// for( TripStructureUtils.Trip oldTrip : trips ){ +// //we dont need to check if the trip is routed because only plans affected by SMC will come here +// // -> only plans with non-routed trips +//// if ( isRouted(oldTrip ) ) { +//// continue;; +//// } +// // else check if parking is affected etc. +// +// oldTrip.getOriginActivity(); +// +// oldTrip.getDestinationActivity(); +// +// +// List newTrip = new ArrayList<>(); +// +//// newTrip.add( oldTrip.getOriginActivity() ); +// +// newTrip.add( pf.createLeg( TransportMode.walk ) ); +// +// newTrip.add( pf.createInteractionActivityFromLinkId( ... ) ); +// +// newTrip .add( pf.createLeg( TransportMode.car ); +// +// newTrip.add( pf.createInteractionActivityFromLinkId( ... ) ); +// +// newTrip.add( pf.createLeg( )) +// +//// newTrip.add( oldTrip.getDestinationActivity() ); +// +// +// TripRouter.insertTrip( +// plan, +// oldTrip.getOriginActivity(), +// newTrip, +// oldTrip.getDestinationActivity() ); +// +// } +// +// } +// }; +// } +//} diff --git a/src/main/java/org/matsim/run/SubtourModeChoiceInclParking.java b/src/main/java/org/matsim/run/SubtourModeChoiceInclParking.java index d169ec9e..4fbd69ba 100644 --- a/src/main/java/org/matsim/run/SubtourModeChoiceInclParking.java +++ b/src/main/java/org/matsim/run/SubtourModeChoiceInclParking.java @@ -46,7 +46,7 @@ public class SubtourModeChoiceInclParking implements Provider { public PlanStrategy get() { PlanStrategyImpl.Builder builder = new Builder(new RandomPlanSelector<>()); builder.addStrategyModule(new org.matsim.core.replanning.modules.SubtourModeChoice(globalConfigGroup, subtourModeChoiceConfigGroup, permissibleModesCalculator)); - builder.addStrategyModule( new ParkingCorrectionMultithreadedModule( globalConfigGroup ) ); +// builder.addStrategyModule( new ParkingCorrectionMultithreadedModule( globalConfigGroup ) ); builder.addStrategyModule(new ReRoute(facilities, tripRouterProvider, globalConfigGroup, timeInterpretation)); return builder.build(); } diff --git a/src/main/java/org/matsim/run/prepare/LeipzigUtils.java b/src/main/java/org/matsim/run/prepare/LeipzigUtils.java new file mode 100644 index 00000000..e177826a --- /dev/null +++ b/src/main/java/org/matsim/run/prepare/LeipzigUtils.java @@ -0,0 +1,20 @@ +package org.matsim.run.prepare; + +import org.matsim.api.core.v01.network.Link; + +public class LeipzigUtils{ + private LeipzigUtils(){} // do not instantiate + + public static boolean parkingIsRestricted( Link link ) { + String result = (String) link.getAttributes().getAttribute( "parking" ); + if ( result == null ) { + return false ; + } else { + return true; + } + } + public static void setParkingToRestricted( Link link ){ + link.getAttributes().putAttribute( "parking", "restricted" ); + } + // yy change the logic of the above to enums +} diff --git a/src/test/java/org/matsim/run/ChessboardParkingTest.java b/src/test/java/org/matsim/run/ChessboardParkingTest.java index 066a0fa9..1cab227d 100644 --- a/src/test/java/org/matsim/run/ChessboardParkingTest.java +++ b/src/test/java/org/matsim/run/ChessboardParkingTest.java @@ -1,121 +1,437 @@ package org.matsim.run; -import org.junit.Ignore; +import com.google.inject.Inject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.Rule; import org.junit.Test; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.network.Node; import org.matsim.api.core.v01.population.*; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; +import org.matsim.core.config.groups.GlobalConfigGroup; +import org.matsim.core.config.groups.StrategyConfigGroup; +import org.matsim.core.config.groups.VspExperimentalConfigGroup; +import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; import org.matsim.core.network.NetworkUtils; import org.matsim.core.population.PopulationUtils; +import org.matsim.core.population.algorithms.PlanAlgorithm; +import org.matsim.core.population.routes.NetworkRoute; +import org.matsim.core.replanning.PlanStrategy; +import org.matsim.core.replanning.PlanStrategyImpl; +import org.matsim.core.replanning.modules.AbstractMultithreadedModule; +import org.matsim.core.replanning.selectors.RandomPlanSelector; +import org.matsim.core.router.*; +import org.matsim.core.scenario.MutableScenario; import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.core.utils.io.IOUtils; +import org.matsim.core.utils.timing.TimeInterpretation; +import org.matsim.core.utils.timing.TimeTracker; import org.matsim.examples.ExamplesUtils; -import org.matsim.run.prepare.NetworkOptions; +import org.matsim.facilities.ActivityFacilities; +import org.matsim.facilities.FacilitiesUtils; +import org.matsim.facilities.Facility; +import org.matsim.run.prepare.LeipzigUtils; import org.matsim.testcases.MatsimTestUtils; +import org.matsim.vehicles.Vehicle; import playground.vsp.openberlinscenario.cemdap.output.ActivityTypes; -import java.io.IOException; +import javax.inject.Provider; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; -public class ChessboardParkingTest { - @Rule - public MatsimTestUtils utils = new MatsimTestUtils(); - - - @Test - public final void runChessboardParkingTest() throws IOException { - String inputPath = String.valueOf(ExamplesUtils.getTestScenarioURL("chessboard")); +import static org.matsim.run.prepare.LeipzigUtils.parkingIsRestricted; - Config config = ConfigUtils.loadConfig(inputPath + "/config.xml"); +/** + * abc + */ +public class ChessboardParkingTest { + private static final Logger log = LogManager.getLogger(ChessboardParkingTest.class ); + @Rule public MatsimTestUtils utils = new MatsimTestUtils(); + private static final String HOME_ZONE_ID = "homeLinkId"; + final String RE_ROUTE_LEIPZIG = "ReRouteLeipzig"; + enum Situation{ residentInResidentialArea, residentOutsideResidentialArea, nonResidentInResidentialAreaNoShop, + nonResidentInResidentialAreaShop, nonResidentOutsideResidentialArea, restrictedToNormal, normalToNormal } + + // yyyyyy Bitte auch Tests, wo diese Unterscheidungen am Ziel stattfinden. Danke! kai, apr'23 + + @Test public final void runChessboardParkingTest1() { + URL url = IOUtils.extendUrl( ExamplesUtils.getTestScenarioURL( "chessboard" ), "config.xml" ); + Config config = ConfigUtils.loadConfig( url ); config.controler().setLastIteration(1); - config.controler().setOutputDirectory("output/chessboardParkingTest/"); - config.global().setNumberOfThreads(1); + config.controler().setOutputDirectory( utils.getOutputDirectory() ); + config.global().setNumberOfThreads(0); config.qsim().setNumberOfThreads(1); - Scenario scenario = ScenarioUtils.loadScenario(config); - Population population = PopulationUtils.createPopulation(config); - - createExampleParkingPopulation(population); - - + config.strategy().clearStrategySettings(); + { + StrategyConfigGroup.StrategySettings stratSets = new StrategyConfigGroup.StrategySettings(); + stratSets.setWeight( 1. ); + stratSets.setStrategyName( RE_ROUTE_LEIPZIG ); + config.strategy().addStrategySettings( stratSets ); + } + config.vspExperimental().setVspDefaultsCheckingLevel( VspExperimentalConfigGroup.VspDefaultsCheckingLevel.warn ); + MutableScenario scenario = (MutableScenario) ScenarioUtils.loadScenario(config ); - NetworkOptions networkOptions = new NetworkOptions(); + Population population = PopulationUtils.createPopulation(config); + createExampleParkingPopulation(population, scenario.getNetwork(), Situation.restrictedToNormal ); + scenario.setPopulation( population ); + log.warn("population size=" + scenario.getPopulation().getPersons().size() ); - // help - networkOptions.prepare(scenario.getNetwork()); +// System.exit(-1); +// NetworkOptions networkOptions = new NetworkOptions(); +// // help // yy what does the "help" mean here? +// networkOptions.prepare(scenario.getNetwork()); + // yy I don't know what the above is supposed to do. Thus commented it out. kai, apr'23 Controler controler = new Controler(scenario); + controler.addOverridingModule( new AbstractModule(){ + @Override public void install(){ +// this.bind( MultimodalLinkChooser.class ).toInstance( new LeipzigMultimodalLinkChooser() ); + this.addPlanStrategyBinding( RE_ROUTE_LEIPZIG ).toProvider( LeipzigRoutingStrategyProvider.class ); + // yyyy this only uses it during replanning!!! kai, apr'23 + } + } ); - + controler.run(); } - final void createExampleParkingPopulation(Population population) { + static final class LeipzigPlanRouter implements PlanAlgorithm { + private final TripRouter tripRouter; + private final ActivityFacilities facilities; + private final TimeInterpretation timeInterpretation; + private final Network fullModalNetwork; + private final Network reducedNetwork; + private final MultimodalLinkChooser linkChooser; + private final Scenario scenario; + LeipzigPlanRouter( final TripRouter tripRouter, final ActivityFacilities facilities, final TimeInterpretation timeInterpretation, + SingleModeNetworksCache singleModeNetworksCache, Scenario scenario, MultimodalLinkChooser linkChooser ) { + this.tripRouter = tripRouter; + this.facilities = facilities; + this.timeInterpretation = timeInterpretation; + this.scenario = scenario; + this.fullModalNetwork = singleModeNetworksCache.getSingleModeNetworksCache().get( TransportMode.car ); + + // yyyy one should look at the networks cache and see how the following is done. And maybe even register it there. + this.reducedNetwork = NetworkUtils.createNetwork( scenario.getConfig().network() ); + this.linkChooser = linkChooser; + for( Node node : this.fullModalNetwork.getNodes().values() ){ + reducedNetwork.addNode( node ); + } + for( Link link : this.fullModalNetwork.getLinks().values() ){ + if ( !LeipzigUtils.parkingIsRestricted( link ) ) { + reducedNetwork.addLink( link ); + } + } + log.warn("returning LeipzigPlanRouter"); + } + enum ParkingType { normal, restricted, shopping } + @Override public void run(final Plan plan) { + final List trips = TripStructureUtils.getTrips( plan ); + TimeTracker timeTracker = new TimeTracker(timeInterpretation); + + log.warn( "=== old plan: ===" ); + for( PlanElement tripElement : plan.getPlanElements() ){ + log.warn( tripElement ); + } + log.warn( "======" ); + + for ( TripStructureUtils.Trip oldTrip : trips) { + final String routingMode = TripStructureUtils.identifyMainMode( oldTrip.getTripElements() ); + timeTracker.addActivity(oldTrip.getOriginActivity()); + + final Facility fromFacility = FacilitiesUtils.toFacility( oldTrip.getOriginActivity(), facilities ); + final Facility toFacility = FacilitiesUtils.toFacility( oldTrip.getDestinationActivity(), facilities ); + + // At this point, I only want to deal with residential parking. Shopping comes later (and is simpler). + + ParkingType parkingTypeAtOrigin = getParkingType( fullModalNetwork, oldTrip.getOriginActivity() ); + ParkingType parkingTypeAtDestination = getParkingType( fullModalNetwork, oldTrip.getDestinationActivity() ); + + if ( parkingTypeAtOrigin==ParkingType.normal && parkingTypeAtDestination==ParkingType.normal ){ + // standard case: + + final List newTripElements = tripRouter.calcRoute( routingMode, fromFacility, toFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); + putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); + TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity() ); + timeTracker.addElements( newTripElements ); + } else if ( parkingTypeAtOrigin==ParkingType.restricted && parkingTypeAtDestination==ParkingType.normal ) { + // restricted parking at origin: + + // first find parking: +// final Link parkingLink = NetworkUtils.getNearestLink( reducedNetwork, oldTrip.getOriginActivity().getCoord() ); + + final Link parkingLink = linkChooser.decideOnLink( fromFacility, reducedNetwork ); + + final Activity parkingActivity = scenario.getPopulation().getFactory().createInteractionActivityFromLinkId( + TripStructureUtils.createStageActivityType( "parking" ), parkingLink.getId() ); + final Facility parkingFacility = FacilitiesUtils.toFacility( parkingActivity, facilities ); + + List newTripElements = new ArrayList<>(); + + // trip from origin to parking: + final List walkTripElements = tripRouter.calcRoute( TransportMode.walk, fromFacility, parkingFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); + newTripElements.addAll( walkTripElements ); + + // parking interaction: + newTripElements.add( parkingActivity ); + + // trip from parking to final destination: + final List carTripElements = tripRouter.calcRoute( routingMode, parkingFacility, toFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); + newTripElements.addAll( carTripElements ); + + putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); + TripRouter.insertTrip( plan, parkingActivity, newTripElements, oldTrip.getDestinationActivity() ); + timeTracker.addElements( newTripElements ); + + + } else { + throw new RuntimeException(); + // to be implemented! + } + + } + log.warn( "=== new plan: ===" ); + for( PlanElement tripElement : plan.getPlanElements() ){ + log.warn( tripElement ); + } + log.warn( "======" ); + + } + private static ParkingType getParkingType( Network fullModalNetwork, Activity originActivity ){ + ParkingType parkingTypeAtOrigin = ParkingType.normal; + + // check if non-home activity (since otherwise we assume that there is no parking restriction): + if ( !originActivity.getType().equals( ActivityTypes.HOME ) ) { + + // if non-home activity, check if in restricted zone: + Link link = fullModalNetwork.getLinks().get( originActivity.getLinkId() ); + if ( parkingIsRestricted(link) ) { + parkingTypeAtOrigin = ParkingType.restricted; + } + + } + return parkingTypeAtOrigin; + } + + /** + * If the old trip had vehicles set in its network routes, and it used a single vehicle, + * and if the new trip does not come with vehicles set in its network routes, + * then put the vehicle of the old trip into the network routes of the new trip. + * @param oldTrip The old trip + * @param newTrip The new trip + * @deprecated -- use from PlanRouter + */ + private static void putVehicleFromOldTripIntoNewTripIfMeaningful( TripStructureUtils.Trip oldTrip, List newTrip ) { + Id oldVehicleId = getUniqueVehicleId(oldTrip ); + if (oldVehicleId != null) { + for (Leg leg : TripStructureUtils.getLegs(newTrip)) { + if (leg.getRoute() instanceof NetworkRoute ) { + if (((NetworkRoute) leg.getRoute()).getVehicleId() == null) { + ((NetworkRoute) leg.getRoute()).setVehicleId(oldVehicleId); + } + } + } + } + } + + /** + * @param trip + * @return + * * @deprecated -- use from PlanRouter + */ + private static Id getUniqueVehicleId( TripStructureUtils.Trip trip ) { + Id vehicleId = null; + for (Leg leg : trip.getLegsOnly()) { + if (leg.getRoute() instanceof NetworkRoute) { + if (vehicleId != null && (!vehicleId.equals(((NetworkRoute) leg.getRoute()).getVehicleId()))) { + return null; // The trip uses several vehicles. + } + vehicleId = ((NetworkRoute) leg.getRoute()).getVehicleId(); + } + } + return vehicleId; + } - PopulationFactory factory = population.getFactory(); - - Leg carLeg = factory.createLeg(TransportMode.car); - - Person residentInResidentialArea = factory.createPerson(Id.createPersonId("residentInResidentialArea")); - - Plan plan1 = factory.createPlan(); - //maybe we also need to set start / end times for the activities.. - plan1.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("80"))); - plan1.addLeg(carLeg); - plan1.addActivity(factory.createActivityFromLinkId(ActivityTypes.EDUCATION, Id.createLinkId("81"))); - residentInResidentialArea.addPlan(plan1); - residentInResidentialArea.getAttributes().putAttribute("parkingType", "residential"); - Person residentOutsideResidentialArea = factory.createPerson(Id.createPersonId("residentOutsideResidentialArea")); + } - Plan plan2 = factory.createPlan(); - plan2.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("35"))); - plan2.addLeg(carLeg); - plan2.addActivity(factory.createActivityFromLinkId(ActivityTypes.WORK, Id.createLinkId("31"))); - residentOutsideResidentialArea.addPlan(plan2); - residentOutsideResidentialArea.getAttributes().putAttribute("parkingType", "residential"); + static final class LeipzigRoutingStrategyProvider implements Provider { + // is a provider in matsim core. maybe try without. kai, apr'23 + @Inject private GlobalConfigGroup globalConfigGroup; + @Inject private ActivityFacilities facilities; + @Inject private Provider tripRouterProvider; + @Inject private SingleModeNetworksCache singleModeNetworksCache; + @Inject private Scenario scenario; + @Inject private TimeInterpretation timeInterpretation; + @Inject MultimodalLinkChooser linkChooser; + @Override public PlanStrategy get() { + PlanStrategyImpl.Builder builder = new PlanStrategyImpl.Builder( new RandomPlanSelector<>()) ; + builder.addStrategyModule( new AbstractMultithreadedModule( globalConfigGroup ){ + @Override public final PlanAlgorithm getPlanAlgoInstance() { + return new LeipzigPlanRouter( tripRouterProvider.get(), facilities, timeInterpretation, singleModeNetworksCache, scenario, linkChooser ); + } + } ); + return builder.build() ; + } + } - Person nonResidentInResidentialAreaNoShop = factory.createPerson(Id.createPersonId("nonResidentInResidentialAreaNoShop")); - Plan plan3 = factory.createPlan(); - plan3.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("41"))); - plan3.addLeg(carLeg); - plan3.addActivity(factory.createActivityFromLinkId(ActivityTypes.WORK, Id.createLinkId("45"))); - nonResidentInResidentialAreaNoShop.addPlan(plan3); - nonResidentInResidentialAreaNoShop.getAttributes().putAttribute("parkingType", "non-residential"); +// static final class LeipzigMultimodalLinkChooser implements MultimodalLinkChooser { +// +// private final MultimodalLinkChooser delegate; +// @Inject LeipzigMultimodalLinkChooser() { +// this.delegate = RouterUtils.getMultimodalLinkChooserDefault(); +// } +// +// @Override public Link decideOnLink( Facility facility, Network network, RoutingRequest routingRequest ){ +// Link result = null ; +// +//// Person person = routingRequest.getPerson(); +//// +//// String homeZoneId = (String) person.getAttributes().getAttribute( HOME_ZONE_ID ); +//// +//// if ( homeLinkId.equals( facility.getLinkId().toString() ) ) { +//// +//// fall back on default: +//// if ( result == null ){ +//// result = this.delegate.decideOnLink( facility, network, routingRequest ); +//// } +// return result; +// } +// } + + static void createExampleParkingPopulation( Population population, Network network, Situation situation ) { - Person nonResidentInResidentialAreaShop = factory.createPerson(Id.createPersonId("nonResidentInResidentialAreaShop")); + PopulationFactory factory = population.getFactory(); - Plan plan4 = factory.createPlan(); - plan4.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("40"))); - plan4.addLeg(carLeg); - plan4.addActivity(factory.createActivityFromLinkId(ActivityTypes.SHOPPING, Id.createLinkId("135"))); - nonResidentInResidentialAreaShop.addPlan(plan4); - nonResidentInResidentialAreaNoShop.getAttributes().putAttribute("parkingType", "non-residential"); + Leg carLeg = factory.createLeg(TransportMode.car); - Person nonResidentOutsideResidentialArea = factory.createPerson(Id.createPersonId("nonResidentOutsideResidentialArea")); + switch( situation ) { + case normalToNormal -> { + Link originLink = network.getLinks().get( Id.createLinkId( "80" )); + Link destinationLink = network.getLinks().get( Id.createLinkId( "81" )); + +// LeipzigUtils.setParkingToRestricted( originLink ); + + Person person = factory.createPerson( Id.createPersonId( situation.toString() ) ); + + Plan plan = factory.createPlan(); + final Activity originActivity = factory.createActivityFromLinkId( ActivityTypes.LEISURE, originLink.getId() ); + originActivity.setEndTime( 3600. ); + plan.addActivity( originActivity ); + plan.addLeg( factory.createLeg( TransportMode.car ) ); + plan.addActivity( factory.createActivityFromLinkId( ActivityTypes.LEISURE, destinationLink.getId()) ); + + person.addPlan( plan ); + + population.addPerson( person ); + } + case restrictedToNormal -> { + Link originLink = network.getLinks().get( Id.createLinkId( "80" )); + Link destinationLink = network.getLinks().get( Id.createLinkId( "81" )); + + LeipzigUtils.setParkingToRestricted( originLink ); + + Person person = factory.createPerson( Id.createPersonId( situation.toString() ) ); + + Plan plan = factory.createPlan(); + final Activity originActivity = factory.createActivityFromLinkId( ActivityTypes.LEISURE, originLink.getId() ); + originActivity.setEndTime( 3600. ); + plan.addActivity( originActivity ); + plan.addLeg( factory.createLeg( TransportMode.car ) ); + plan.addActivity( factory.createActivityFromLinkId( ActivityTypes.LEISURE, destinationLink.getId()) ); + + person.addPlan( plan ); + + population.addPerson( person ); + } + + // yy Ich habe den Setup der Fälle, die hier kommen, leider nicht verstanden. Daher habe ich obigen Fall "restrictedToNormal" neu gebaut. Sorry ... kai, apr'23 + + case residentInResidentialArea -> { + Person residentInResidentialArea = factory.createPerson(Id.createPersonId("residentInResidentialArea")); + + Plan plan1 = factory.createPlan(); + //maybe we also need to set start / end times for the activities.. + plan1.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("80"))); + plan1.addLeg(carLeg); + // (yy The way that was set up it would not have worked: You cannot re-use a leg that is also inserted in some other plan. kai, apr'23) + + plan1.addActivity(factory.createActivityFromLinkId(ActivityTypes.EDUCATION, Id.createLinkId("81"))); + residentInResidentialArea.addPlan(plan1); + residentInResidentialArea.getAttributes().putAttribute("parkingType", "residential"); + + population.addPerson(residentInResidentialArea); + } + case residentOutsideResidentialArea -> { + Person residentOutsideResidentialArea = factory.createPerson(Id.createPersonId("residentOutsideResidentialArea")); + + Plan plan2 = factory.createPlan(); + plan2.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("35"))); + plan2.addLeg(carLeg); + plan2.addActivity(factory.createActivityFromLinkId(ActivityTypes.WORK, Id.createLinkId("31"))); + residentOutsideResidentialArea.addPlan(plan2); + residentOutsideResidentialArea.getAttributes().putAttribute("parkingType", "residential"); + + population.addPerson( residentOutsideResidentialArea ); + } + case nonResidentInResidentialAreaNoShop -> { + Person nonResidentInResidentialAreaNoShop = factory.createPerson(Id.createPersonId("nonResidentInResidentialAreaNoShop")); + + Plan plan3 = factory.createPlan(); + plan3.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("41"))); + plan3.addLeg(carLeg); + plan3.addActivity(factory.createActivityFromLinkId(ActivityTypes.WORK, Id.createLinkId("45"))); + nonResidentInResidentialAreaNoShop.addPlan(plan3); + nonResidentInResidentialAreaNoShop.getAttributes().putAttribute("parkingType", "non-residential"); + + population.addPerson( nonResidentInResidentialAreaNoShop ); + } + case nonResidentInResidentialAreaShop -> { + Person nonResidentInResidentialAreaShop = factory.createPerson(Id.createPersonId("nonResidentInResidentialAreaShop")); + + Plan plan4 = factory.createPlan(); + plan4.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("40"))); + plan4.addLeg(carLeg); + plan4.addActivity(factory.createActivityFromLinkId(ActivityTypes.SHOPPING, Id.createLinkId("135"))); + nonResidentInResidentialAreaShop.addPlan(plan4); + nonResidentInResidentialAreaShop.getAttributes().putAttribute("parkingType", "non-residential"); + + population.addPerson(nonResidentInResidentialAreaShop); + } + case nonResidentOutsideResidentialArea -> { + Person nonResidentOutsideResidentialArea = factory.createPerson(Id.createPersonId("nonResidentOutsideResidentialArea")); + + Plan plan5 = factory.createPlan(); + plan5.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("64"))); + plan5.addLeg(carLeg); + plan5.addActivity(factory.createActivityFromLinkId(ActivityTypes.LEISURE, Id.createLinkId("66"))); + nonResidentOutsideResidentialArea.addPlan(plan5); + nonResidentOutsideResidentialArea.getAttributes().putAttribute("parkingType", "non-residential"); + + population.addPerson( nonResidentOutsideResidentialArea ); + } + default -> throw new IllegalStateException( "Unexpected value: " + situation ); + } - Plan plan5 = factory.createPlan(); - plan5.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("64"))); - plan5.addLeg(carLeg); - plan5.addActivity(factory.createActivityFromLinkId(ActivityTypes.LEISURE, Id.createLinkId("66"))); - nonResidentOutsideResidentialArea.addPlan(plan5); - nonResidentOutsideResidentialArea.getAttributes().putAttribute("parkingType", "non-residential"); //residential area is maximum including the following edges (square): 124-126, 34-36, 178-180, 88-90 - population.addPerson(residentInResidentialArea); - population.addPerson(residentOutsideResidentialArea); - population.addPerson(nonResidentOutsideResidentialArea); - population.addPerson(nonResidentInResidentialAreaNoShop); - population.addPerson(nonResidentInResidentialAreaShop); - } } From 3008c74077971e59afd821f76383f638a0df917f Mon Sep 17 00:00:00 2001 From: Kai Nagel Date: Mon, 24 Apr 2023 13:07:36 +0200 Subject: [PATCH 009/106] changes around parking. pull up matsim version --- pom.xml | 2 +- .../org/matsim/run/ChessboardParkingTest.java | 176 +----------------- .../run/LeipzigRouterPlanAlgorithm.java | 156 ++++++++++++++++ 3 files changed, 158 insertions(+), 176 deletions(-) create mode 100644 src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java diff --git a/pom.xml b/pom.xml index 6e890c44..cf31d9ba 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.matsim matsim-all - 15.0-PR2457 + 16.0-PR2530 4.0.0 diff --git a/src/test/java/org/matsim/run/ChessboardParkingTest.java b/src/test/java/org/matsim/run/ChessboardParkingTest.java index 1cab227d..a80d380e 100644 --- a/src/test/java/org/matsim/run/ChessboardParkingTest.java +++ b/src/test/java/org/matsim/run/ChessboardParkingTest.java @@ -11,7 +11,6 @@ import org.matsim.api.core.v01.TransportMode; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; -import org.matsim.api.core.v01.network.Node; import org.matsim.api.core.v01.population.*; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; @@ -20,10 +19,8 @@ import org.matsim.core.config.groups.VspExperimentalConfigGroup; import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; -import org.matsim.core.network.NetworkUtils; import org.matsim.core.population.PopulationUtils; import org.matsim.core.population.algorithms.PlanAlgorithm; -import org.matsim.core.population.routes.NetworkRoute; import org.matsim.core.replanning.PlanStrategy; import org.matsim.core.replanning.PlanStrategyImpl; import org.matsim.core.replanning.modules.AbstractMultithreadedModule; @@ -33,22 +30,14 @@ import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.utils.io.IOUtils; import org.matsim.core.utils.timing.TimeInterpretation; -import org.matsim.core.utils.timing.TimeTracker; import org.matsim.examples.ExamplesUtils; import org.matsim.facilities.ActivityFacilities; -import org.matsim.facilities.FacilitiesUtils; -import org.matsim.facilities.Facility; import org.matsim.run.prepare.LeipzigUtils; import org.matsim.testcases.MatsimTestUtils; -import org.matsim.vehicles.Vehicle; import playground.vsp.openberlinscenario.cemdap.output.ActivityTypes; import javax.inject.Provider; import java.net.URL; -import java.util.ArrayList; -import java.util.List; - -import static org.matsim.run.prepare.LeipzigUtils.parkingIsRestricted; /** * abc @@ -108,169 +97,6 @@ enum Situation{ residentInResidentialArea, residentOutsideResidentialArea, nonRe controler.run(); } - static final class LeipzigPlanRouter implements PlanAlgorithm { - private final TripRouter tripRouter; - private final ActivityFacilities facilities; - private final TimeInterpretation timeInterpretation; - private final Network fullModalNetwork; - private final Network reducedNetwork; - private final MultimodalLinkChooser linkChooser; - private final Scenario scenario; - LeipzigPlanRouter( final TripRouter tripRouter, final ActivityFacilities facilities, final TimeInterpretation timeInterpretation, - SingleModeNetworksCache singleModeNetworksCache, Scenario scenario, MultimodalLinkChooser linkChooser ) { - this.tripRouter = tripRouter; - this.facilities = facilities; - this.timeInterpretation = timeInterpretation; - this.scenario = scenario; - this.fullModalNetwork = singleModeNetworksCache.getSingleModeNetworksCache().get( TransportMode.car ); - - // yyyy one should look at the networks cache and see how the following is done. And maybe even register it there. - this.reducedNetwork = NetworkUtils.createNetwork( scenario.getConfig().network() ); - this.linkChooser = linkChooser; - for( Node node : this.fullModalNetwork.getNodes().values() ){ - reducedNetwork.addNode( node ); - } - for( Link link : this.fullModalNetwork.getLinks().values() ){ - if ( !LeipzigUtils.parkingIsRestricted( link ) ) { - reducedNetwork.addLink( link ); - } - } - log.warn("returning LeipzigPlanRouter"); - } - enum ParkingType { normal, restricted, shopping } - @Override public void run(final Plan plan) { - final List trips = TripStructureUtils.getTrips( plan ); - TimeTracker timeTracker = new TimeTracker(timeInterpretation); - - log.warn( "=== old plan: ===" ); - for( PlanElement tripElement : plan.getPlanElements() ){ - log.warn( tripElement ); - } - log.warn( "======" ); - - for ( TripStructureUtils.Trip oldTrip : trips) { - final String routingMode = TripStructureUtils.identifyMainMode( oldTrip.getTripElements() ); - timeTracker.addActivity(oldTrip.getOriginActivity()); - - final Facility fromFacility = FacilitiesUtils.toFacility( oldTrip.getOriginActivity(), facilities ); - final Facility toFacility = FacilitiesUtils.toFacility( oldTrip.getDestinationActivity(), facilities ); - - // At this point, I only want to deal with residential parking. Shopping comes later (and is simpler). - - ParkingType parkingTypeAtOrigin = getParkingType( fullModalNetwork, oldTrip.getOriginActivity() ); - ParkingType parkingTypeAtDestination = getParkingType( fullModalNetwork, oldTrip.getDestinationActivity() ); - - if ( parkingTypeAtOrigin==ParkingType.normal && parkingTypeAtDestination==ParkingType.normal ){ - // standard case: - - final List newTripElements = tripRouter.calcRoute( routingMode, fromFacility, toFacility, - timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); - putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); - TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity() ); - timeTracker.addElements( newTripElements ); - } else if ( parkingTypeAtOrigin==ParkingType.restricted && parkingTypeAtDestination==ParkingType.normal ) { - // restricted parking at origin: - - // first find parking: -// final Link parkingLink = NetworkUtils.getNearestLink( reducedNetwork, oldTrip.getOriginActivity().getCoord() ); - - final Link parkingLink = linkChooser.decideOnLink( fromFacility, reducedNetwork ); - - final Activity parkingActivity = scenario.getPopulation().getFactory().createInteractionActivityFromLinkId( - TripStructureUtils.createStageActivityType( "parking" ), parkingLink.getId() ); - final Facility parkingFacility = FacilitiesUtils.toFacility( parkingActivity, facilities ); - - List newTripElements = new ArrayList<>(); - - // trip from origin to parking: - final List walkTripElements = tripRouter.calcRoute( TransportMode.walk, fromFacility, parkingFacility, - timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); - newTripElements.addAll( walkTripElements ); - - // parking interaction: - newTripElements.add( parkingActivity ); - - // trip from parking to final destination: - final List carTripElements = tripRouter.calcRoute( routingMode, parkingFacility, toFacility, - timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); - newTripElements.addAll( carTripElements ); - - putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); - TripRouter.insertTrip( plan, parkingActivity, newTripElements, oldTrip.getDestinationActivity() ); - timeTracker.addElements( newTripElements ); - - - } else { - throw new RuntimeException(); - // to be implemented! - } - - } - log.warn( "=== new plan: ===" ); - for( PlanElement tripElement : plan.getPlanElements() ){ - log.warn( tripElement ); - } - log.warn( "======" ); - - } - private static ParkingType getParkingType( Network fullModalNetwork, Activity originActivity ){ - ParkingType parkingTypeAtOrigin = ParkingType.normal; - - // check if non-home activity (since otherwise we assume that there is no parking restriction): - if ( !originActivity.getType().equals( ActivityTypes.HOME ) ) { - - // if non-home activity, check if in restricted zone: - Link link = fullModalNetwork.getLinks().get( originActivity.getLinkId() ); - if ( parkingIsRestricted(link) ) { - parkingTypeAtOrigin = ParkingType.restricted; - } - - } - return parkingTypeAtOrigin; - } - - /** - * If the old trip had vehicles set in its network routes, and it used a single vehicle, - * and if the new trip does not come with vehicles set in its network routes, - * then put the vehicle of the old trip into the network routes of the new trip. - * @param oldTrip The old trip - * @param newTrip The new trip - * @deprecated -- use from PlanRouter - */ - private static void putVehicleFromOldTripIntoNewTripIfMeaningful( TripStructureUtils.Trip oldTrip, List newTrip ) { - Id oldVehicleId = getUniqueVehicleId(oldTrip ); - if (oldVehicleId != null) { - for (Leg leg : TripStructureUtils.getLegs(newTrip)) { - if (leg.getRoute() instanceof NetworkRoute ) { - if (((NetworkRoute) leg.getRoute()).getVehicleId() == null) { - ((NetworkRoute) leg.getRoute()).setVehicleId(oldVehicleId); - } - } - } - } - } - - /** - * @param trip - * @return - * * @deprecated -- use from PlanRouter - */ - private static Id getUniqueVehicleId( TripStructureUtils.Trip trip ) { - Id vehicleId = null; - for (Leg leg : trip.getLegsOnly()) { - if (leg.getRoute() instanceof NetworkRoute) { - if (vehicleId != null && (!vehicleId.equals(((NetworkRoute) leg.getRoute()).getVehicleId()))) { - return null; // The trip uses several vehicles. - } - vehicleId = ((NetworkRoute) leg.getRoute()).getVehicleId(); - } - } - return vehicleId; - } - - - } - static final class LeipzigRoutingStrategyProvider implements Provider { // is a provider in matsim core. maybe try without. kai, apr'23 @Inject private GlobalConfigGroup globalConfigGroup; @@ -284,7 +110,7 @@ static final class LeipzigRoutingStrategyProvider implements Provider()) ; builder.addStrategyModule( new AbstractMultithreadedModule( globalConfigGroup ){ @Override public final PlanAlgorithm getPlanAlgoInstance() { - return new LeipzigPlanRouter( tripRouterProvider.get(), facilities, timeInterpretation, singleModeNetworksCache, scenario, linkChooser ); + return new LeipzigRouterPlanAlgorithm( tripRouterProvider.get(), facilities, timeInterpretation, singleModeNetworksCache, scenario, linkChooser ); } } ); return builder.build() ; diff --git a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java new file mode 100644 index 00000000..8ea9000a --- /dev/null +++ b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -0,0 +1,156 @@ +package org.matsim.run; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.network.Node; +import org.matsim.api.core.v01.population.Activity; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.api.core.v01.population.PlanElement; +import org.matsim.core.network.NetworkUtils; +import org.matsim.core.population.algorithms.PlanAlgorithm; +import org.matsim.core.router.MultimodalLinkChooser; +import org.matsim.core.router.SingleModeNetworksCache; +import org.matsim.core.router.TripRouter; +import org.matsim.core.router.TripStructureUtils; +import org.matsim.core.utils.timing.TimeInterpretation; +import org.matsim.core.utils.timing.TimeTracker; +import org.matsim.facilities.ActivityFacilities; +import org.matsim.facilities.FacilitiesUtils; +import org.matsim.facilities.Facility; +import org.matsim.run.prepare.LeipzigUtils; +import playground.vsp.openberlinscenario.cemdap.output.ActivityTypes; + +import java.util.ArrayList; +import java.util.List; + +import static org.matsim.core.router.PlanRouter.putVehicleFromOldTripIntoNewTripIfMeaningful; +import static org.matsim.run.prepare.LeipzigUtils.parkingIsRestricted; + +final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ + private static final Logger log = LogManager.getLogger(LeipzigRouterPlanAlgorithm.class ); + private final TripRouter tripRouter; + private final ActivityFacilities facilities; + private final TimeInterpretation timeInterpretation; + private final Network fullModalNetwork; + private final Network reducedNetwork; + private final MultimodalLinkChooser linkChooser; + private final Scenario scenario; + LeipzigRouterPlanAlgorithm( final TripRouter tripRouter, final ActivityFacilities facilities, final TimeInterpretation timeInterpretation, + SingleModeNetworksCache singleModeNetworksCache, Scenario scenario, MultimodalLinkChooser linkChooser ){ + this.tripRouter = tripRouter; + this.facilities = facilities; + this.timeInterpretation = timeInterpretation; + this.scenario = scenario; + this.fullModalNetwork = singleModeNetworksCache.getSingleModeNetworksCache().get( TransportMode.car ); + + // yyyy one should look at the networks cache and see how the following is done. And maybe even register it there. + this.reducedNetwork = NetworkUtils.createNetwork( scenario.getConfig().network() ); + this.linkChooser = linkChooser; + for( Node node : this.fullModalNetwork.getNodes().values() ){ + reducedNetwork.addNode( node ); + } + for( Link link : this.fullModalNetwork.getLinks().values() ){ + if( !LeipzigUtils.parkingIsRestricted( link ) ){ + reducedNetwork.addLink( link ); + } + } + log.warn( "returning LeipzigPlanRouter" ); + } + enum ParkingType{normal, restricted, shopping} + @Override public void run( final Plan plan ){ + final List trips = TripStructureUtils.getTrips( plan ); + TimeTracker timeTracker = new TimeTracker( timeInterpretation ); + + log.warn( "=== old plan: ===" ); + for( PlanElement tripElement : plan.getPlanElements() ){ + log.warn( tripElement ); + } + log.warn( "======" ); + + for( TripStructureUtils.Trip oldTrip : trips ){ + final String routingMode = TripStructureUtils.identifyMainMode( oldTrip.getTripElements() ); + timeTracker.addActivity( oldTrip.getOriginActivity() ); + + final Facility fromFacility = FacilitiesUtils.toFacility( oldTrip.getOriginActivity(), facilities ); + final Facility toFacility = FacilitiesUtils.toFacility( oldTrip.getDestinationActivity(), facilities ); + + // At this point, I only want to deal with residential parking. Shopping comes later (and is simpler). + + ParkingType parkingTypeAtOrigin = getParkingType( fullModalNetwork, oldTrip.getOriginActivity() ); + ParkingType parkingTypeAtDestination = getParkingType( fullModalNetwork, oldTrip.getDestinationActivity() ); + + if( parkingTypeAtOrigin == ParkingType.normal && parkingTypeAtDestination == ParkingType.normal ){ + // standard case: + + final List newTripElements = tripRouter.calcRoute( routingMode, fromFacility, toFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); + putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); + TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity() ); + timeTracker.addElements( newTripElements ); + } else if( parkingTypeAtOrigin == ParkingType.restricted && parkingTypeAtDestination == ParkingType.normal ){ + // restricted parking at origin: + + // first find parking: +// final Link parkingLink = NetworkUtils.getNearestLink( reducedNetwork, oldTrip.getOriginActivity().getCoord() ); + + final Link parkingLink = linkChooser.decideOnLink( fromFacility, reducedNetwork ); + + final Activity parkingActivity = scenario.getPopulation().getFactory().createInteractionActivityFromLinkId( + TripStructureUtils.createStageActivityType( "parking" ), parkingLink.getId() ); + final Facility parkingFacility = FacilitiesUtils.toFacility( parkingActivity, facilities ); + + List newTripElements = new ArrayList<>(); + + // trip from origin to parking: + final List walkTripElements = tripRouter.calcRoute( TransportMode.walk, fromFacility, parkingFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); + newTripElements.addAll( walkTripElements ); + + // parking interaction: + newTripElements.add( parkingActivity ); + + // trip from parking to final destination: + final List carTripElements = tripRouter.calcRoute( routingMode, parkingFacility, toFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); + newTripElements.addAll( carTripElements ); + + putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); + TripRouter.insertTrip( plan, parkingActivity, newTripElements, oldTrip.getDestinationActivity() ); + timeTracker.addElements( newTripElements ); + + + } else{ + throw new RuntimeException(); + // to be implemented! + } + + } + log.warn( "=== new plan: ===" ); + for( PlanElement tripElement : plan.getPlanElements() ){ + log.warn( tripElement ); + } + log.warn( "======" ); + + } + private static ParkingType getParkingType( Network fullModalNetwork, Activity originActivity ){ + ParkingType parkingTypeAtOrigin = ParkingType.normal; + + // check if non-home activity (since otherwise we assume that there is no parking restriction): + if( !originActivity.getType().equals( ActivityTypes.HOME ) ){ + + // if non-home activity, check if in restricted zone: + Link link = fullModalNetwork.getLinks().get( originActivity.getLinkId() ); + if( parkingIsRestricted( link ) ){ + parkingTypeAtOrigin = ParkingType.restricted; + } + + } + return parkingTypeAtOrigin; + } + + +} From 5af8021e8c01647a849cd82049e12c4fcdba608b Mon Sep 17 00:00:00 2001 From: Kai Nagel Date: Mon, 24 Apr 2023 13:13:02 +0200 Subject: [PATCH 010/106] example for shp file based test --- .../org/matsim/run/ChessboardParkingTest.java | 53 ++++++++----------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/src/test/java/org/matsim/run/ChessboardParkingTest.java b/src/test/java/org/matsim/run/ChessboardParkingTest.java index a80d380e..64d97c62 100644 --- a/src/test/java/org/matsim/run/ChessboardParkingTest.java +++ b/src/test/java/org/matsim/run/ChessboardParkingTest.java @@ -48,7 +48,7 @@ public class ChessboardParkingTest { private static final String HOME_ZONE_ID = "homeLinkId"; final String RE_ROUTE_LEIPZIG = "ReRouteLeipzig"; enum Situation{ residentInResidentialArea, residentOutsideResidentialArea, nonResidentInResidentialAreaNoShop, - nonResidentInResidentialAreaShop, nonResidentOutsideResidentialArea, restrictedToNormal, normalToNormal } + nonResidentInResidentialAreaShop, nonResidentOutsideResidentialArea, restrictedToNormal, normalToNormal,fromShpFile } // yyyyyy Bitte auch Tests, wo diese Unterscheidungen am Ziel stattfinden. Danke! kai, apr'23 @@ -148,48 +148,39 @@ static void createExampleParkingPopulation( Population population, Network netwo Leg carLeg = factory.createLeg(TransportMode.car); - switch( situation ) { - case normalToNormal -> { - Link originLink = network.getLinks().get( Id.createLinkId( "80" )); - Link destinationLink = network.getLinks().get( Id.createLinkId( "81" )); + Link originLink = network.getLinks().get( Id.createLinkId( "80" )); + Link destinationLink = network.getLinks().get( Id.createLinkId( "81" )); -// LeipzigUtils.setParkingToRestricted( originLink ); + Person person = factory.createPerson( Id.createPersonId( situation.toString() ) ); - Person person = factory.createPerson( Id.createPersonId( situation.toString() ) ); + Plan plan = factory.createPlan(); + final Activity originActivity = factory.createActivityFromLinkId( ActivityTypes.LEISURE, originLink.getId() ); + originActivity.setEndTime( 3600. ); + plan.addActivity( originActivity ); + plan.addLeg( factory.createLeg( TransportMode.car ) ); + plan.addActivity( factory.createActivityFromLinkId( ActivityTypes.LEISURE, destinationLink.getId()) ); - Plan plan = factory.createPlan(); - final Activity originActivity = factory.createActivityFromLinkId( ActivityTypes.LEISURE, originLink.getId() ); - originActivity.setEndTime( 3600. ); - plan.addActivity( originActivity ); - plan.addLeg( factory.createLeg( TransportMode.car ) ); - plan.addActivity( factory.createActivityFromLinkId( ActivityTypes.LEISURE, destinationLink.getId()) ); + person.addPlan( plan ); - person.addPlan( plan ); + population.addPerson( person ); - population.addPerson( person ); + switch( situation ) { + case normalToNormal -> { + // do nothing } case restrictedToNormal -> { - Link originLink = network.getLinks().get( Id.createLinkId( "80" )); - Link destinationLink = network.getLinks().get( Id.createLinkId( "81" )); - LeipzigUtils.setParkingToRestricted( originLink ); - - Person person = factory.createPerson( Id.createPersonId( situation.toString() ) ); - - Plan plan = factory.createPlan(); - final Activity originActivity = factory.createActivityFromLinkId( ActivityTypes.LEISURE, originLink.getId() ); - originActivity.setEndTime( 3600. ); - plan.addActivity( originActivity ); - plan.addLeg( factory.createLeg( TransportMode.car ) ); - plan.addActivity( factory.createActivityFromLinkId( ActivityTypes.LEISURE, destinationLink.getId()) ); - - person.addPlan( plan ); - - population.addPerson( person ); } // yy Ich habe den Setup der Fälle, die hier kommen, leider nicht verstanden. Daher habe ich obigen Fall "restrictedToNormal" neu gebaut. Sorry ... kai, apr'23 + case fromShpFile -> { +// for( Link link : network.getLinks().values() ){ +// if ( link is in polygon ) { +// LeipzigUtils.parkingIsRestricted( link ); +// } +// } + } case residentInResidentialArea -> { Person residentInResidentialArea = factory.createPerson(Id.createPersonId("residentInResidentialArea")); From 732849eff54d9cd6136a119418e8de0e109e31c3 Mon Sep 17 00:00:00 2001 From: Kai Nagel Date: Tue, 25 Apr 2023 04:48:53 +0200 Subject: [PATCH 011/106] now seems to be working. Problem was that the chessboard example uses facilities from file. I cannot say if they are not set up correctly or if that execution path is not fully debugged, but it is something that we rarely use at VSP. The output now is as follows. Can't say if we should stick with the parking activity, or just use car interaction. 2023-04-25T04:45:12,079 WARN LeipzigRouterPlanAlgorithm:62 returning LeipzigPlanRouter 2023-04-25T04:45:12,080 WARN LeipzigRouterPlanAlgorithm:69 === old plan: === 2023-04-25T04:45:12,083 WARN LeipzigRouterPlanAlgorithm:71 act [type=leisure][coord=null][linkId=80][startTime=undefined][endTime=01:00:00][duration=undefined][facilityId=f_auto_80] 2023-04-25T04:45:12,095 WARN LeipzigRouterPlanAlgorithm:71 leg [mode=walk][depTime=01:00:00][travTime=00:00:00][arrTime=01:00:00][route= startLinkId=80 endLinkId=80 travTime=0.0 dist=0.0] 2023-04-25T04:45:12,095 WARN LeipzigRouterPlanAlgorithm:71 act [type=car interaction][coord=[x=7000.0 | y=7000.0]][linkId=80][startTime=undefined][endTime=undefined][duration=00:00:00][facilityId=null] 2023-04-25T04:45:12,096 WARN LeipzigRouterPlanAlgorithm:71 leg [mode=car][depTime=01:00:00][travTime=00:13:19][arrTime=01:13:19][route= startLinkId=80 endLinkId=81 travTime=799.0 dist=7000.0 linkIds=[170, 44, 134, 133, 36, 178] travelCost=2.6666600000000003] 2023-04-25T04:45:12,096 WARN LeipzigRouterPlanAlgorithm:71 act [type=car interaction][coord=[x=8000.0 | y=7000.0]][linkId=81][startTime=undefined][endTime=undefined][duration=00:00:00][facilityId=null] 2023-04-25T04:45:12,096 WARN LeipzigRouterPlanAlgorithm:71 leg [mode=walk][depTime=01:13:19][travTime=00:00:00][arrTime=01:13:19][route= startLinkId=81 endLinkId=81 travTime=0.0 dist=0.0] 2023-04-25T04:45:12,096 WARN LeipzigRouterPlanAlgorithm:71 act [type=leisure][coord=null][linkId=81][startTime=undefined][endTime=undefined][duration=undefined][facilityId=f_auto_81] 2023-04-25T04:45:12,096 WARN LeipzigRouterPlanAlgorithm:73 ====== 2023-04-25T04:45:12,096 WARN LeipzigRouterPlanAlgorithm:82 fromFacility=[org.matsim.facilities.ActivityFacilityImpl@d84b3a2 ID=f_auto_80| linkID=80| nof_activities=1] 2023-04-25T04:45:12,097 INFO NetworkImpl:419 building QuadTree for nodes: xrange(-1.0,9001.0); yrange(-1.0,9001.0) 2023-04-25T04:45:12,099 INFO NetworkImpl:428 Building QuadTree took 0.001 seconds. 2023-04-25T04:45:12,101 WARN LeipzigRouterPlanAlgorithm:141 === new plan: === 2023-04-25T04:45:12,101 WARN LeipzigRouterPlanAlgorithm:143 act [type=leisure][coord=null][linkId=80][startTime=undefined][endTime=01:00:00][duration=undefined][facilityId=f_auto_80] 2023-04-25T04:45:12,102 WARN LeipzigRouterPlanAlgorithm:143 leg [mode=walk][depTime=01:00:00][travTime=00:00:00][arrTime=01:00:00][route= startLinkId=80 endLinkId=169 travTime=0.0 dist=0.0] 2023-04-25T04:45:12,102 WARN LeipzigRouterPlanAlgorithm:143 act [type=parking interaction][coord=null][linkId=169][startTime=undefined][endTime=undefined][duration=00:00:00][facilityId=null] 2023-04-25T04:45:12,102 WARN LeipzigRouterPlanAlgorithm:143 leg [mode=car][depTime=01:00:00][travTime=00:13:21][arrTime=01:13:21][route= startLinkId=169 endLinkId=81 travTime=801.0 dist=7000.0 linkIds=[79, 124, 34, 35, 36, 178] travelCost=2.6711066666666667] 2023-04-25T04:45:12,102 WARN LeipzigRouterPlanAlgorithm:143 act [type=car interaction][coord=[x=8000.0 | y=7000.0]][linkId=81][startTime=undefined][endTime=undefined][duration=00:00:00][facilityId=null] 2023-04-25T04:45:12,102 WARN LeipzigRouterPlanAlgorithm:143 leg [mode=walk][depTime=01:13:21][travTime=00:00:00][arrTime=01:13:21][route= startLinkId=81 endLinkId=81 travTime=0.0 dist=0.0] 2023-04-25T04:45:12,102 WARN LeipzigRouterPlanAlgorithm:143 act [type=leisure][coord=null][linkId=81][startTime=undefined][endTime=undefined][duration=undefined][facilityId=f_auto_81] 2023-04-25T04:45:12,102 WARN LeipzigRouterPlanAlgorithm:145 ====== --- .../org/matsim/run/ChessboardParkingTest.java | 12 +++++++++--- .../matsim/run/LeipzigRouterPlanAlgorithm.java | 15 ++++++++++++--- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/test/java/org/matsim/run/ChessboardParkingTest.java b/src/test/java/org/matsim/run/ChessboardParkingTest.java index 64d97c62..45fd7eaa 100644 --- a/src/test/java/org/matsim/run/ChessboardParkingTest.java +++ b/src/test/java/org/matsim/run/ChessboardParkingTest.java @@ -14,9 +14,7 @@ import org.matsim.api.core.v01.population.*; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.GlobalConfigGroup; -import org.matsim.core.config.groups.StrategyConfigGroup; -import org.matsim.core.config.groups.VspExperimentalConfigGroup; +import org.matsim.core.config.groups.*; import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; import org.matsim.core.population.PopulationUtils; @@ -39,6 +37,8 @@ import javax.inject.Provider; import java.net.URL; +import static org.matsim.core.config.groups.PlanCalcScoreConfigGroup.*; + /** * abc */ @@ -60,6 +60,8 @@ enum Situation{ residentInResidentialArea, residentOutsideResidentialArea, nonRe config.global().setNumberOfThreads(0); config.qsim().setNumberOfThreads(1); + config.plansCalcRoute().setAccessEgressType( PlansCalcRouteConfigGroup.AccessEgressType.accessEgressModeToLink ); + config.strategy().clearStrategySettings(); { StrategyConfigGroup.StrategySettings stratSets = new StrategyConfigGroup.StrategySettings(); @@ -68,6 +70,10 @@ enum Situation{ residentInResidentialArea, residentOutsideResidentialArea, nonRe config.strategy().addStrategySettings( stratSets ); } + config.facilities().setFacilitiesSource( FacilitiesConfigGroup.FacilitiesSource.onePerActivityLinkInPlansFile ); + + config.planCalcScore().addActivityParams( new ActivityParams( TripStructureUtils.createStageActivityType( "parking" ) ).setScoringThisActivityAtAll( false ) ); + config.vspExperimental().setVspDefaultsCheckingLevel( VspExperimentalConfigGroup.VspDefaultsCheckingLevel.warn ); MutableScenario scenario = (MutableScenario) ScenarioUtils.loadScenario(config ); diff --git a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index 8ea9000a..a7d46fdf 100644 --- a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -8,6 +8,7 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.network.Node; import org.matsim.api.core.v01.population.Activity; +import org.matsim.api.core.v01.population.Leg; import org.matsim.api.core.v01.population.Plan; import org.matsim.api.core.v01.population.PlanElement; import org.matsim.core.network.NetworkUtils; @@ -78,6 +79,9 @@ enum ParkingType{normal, restricted, shopping} final Facility fromFacility = FacilitiesUtils.toFacility( oldTrip.getOriginActivity(), facilities ); final Facility toFacility = FacilitiesUtils.toFacility( oldTrip.getDestinationActivity(), facilities ); + log.warn("fromFacility=" + fromFacility); +// System.exit(-1); + // At this point, I only want to deal with residential parking. Shopping comes later (and is simpler). ParkingType parkingTypeAtOrigin = getParkingType( fullModalNetwork, oldTrip.getOriginActivity() ); @@ -95,8 +99,7 @@ enum ParkingType{normal, restricted, shopping} // restricted parking at origin: // first find parking: -// final Link parkingLink = NetworkUtils.getNearestLink( reducedNetwork, oldTrip.getOriginActivity().getCoord() ); - +// final Link parkingLink = NetworkUtils.getNearestLink( reducedNetwork, oldTrip.getOriginActivity().getCoord() ); final Link parkingLink = linkChooser.decideOnLink( fromFacility, reducedNetwork ); final Activity parkingActivity = scenario.getPopulation().getFactory().createInteractionActivityFromLinkId( @@ -108,6 +111,11 @@ enum ParkingType{normal, restricted, shopping} // trip from origin to parking: final List walkTripElements = tripRouter.calcRoute( TransportMode.walk, fromFacility, parkingFacility, timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); + for( PlanElement tripElement : walkTripElements ){ + if ( tripElement instanceof Leg ) { + TripStructureUtils.setRoutingMode( (Leg) tripElement, TransportMode.car ); + } + } newTripElements.addAll( walkTripElements ); // parking interaction: @@ -118,8 +126,9 @@ enum ParkingType{normal, restricted, shopping} timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); newTripElements.addAll( carTripElements ); + putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); - TripRouter.insertTrip( plan, parkingActivity, newTripElements, oldTrip.getDestinationActivity() ); + TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity() ); timeTracker.addElements( newTripElements ); From 34e20fc6f0e4ba3285236fb7f23f7fd0cbabd19b Mon Sep 17 00:00:00 2001 From: simei94 Date: Tue, 25 Apr 2023 19:17:02 +0200 Subject: [PATCH 012/106] add funtionality to tag residentialParking on persons --- .../run/prepare/AssignParkingAttributes.java | 44 +++++++++++++++++++ .../org/matsim/run/prepare/LeipzigUtils.java | 16 +++++++ 2 files changed, 60 insertions(+) create mode 100644 src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java diff --git a/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java b/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java new file mode 100644 index 00000000..38a82b28 --- /dev/null +++ b/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java @@ -0,0 +1,44 @@ +package org.matsim.run.prepare; + +import org.locationtech.jts.geom.Geometry; +import org.matsim.api.core.v01.population.Activity; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.PlanElement; +import org.matsim.api.core.v01.population.Population; +import org.matsim.application.options.ShpOptions; +import org.matsim.core.utils.geometry.geotools.MGC; +import playground.vsp.openberlinscenario.cemdap.output.ActivityTypes; + +final class AssignParkingAttributes { + + AssignParkingAttributes() {} + + static void addParkingAttributesToPopulation(Population population, ShpOptions shp) { + + Geometry restrictedParkingArea = shp.getGeometry(); + boolean isInsideRestrictedParkingArea; + + for (Person person : population.getPersons().values()) { + for (PlanElement element : person.getSelectedPlan().getPlanElements()) { + if (element instanceof Activity) { + + Activity activity = (Activity) element; + + if (!activity.getType().contains(ActivityTypes.HOME)) { + continue; + } + + isInsideRestrictedParkingArea = MGC.coord2Point(activity.getCoord()).within(restrictedParkingArea); + + if (isInsideRestrictedParkingArea) { + LeipzigUtils.setParkingToRestricted(person); + } else { + LeipzigUtils.setParkingToNonRestricted(person); + } + + break; + } + } + } + } +} diff --git a/src/main/java/org/matsim/run/prepare/LeipzigUtils.java b/src/main/java/org/matsim/run/prepare/LeipzigUtils.java index e177826a..9b6137f0 100644 --- a/src/main/java/org/matsim/run/prepare/LeipzigUtils.java +++ b/src/main/java/org/matsim/run/prepare/LeipzigUtils.java @@ -1,6 +1,8 @@ package org.matsim.run.prepare; import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.population.Person; +import playground.vsp.simpleParkingCostHandler.ParkingCostConfigGroup; public class LeipzigUtils{ private LeipzigUtils(){} // do not instantiate @@ -17,4 +19,18 @@ public static void setParkingToRestricted( Link link ){ link.getAttributes().putAttribute( "parking", "restricted" ); } // yy change the logic of the above to enums + + public static void setParkingToRestricted(Person person) { + person.getAttributes().putAttribute("parkingType", "residentialParking"); + } + + public static void setParkingToNonRestricted(Person person) { + person.getAttributes().putAttribute("parkingType", "nonResidentialParking"); + } + + //TODO put this into PrepareNetwork.prepareParkingCost after merge, lines 195-197 + //TODO also add in ParkingCapacityAttacher lines 74-79 + public static void setLinkParkingCostAttributes(Link link, String attributeName, double attributeValue) { + link.getAttributes().putAttribute(attributeName, attributeValue); + } } From 54ab7b8cce7c8d40697a4c843705dcd2c9f2b657 Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Thu, 27 Apr 2023 19:39:13 +0200 Subject: [PATCH 013/106] start implementing assert statements --- .../org/matsim/run/ChessboardParkingTest.java | 167 ++++++++++++------ 1 file changed, 113 insertions(+), 54 deletions(-) diff --git a/src/test/java/org/matsim/run/ChessboardParkingTest.java b/src/test/java/org/matsim/run/ChessboardParkingTest.java index 45fd7eaa..eb6a5bbd 100644 --- a/src/test/java/org/matsim/run/ChessboardParkingTest.java +++ b/src/test/java/org/matsim/run/ChessboardParkingTest.java @@ -4,11 +4,17 @@ import com.google.inject.Inject; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.events.ActivityEndEvent; +import org.matsim.api.core.v01.events.ActivityStartEvent; +import org.matsim.api.core.v01.events.Event; +import org.matsim.api.core.v01.events.handler.ActivityEndEventHandler; +import org.matsim.api.core.v01.events.handler.ActivityStartEventHandler; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.population.*; @@ -36,6 +42,10 @@ import javax.inject.Provider; import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; import static org.matsim.core.config.groups.PlanCalcScoreConfigGroup.*; @@ -43,45 +53,50 @@ * abc */ public class ChessboardParkingTest { - private static final Logger log = LogManager.getLogger(ChessboardParkingTest.class ); - @Rule public MatsimTestUtils utils = new MatsimTestUtils(); + private static final Logger log = LogManager.getLogger(ChessboardParkingTest.class); + @Rule + public MatsimTestUtils utils = new MatsimTestUtils(); private static final String HOME_ZONE_ID = "homeLinkId"; final String RE_ROUTE_LEIPZIG = "ReRouteLeipzig"; - enum Situation{ residentInResidentialArea, residentOutsideResidentialArea, nonResidentInResidentialAreaNoShop, - nonResidentInResidentialAreaShop, nonResidentOutsideResidentialArea, restrictedToNormal, normalToNormal,fromShpFile } + + enum Situation { + residentInResidentialArea, residentOutsideResidentialArea, nonResidentInResidentialAreaNoShop, + nonResidentInResidentialAreaShop, nonResidentOutsideResidentialArea, restrictedToNormal, normalToNormal, fromShpFile + } // yyyyyy Bitte auch Tests, wo diese Unterscheidungen am Ziel stattfinden. Danke! kai, apr'23 - @Test public final void runChessboardParkingTest1() { - URL url = IOUtils.extendUrl( ExamplesUtils.getTestScenarioURL( "chessboard" ), "config.xml" ); - Config config = ConfigUtils.loadConfig( url ); + @Test + public final void runChessboardParkingTest1() { + URL url = IOUtils.extendUrl(ExamplesUtils.getTestScenarioURL("chessboard"), "config.xml"); + Config config = ConfigUtils.loadConfig(url); config.controler().setLastIteration(1); - config.controler().setOutputDirectory( utils.getOutputDirectory() ); + config.controler().setOutputDirectory(utils.getOutputDirectory()); config.global().setNumberOfThreads(0); config.qsim().setNumberOfThreads(1); - config.plansCalcRoute().setAccessEgressType( PlansCalcRouteConfigGroup.AccessEgressType.accessEgressModeToLink ); + config.plansCalcRoute().setAccessEgressType(PlansCalcRouteConfigGroup.AccessEgressType.accessEgressModeToLink); config.strategy().clearStrategySettings(); { StrategyConfigGroup.StrategySettings stratSets = new StrategyConfigGroup.StrategySettings(); - stratSets.setWeight( 1. ); - stratSets.setStrategyName( RE_ROUTE_LEIPZIG ); - config.strategy().addStrategySettings( stratSets ); + stratSets.setWeight(1.); + stratSets.setStrategyName(RE_ROUTE_LEIPZIG); + config.strategy().addStrategySettings(stratSets); } - config.facilities().setFacilitiesSource( FacilitiesConfigGroup.FacilitiesSource.onePerActivityLinkInPlansFile ); + config.facilities().setFacilitiesSource(FacilitiesConfigGroup.FacilitiesSource.onePerActivityLinkInPlansFile); - config.planCalcScore().addActivityParams( new ActivityParams( TripStructureUtils.createStageActivityType( "parking" ) ).setScoringThisActivityAtAll( false ) ); + config.planCalcScore().addActivityParams(new ActivityParams(TripStructureUtils.createStageActivityType("parking")).setScoringThisActivityAtAll(false)); - config.vspExperimental().setVspDefaultsCheckingLevel( VspExperimentalConfigGroup.VspDefaultsCheckingLevel.warn ); + config.vspExperimental().setVspDefaultsCheckingLevel(VspExperimentalConfigGroup.VspDefaultsCheckingLevel.warn); - MutableScenario scenario = (MutableScenario) ScenarioUtils.loadScenario(config ); + MutableScenario scenario = (MutableScenario) ScenarioUtils.loadScenario(config); Population population = PopulationUtils.createPopulation(config); - createExampleParkingPopulation(population, scenario.getNetwork(), Situation.restrictedToNormal ); - scenario.setPopulation( population ); - log.warn("population size=" + scenario.getPopulation().getPersons().size() ); + createExampleParkingPopulation(population, scenario.getNetwork(), Situation.restrictedToNormal); + scenario.setPopulation(population); + log.warn("population size=" + scenario.getPopulation().getPersons().size()); // System.exit(-1); @@ -92,34 +107,57 @@ enum Situation{ residentInResidentialArea, residentOutsideResidentialArea, nonRe Controler controler = new Controler(scenario); - controler.addOverridingModule( new AbstractModule(){ - @Override public void install(){ + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { // this.bind( MultimodalLinkChooser.class ).toInstance( new LeipzigMultimodalLinkChooser() ); - this.addPlanStrategyBinding( RE_ROUTE_LEIPZIG ).toProvider( LeipzigRoutingStrategyProvider.class ); + this.addPlanStrategyBinding(RE_ROUTE_LEIPZIG).toProvider(LeipzigRoutingStrategyProvider.class); // yyyy this only uses it during replanning!!! kai, apr'23 } - } ); + }); + TestParkingListener handler = new TestParkingListener(); + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { + addEventHandlerBinding().toInstance(handler); + } + }); controler.run(); + + Assert.assertTrue(handler.parkingActivities.containsKey(Id.createPersonId(String.valueOf(Situation.restrictedToNormal)))); + Assert.assertEquals("wrong number of parking activites!", 1, handler.parkingActivities.get(Id.createPersonId(String.valueOf(Situation.restrictedToNormal))).size()); + Assert.assertEquals("wrong link", Id.createLinkId("169"), handler.parkingActivities.get(Id.createPersonId(String.valueOf(Situation.restrictedToNormal))).get(0).getLinkId()); + } static final class LeipzigRoutingStrategyProvider implements Provider { // is a provider in matsim core. maybe try without. kai, apr'23 - @Inject private GlobalConfigGroup globalConfigGroup; - @Inject private ActivityFacilities facilities; - @Inject private Provider tripRouterProvider; - @Inject private SingleModeNetworksCache singleModeNetworksCache; - @Inject private Scenario scenario; - @Inject private TimeInterpretation timeInterpretation; - @Inject MultimodalLinkChooser linkChooser; - @Override public PlanStrategy get() { - PlanStrategyImpl.Builder builder = new PlanStrategyImpl.Builder( new RandomPlanSelector<>()) ; - builder.addStrategyModule( new AbstractMultithreadedModule( globalConfigGroup ){ - @Override public final PlanAlgorithm getPlanAlgoInstance() { - return new LeipzigRouterPlanAlgorithm( tripRouterProvider.get(), facilities, timeInterpretation, singleModeNetworksCache, scenario, linkChooser ); + @Inject + private GlobalConfigGroup globalConfigGroup; + @Inject + private ActivityFacilities facilities; + @Inject + private Provider tripRouterProvider; + @Inject + private SingleModeNetworksCache singleModeNetworksCache; + @Inject + private Scenario scenario; + @Inject + private TimeInterpretation timeInterpretation; + @Inject + MultimodalLinkChooser linkChooser; + + @Override + public PlanStrategy get() { + PlanStrategyImpl.Builder builder = new PlanStrategyImpl.Builder(new RandomPlanSelector<>()); + builder.addStrategyModule(new AbstractMultithreadedModule(globalConfigGroup) { + @Override + public final PlanAlgorithm getPlanAlgoInstance() { + return new LeipzigRouterPlanAlgorithm(tripRouterProvider.get(), facilities, timeInterpretation, singleModeNetworksCache, scenario, linkChooser); } - } ); - return builder.build() ; + }); + return builder.build(); } } @@ -148,34 +186,34 @@ static final class LeipzigRoutingStrategyProvider implements Provider { // do nothing } case restrictedToNormal -> { - LeipzigUtils.setParkingToRestricted( originLink ); + LeipzigUtils.setParkingToRestricted(originLink); } // yy Ich habe den Setup der Fälle, die hier kommen, leider nicht verstanden. Daher habe ich obigen Fall "restrictedToNormal" neu gebaut. Sorry ... kai, apr'23 @@ -212,7 +250,7 @@ static void createExampleParkingPopulation( Population population, Network netwo residentOutsideResidentialArea.addPlan(plan2); residentOutsideResidentialArea.getAttributes().putAttribute("parkingType", "residential"); - population.addPerson( residentOutsideResidentialArea ); + population.addPerson(residentOutsideResidentialArea); } case nonResidentInResidentialAreaNoShop -> { Person nonResidentInResidentialAreaNoShop = factory.createPerson(Id.createPersonId("nonResidentInResidentialAreaNoShop")); @@ -224,7 +262,7 @@ static void createExampleParkingPopulation( Population population, Network netwo nonResidentInResidentialAreaNoShop.addPlan(plan3); nonResidentInResidentialAreaNoShop.getAttributes().putAttribute("parkingType", "non-residential"); - population.addPerson( nonResidentInResidentialAreaNoShop ); + population.addPerson(nonResidentInResidentialAreaNoShop); } case nonResidentInResidentialAreaShop -> { Person nonResidentInResidentialAreaShop = factory.createPerson(Id.createPersonId("nonResidentInResidentialAreaShop")); @@ -248,13 +286,34 @@ static void createExampleParkingPopulation( Population population, Network netwo nonResidentOutsideResidentialArea.addPlan(plan5); nonResidentOutsideResidentialArea.getAttributes().putAttribute("parkingType", "non-residential"); - population.addPerson( nonResidentOutsideResidentialArea ); + population.addPerson(nonResidentOutsideResidentialArea); } - default -> throw new IllegalStateException( "Unexpected value: " + situation ); + default -> throw new IllegalStateException("Unexpected value: " + situation); } //residential area is maximum including the following edges (square): 124-126, 34-36, 178-180, 88-90 } + + class TestParkingListener implements ActivityStartEventHandler { + + HashMap, List> parkingActivities = new HashMap<>(); + + + + @Override + public void handleEvent(ActivityStartEvent activityStartEvent) { + if (activityStartEvent.getActType().equals("parking interaction")) { + if (!parkingActivities.containsKey(activityStartEvent.getPersonId())) { + parkingActivities.put(activityStartEvent.getPersonId(), new ArrayList<>(Arrays.asList(activityStartEvent))); + } else parkingActivities.get(activityStartEvent).add(activityStartEvent); + } + } + + @Override + public void reset(int iteration) { + ActivityStartEventHandler.super.reset(iteration); + } + } } From 53c44e269c6f3efaf18d6b36ff0669210204b0c7 Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Tue, 2 May 2023 19:21:25 +0200 Subject: [PATCH 014/106] continued parking test, implemented normal to restricted --- .../org/matsim/run/ChessboardParkingTest.java | 186 +++++++++--------- .../run/LeipzigRouterPlanAlgorithm.java | 37 +++- 2 files changed, 127 insertions(+), 96 deletions(-) diff --git a/src/test/java/org/matsim/run/ChessboardParkingTest.java b/src/test/java/org/matsim/run/ChessboardParkingTest.java index eb6a5bbd..977cf900 100644 --- a/src/test/java/org/matsim/run/ChessboardParkingTest.java +++ b/src/test/java/org/matsim/run/ChessboardParkingTest.java @@ -23,6 +23,7 @@ import org.matsim.core.config.groups.*; import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; +import org.matsim.core.controler.OutputDirectoryHierarchy; import org.matsim.core.population.PopulationUtils; import org.matsim.core.population.algorithms.PlanAlgorithm; import org.matsim.core.replanning.PlanStrategy; @@ -42,10 +43,7 @@ import javax.inject.Provider; import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; +import java.util.*; import static org.matsim.core.config.groups.PlanCalcScoreConfigGroup.*; @@ -68,66 +66,68 @@ enum Situation { @Test public final void runChessboardParkingTest1() { + URL url = IOUtils.extendUrl(ExamplesUtils.getTestScenarioURL("chessboard"), "config.xml"); - Config config = ConfigUtils.loadConfig(url); - config.controler().setLastIteration(1); - config.controler().setOutputDirectory(utils.getOutputDirectory()); - config.global().setNumberOfThreads(0); - config.qsim().setNumberOfThreads(1); - - config.plansCalcRoute().setAccessEgressType(PlansCalcRouteConfigGroup.AccessEgressType.accessEgressModeToLink); - - config.strategy().clearStrategySettings(); - { - StrategyConfigGroup.StrategySettings stratSets = new StrategyConfigGroup.StrategySettings(); - stratSets.setWeight(1.); - stratSets.setStrategyName(RE_ROUTE_LEIPZIG); - config.strategy().addStrategySettings(stratSets); - } - config.facilities().setFacilitiesSource(FacilitiesConfigGroup.FacilitiesSource.onePerActivityLinkInPlansFile); + for (Situation situation : Situation.values()) { + Config config = ConfigUtils.loadConfig(url); + config.controler().setLastIteration(1); + config.controler().setOutputDirectory(utils.getOutputDirectory()); + config.global().setNumberOfThreads(0); + config.qsim().setNumberOfThreads(1); + config.controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + config.plansCalcRoute().setAccessEgressType(PlansCalcRouteConfigGroup.AccessEgressType.accessEgressModeToLink); + + config.strategy().clearStrategySettings(); + { + StrategyConfigGroup.StrategySettings stratSets = new StrategyConfigGroup.StrategySettings(); + stratSets.setWeight(1.); + stratSets.setStrategyName(RE_ROUTE_LEIPZIG); + config.strategy().addStrategySettings(stratSets); + } + + config.facilities().setFacilitiesSource(FacilitiesConfigGroup.FacilitiesSource.onePerActivityLinkInPlansFile); - config.planCalcScore().addActivityParams(new ActivityParams(TripStructureUtils.createStageActivityType("parking")).setScoringThisActivityAtAll(false)); + config.planCalcScore().addActivityParams(new ActivityParams(TripStructureUtils.createStageActivityType("parking")).setScoringThisActivityAtAll(false)); - config.vspExperimental().setVspDefaultsCheckingLevel(VspExperimentalConfigGroup.VspDefaultsCheckingLevel.warn); + config.vspExperimental().setVspDefaultsCheckingLevel(VspExperimentalConfigGroup.VspDefaultsCheckingLevel.warn); - MutableScenario scenario = (MutableScenario) ScenarioUtils.loadScenario(config); + MutableScenario scenario = (MutableScenario) ScenarioUtils.loadScenario(config); - Population population = PopulationUtils.createPopulation(config); - createExampleParkingPopulation(population, scenario.getNetwork(), Situation.restrictedToNormal); - scenario.setPopulation(population); - log.warn("population size=" + scenario.getPopulation().getPersons().size()); + Population population = PopulationUtils.createPopulation(config); + createExampleParkingPopulation(population, scenario.getNetwork(), situation); + scenario.setPopulation(population); + log.warn("population size=" + scenario.getPopulation().getPersons().size() +" for case" + situation); // System.exit(-1); // NetworkOptions networkOptions = new NetworkOptions(); // // help // yy what does the "help" mean here? // networkOptions.prepare(scenario.getNetwork()); - // yy I don't know what the above is supposed to do. Thus commented it out. kai, apr'23 + // yy I don't know what the above is supposed to do. Thus commented it out. kai, apr'23 - Controler controler = new Controler(scenario); - - controler.addOverridingModule(new AbstractModule() { - @Override - public void install() { + Controler controler = new Controler(scenario); + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { // this.bind( MultimodalLinkChooser.class ).toInstance( new LeipzigMultimodalLinkChooser() ); - this.addPlanStrategyBinding(RE_ROUTE_LEIPZIG).toProvider(LeipzigRoutingStrategyProvider.class); - // yyyy this only uses it during replanning!!! kai, apr'23 - } - }); + this.addPlanStrategyBinding(RE_ROUTE_LEIPZIG).toProvider(LeipzigRoutingStrategyProvider.class); + // yyyy this only uses it during replanning!!! kai, apr'23 + } + }); - TestParkingListener handler = new TestParkingListener(); - controler.addOverridingModule(new AbstractModule() { - @Override - public void install() { - addEventHandlerBinding().toInstance(handler); - } - }); - controler.run(); + TestParkingListener handler = new TestParkingListener(); + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { + addEventHandlerBinding().toInstance(handler); + } + }); + controler.run(); + + getAssertions(situation, handler); + } - Assert.assertTrue(handler.parkingActivities.containsKey(Id.createPersonId(String.valueOf(Situation.restrictedToNormal)))); - Assert.assertEquals("wrong number of parking activites!", 1, handler.parkingActivities.get(Id.createPersonId(String.valueOf(Situation.restrictedToNormal))).size()); - Assert.assertEquals("wrong link", Id.createLinkId("169"), handler.parkingActivities.get(Id.createPersonId(String.valueOf(Situation.restrictedToNormal))).get(0).getLinkId()); } @@ -189,23 +189,18 @@ public final PlanAlgorithm getPlanAlgoInstance() { static void createExampleParkingPopulation(Population population, Network network, Situation situation) { PopulationFactory factory = population.getFactory(); - Leg carLeg = factory.createLeg(TransportMode.car); - Link originLink = network.getLinks().get(Id.createLinkId("80")); Link destinationLink = network.getLinks().get(Id.createLinkId("81")); Person person = factory.createPerson(Id.createPersonId(situation.toString())); - Plan plan = factory.createPlan(); final Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, originLink.getId()); originActivity.setEndTime(3600.); plan.addActivity(originActivity); plan.addLeg(factory.createLeg(TransportMode.car)); plan.addActivity(factory.createActivityFromLinkId(ActivityTypes.LEISURE, destinationLink.getId())); - person.addPlan(plan); - population.addPerson(person); switch (situation) { @@ -226,66 +221,48 @@ static void createExampleParkingPopulation(Population population, Network networ // } } case residentInResidentialArea -> { - Person residentInResidentialArea = factory.createPerson(Id.createPersonId("residentInResidentialArea")); - - Plan plan1 = factory.createPlan(); - //maybe we also need to set start / end times for the activities.. - plan1.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("80"))); - plan1.addLeg(carLeg); - // (yy The way that was set up it would not have worked: You cannot re-use a leg that is also inserted in some other plan. kai, apr'23) - - plan1.addActivity(factory.createActivityFromLinkId(ActivityTypes.EDUCATION, Id.createLinkId("81"))); - residentInResidentialArea.addPlan(plan1); - residentInResidentialArea.getAttributes().putAttribute("parkingType", "residential"); - + population.getPersons().clear(); + Person residentInResidentialArea = factory.createPerson(Id.createPersonId(situation.toString())); + LeipzigUtils.setParkingToNonRestricted(residentInResidentialArea); + LeipzigUtils.parkingIsRestricted(originLink); + residentInResidentialArea.addPlan(plan); population.addPerson(residentInResidentialArea); } case residentOutsideResidentialArea -> { - Person residentOutsideResidentialArea = factory.createPerson(Id.createPersonId("residentOutsideResidentialArea")); - - Plan plan2 = factory.createPlan(); - plan2.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("35"))); - plan2.addLeg(carLeg); - plan2.addActivity(factory.createActivityFromLinkId(ActivityTypes.WORK, Id.createLinkId("31"))); - residentOutsideResidentialArea.addPlan(plan2); - residentOutsideResidentialArea.getAttributes().putAttribute("parkingType", "residential"); - + population.getPersons().clear(); + Person residentOutsideResidentialArea = factory.createPerson(Id.createPersonId(situation.toString())); + residentOutsideResidentialArea.addPlan(plan); + LeipzigUtils.setParkingToRestricted(residentOutsideResidentialArea); population.addPerson(residentOutsideResidentialArea); } case nonResidentInResidentialAreaNoShop -> { - Person nonResidentInResidentialAreaNoShop = factory.createPerson(Id.createPersonId("nonResidentInResidentialAreaNoShop")); - - Plan plan3 = factory.createPlan(); - plan3.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("41"))); - plan3.addLeg(carLeg); - plan3.addActivity(factory.createActivityFromLinkId(ActivityTypes.WORK, Id.createLinkId("45"))); - nonResidentInResidentialAreaNoShop.addPlan(plan3); - nonResidentInResidentialAreaNoShop.getAttributes().putAttribute("parkingType", "non-residential"); - + population.getPersons().clear(); + Person nonResidentInResidentialAreaNoShop = factory.createPerson(Id.createPersonId(situation.toString())); + nonResidentInResidentialAreaNoShop.addPlan(plan); + LeipzigUtils.setParkingToRestricted(nonResidentInResidentialAreaNoShop); + LeipzigUtils.setParkingToRestricted(destinationLink); population.addPerson(nonResidentInResidentialAreaNoShop); } case nonResidentInResidentialAreaShop -> { + population.getPersons().clear(); Person nonResidentInResidentialAreaShop = factory.createPerson(Id.createPersonId("nonResidentInResidentialAreaShop")); - Plan plan4 = factory.createPlan(); plan4.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("40"))); plan4.addLeg(carLeg); plan4.addActivity(factory.createActivityFromLinkId(ActivityTypes.SHOPPING, Id.createLinkId("135"))); nonResidentInResidentialAreaShop.addPlan(plan4); nonResidentInResidentialAreaShop.getAttributes().putAttribute("parkingType", "non-residential"); - population.addPerson(nonResidentInResidentialAreaShop); } case nonResidentOutsideResidentialArea -> { + population.getPersons().clear(); Person nonResidentOutsideResidentialArea = factory.createPerson(Id.createPersonId("nonResidentOutsideResidentialArea")); - Plan plan5 = factory.createPlan(); plan5.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("64"))); plan5.addLeg(carLeg); plan5.addActivity(factory.createActivityFromLinkId(ActivityTypes.LEISURE, Id.createLinkId("66"))); nonResidentOutsideResidentialArea.addPlan(plan5); nonResidentOutsideResidentialArea.getAttributes().putAttribute("parkingType", "non-residential"); - population.addPerson(nonResidentOutsideResidentialArea); } default -> throw new IllegalStateException("Unexpected value: " + situation); @@ -300,8 +277,6 @@ class TestParkingListener implements ActivityStartEventHandler { HashMap, List> parkingActivities = new HashMap<>(); - - @Override public void handleEvent(ActivityStartEvent activityStartEvent) { if (activityStartEvent.getActType().equals("parking interaction")) { @@ -316,4 +291,37 @@ public void reset(int iteration) { ActivityStartEventHandler.super.reset(iteration); } } + + private void getAssertions(Situation situation, TestParkingListener handler) { + + switch (situation) { + case normalToNormal -> { + + } + + case restrictedToNormal -> { + Assert.assertTrue(handler.parkingActivities.containsKey(Id.createPersonId(String.valueOf(Situation.restrictedToNormal)))); + Assert.assertEquals("wrong number of parking activites!", 1, handler.parkingActivities.get(Id.createPersonId(String.valueOf(Situation.restrictedToNormal))).size()); + Assert.assertEquals("wrong link", Id.createLinkId("169"), handler.parkingActivities.get(Id.createPersonId(String.valueOf(Situation.restrictedToNormal))).get(0).getLinkId()); + } + + case residentInResidentialArea -> { + Assert.assertTrue(!handler.parkingActivities.containsKey(Id.createPersonId(String.valueOf(Situation.residentInResidentialArea)))); + } + + case residentOutsideResidentialArea -> { + Assert.assertTrue(!handler.parkingActivities.containsKey(Id.createPersonId(String.valueOf(Situation.residentOutsideResidentialArea)))); + } + + case nonResidentInResidentialAreaNoShop -> { + Assert.assertTrue(handler.parkingActivities.containsKey(Id.createPersonId(String.valueOf(Situation.nonResidentInResidentialAreaNoShop)))); + Assert.assertEquals("wrong number of parking activites!", 1, handler.parkingActivities.get(Id.createPersonId(String.valueOf(Situation.nonResidentInResidentialAreaNoShop))).size()); + Assert.assertNotEquals("wrong link", Id.createLinkId("81"), handler.parkingActivities.get(Id.createPersonId(String.valueOf(Situation.nonResidentInResidentialAreaNoShop))).get(0).getLinkId()); + } + + + + } + + } } diff --git a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index a7d46fdf..b495e224 100644 --- a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -97,7 +97,6 @@ enum ParkingType{normal, restricted, shopping} timeTracker.addElements( newTripElements ); } else if( parkingTypeAtOrigin == ParkingType.restricted && parkingTypeAtDestination == ParkingType.normal ){ // restricted parking at origin: - // first find parking: // final Link parkingLink = NetworkUtils.getNearestLink( reducedNetwork, oldTrip.getOriginActivity().getCoord() ); final Link parkingLink = linkChooser.decideOnLink( fromFacility, reducedNetwork ); @@ -105,9 +104,7 @@ enum ParkingType{normal, restricted, shopping} final Activity parkingActivity = scenario.getPopulation().getFactory().createInteractionActivityFromLinkId( TripStructureUtils.createStageActivityType( "parking" ), parkingLink.getId() ); final Facility parkingFacility = FacilitiesUtils.toFacility( parkingActivity, facilities ); - List newTripElements = new ArrayList<>(); - // trip from origin to parking: final List walkTripElements = tripRouter.calcRoute( TransportMode.walk, fromFacility, parkingFacility, timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); @@ -117,21 +114,47 @@ enum ParkingType{normal, restricted, shopping} } } newTripElements.addAll( walkTripElements ); - // parking interaction: newTripElements.add( parkingActivity ); - // trip from parking to final destination: final List carTripElements = tripRouter.calcRoute( routingMode, parkingFacility, toFacility, timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); newTripElements.addAll( carTripElements ); - - putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity() ); timeTracker.addElements( newTripElements ); + } else if (parkingTypeAtOrigin == ParkingType.normal && parkingTypeAtDestination == ParkingType.restricted) { + + //parking at destination + final Link parkingLink = linkChooser.decideOnLink( toFacility, reducedNetwork ); + final Activity parkingActivity = scenario.getPopulation().getFactory().createInteractionActivityFromLinkId( + TripStructureUtils.createStageActivityType( "parking" ), parkingLink.getId() ); + final Facility parkingFacility = FacilitiesUtils.toFacility( parkingActivity, facilities ); + List newTripElements = new ArrayList<>(); + + // trip from origin to parking: + final List carTripElements = tripRouter.calcRoute( routingMode, fromFacility, parkingFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); + // parking interaction: + + newTripElements.addAll(carTripElements ); + newTripElements.add( parkingActivity ); + + // trip from parking to destination: + final List walkTripElements = tripRouter.calcRoute( TransportMode.walk, parkingFacility, toFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); + for( PlanElement tripElement : walkTripElements ){ + if ( tripElement instanceof Leg ) { + TripStructureUtils.setRoutingMode( (Leg) tripElement, TransportMode.car ); + } + } + newTripElements.addAll(walkTripElements); + putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); + TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity() ); + timeTracker.addElements(newTripElements); + } else{ throw new RuntimeException(); // to be implemented! From 2f5184a36d91b1a687dc91a7ad3b05d108f60121 Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Tue, 2 May 2023 20:43:51 +0200 Subject: [PATCH 015/106] continued parking test --- .../org/matsim/run/ChessboardParkingTest.java | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/test/java/org/matsim/run/ChessboardParkingTest.java b/src/test/java/org/matsim/run/ChessboardParkingTest.java index 977cf900..8be0a872 100644 --- a/src/test/java/org/matsim/run/ChessboardParkingTest.java +++ b/src/test/java/org/matsim/run/ChessboardParkingTest.java @@ -232,7 +232,6 @@ static void createExampleParkingPopulation(Population population, Network networ population.getPersons().clear(); Person residentOutsideResidentialArea = factory.createPerson(Id.createPersonId(situation.toString())); residentOutsideResidentialArea.addPlan(plan); - LeipzigUtils.setParkingToRestricted(residentOutsideResidentialArea); population.addPerson(residentOutsideResidentialArea); } case nonResidentInResidentialAreaNoShop -> { @@ -244,7 +243,7 @@ static void createExampleParkingPopulation(Population population, Network networ population.addPerson(nonResidentInResidentialAreaNoShop); } case nonResidentInResidentialAreaShop -> { - population.getPersons().clear(); + /*population.getPersons().clear(); Person nonResidentInResidentialAreaShop = factory.createPerson(Id.createPersonId("nonResidentInResidentialAreaShop")); Plan plan4 = factory.createPlan(); plan4.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("40"))); @@ -252,17 +251,13 @@ static void createExampleParkingPopulation(Population population, Network networ plan4.addActivity(factory.createActivityFromLinkId(ActivityTypes.SHOPPING, Id.createLinkId("135"))); nonResidentInResidentialAreaShop.addPlan(plan4); nonResidentInResidentialAreaShop.getAttributes().putAttribute("parkingType", "non-residential"); - population.addPerson(nonResidentInResidentialAreaShop); + population.addPerson(nonResidentInResidentialAreaShop);*/ } case nonResidentOutsideResidentialArea -> { population.getPersons().clear(); - Person nonResidentOutsideResidentialArea = factory.createPerson(Id.createPersonId("nonResidentOutsideResidentialArea")); - Plan plan5 = factory.createPlan(); - plan5.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("64"))); - plan5.addLeg(carLeg); - plan5.addActivity(factory.createActivityFromLinkId(ActivityTypes.LEISURE, Id.createLinkId("66"))); - nonResidentOutsideResidentialArea.addPlan(plan5); - nonResidentOutsideResidentialArea.getAttributes().putAttribute("parkingType", "non-residential"); + Person nonResidentOutsideResidentialArea = factory.createPerson(Id.createPersonId(situation.toString())); + nonResidentOutsideResidentialArea.addPlan(plan); + LeipzigUtils.setParkingToRestricted(nonResidentOutsideResidentialArea); population.addPerson(nonResidentOutsideResidentialArea); } default -> throw new IllegalStateException("Unexpected value: " + situation); From c8d006f478443a407aa3528aabb0f9ab750e31cf Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Sun, 7 May 2023 22:00:19 +0200 Subject: [PATCH 016/106] continued parking test, started with shopping still WIP --- .../org/matsim/run/prepare/LeipzigUtils.java | 14 +++++ .../org/matsim/run/ChessboardParkingTest.java | 32 +++++++---- .../run/LeipzigRouterPlanAlgorithm.java | 55 ++++++++++++++++++- 3 files changed, 88 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/matsim/run/prepare/LeipzigUtils.java b/src/main/java/org/matsim/run/prepare/LeipzigUtils.java index 9b6137f0..b1919301 100644 --- a/src/main/java/org/matsim/run/prepare/LeipzigUtils.java +++ b/src/main/java/org/matsim/run/prepare/LeipzigUtils.java @@ -33,4 +33,18 @@ public static void setParkingToNonRestricted(Person person) { public static void setLinkParkingCostAttributes(Link link, String attributeName, double attributeValue) { link.getAttributes().putAttribute(attributeName, attributeValue); } + + //TODO i don´t like the name for this + public static void setLinkToParkingForShopping (Link link) { + link.getAttributes().putAttribute("parkingForShopping", "parkingLot"); + } + + public static boolean parkingAllowedForShopping(Link link) { + String result = (String) link.getAttributes().getAttribute( "parkingForShopping" ); + if (result == null) { + return false ; + } else { + return true; + } + } } diff --git a/src/test/java/org/matsim/run/ChessboardParkingTest.java b/src/test/java/org/matsim/run/ChessboardParkingTest.java index 8be0a872..dc25ce68 100644 --- a/src/test/java/org/matsim/run/ChessboardParkingTest.java +++ b/src/test/java/org/matsim/run/ChessboardParkingTest.java @@ -192,6 +192,7 @@ static void createExampleParkingPopulation(Population population, Network networ Leg carLeg = factory.createLeg(TransportMode.car); Link originLink = network.getLinks().get(Id.createLinkId("80")); Link destinationLink = network.getLinks().get(Id.createLinkId("81")); + Link linkForShopping = network.getLinks().get(Id.createLinkId("86")); Person person = factory.createPerson(Id.createPersonId(situation.toString())); Plan plan = factory.createPlan(); @@ -242,17 +243,6 @@ static void createExampleParkingPopulation(Population population, Network networ LeipzigUtils.setParkingToRestricted(destinationLink); population.addPerson(nonResidentInResidentialAreaNoShop); } - case nonResidentInResidentialAreaShop -> { - /*population.getPersons().clear(); - Person nonResidentInResidentialAreaShop = factory.createPerson(Id.createPersonId("nonResidentInResidentialAreaShop")); - Plan plan4 = factory.createPlan(); - plan4.addActivity(factory.createActivityFromLinkId(ActivityTypes.HOME, Id.createLinkId("40"))); - plan4.addLeg(carLeg); - plan4.addActivity(factory.createActivityFromLinkId(ActivityTypes.SHOPPING, Id.createLinkId("135"))); - nonResidentInResidentialAreaShop.addPlan(plan4); - nonResidentInResidentialAreaShop.getAttributes().putAttribute("parkingType", "non-residential"); - population.addPerson(nonResidentInResidentialAreaShop);*/ - } case nonResidentOutsideResidentialArea -> { population.getPersons().clear(); Person nonResidentOutsideResidentialArea = factory.createPerson(Id.createPersonId(situation.toString())); @@ -260,6 +250,21 @@ static void createExampleParkingPopulation(Population population, Network networ LeipzigUtils.setParkingToRestricted(nonResidentOutsideResidentialArea); population.addPerson(nonResidentOutsideResidentialArea); } + case nonResidentInResidentialAreaShop -> { + population.getPersons().clear(); + Person nonResidentInResidentialAreaShop = factory.createPerson(Id.createPersonId(situation.toString())); + originActivity.setEndTime(3600.); + Plan planForShopping = factory.createPlan(); + planForShopping.addActivity(originActivity); + planForShopping.addLeg(factory.createLeg(TransportMode.car)); + planForShopping.addActivity(factory.createActivityFromLinkId(ActivityTypes.SHOPPING, destinationLink.getId())); + population.addPerson(nonResidentInResidentialAreaShop); + nonResidentInResidentialAreaShop.addPlan(planForShopping); + LeipzigUtils.setParkingToRestricted(nonResidentInResidentialAreaShop); + LeipzigUtils.setParkingToRestricted(destinationLink); + LeipzigUtils.setLinkToParkingForShopping(linkForShopping); + } + default -> throw new IllegalStateException("Unexpected value: " + situation); } @@ -314,6 +319,11 @@ private void getAssertions(Situation situation, TestParkingListener handler) { Assert.assertNotEquals("wrong link", Id.createLinkId("81"), handler.parkingActivities.get(Id.createPersonId(String.valueOf(Situation.nonResidentInResidentialAreaNoShop))).get(0).getLinkId()); } + case nonResidentInResidentialAreaShop -> { + Assert.assertTrue(handler.parkingActivities.containsKey(Id.createPersonId(String.valueOf(Situation.nonResidentInResidentialAreaShop)))); + Assert.assertEquals("wrong link", Id.createLinkId("86"),handler.parkingActivities.get(Id.createPersonId(String.valueOf(Situation.nonResidentInResidentialAreaShop))).get(0).getLinkId()); + } + } diff --git a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index b495e224..10b3fe17 100644 --- a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -29,6 +29,7 @@ import java.util.List; import static org.matsim.core.router.PlanRouter.putVehicleFromOldTripIntoNewTripIfMeaningful; +import static org.matsim.run.prepare.LeipzigUtils.parkingAllowedForShopping; import static org.matsim.run.prepare.LeipzigUtils.parkingIsRestricted; final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ @@ -40,6 +41,8 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ private final Network reducedNetwork; private final MultimodalLinkChooser linkChooser; private final Scenario scenario; + private final Network networkForShopping; + LeipzigRouterPlanAlgorithm( final TripRouter tripRouter, final ActivityFacilities facilities, final TimeInterpretation timeInterpretation, SingleModeNetworksCache singleModeNetworksCache, Scenario scenario, MultimodalLinkChooser linkChooser ){ this.tripRouter = tripRouter; @@ -51,6 +54,7 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ // yyyy one should look at the networks cache and see how the following is done. And maybe even register it there. this.reducedNetwork = NetworkUtils.createNetwork( scenario.getConfig().network() ); this.linkChooser = linkChooser; + this.networkForShopping = NetworkUtils.createNetwork(scenario.getConfig().network()); for( Node node : this.fullModalNetwork.getNodes().values() ){ reducedNetwork.addNode( node ); } @@ -89,7 +93,6 @@ enum ParkingType{normal, restricted, shopping} if( parkingTypeAtOrigin == ParkingType.normal && parkingTypeAtDestination == ParkingType.normal ){ // standard case: - final List newTripElements = tripRouter.calcRoute( routingMode, fromFacility, toFacility, timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); @@ -155,7 +158,52 @@ enum ParkingType{normal, restricted, shopping} TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity() ); timeTracker.addElements(newTripElements); - } else{ + } else if (parkingTypeAtOrigin == ParkingType.normal && parkingTypeAtDestination == ParkingType.shopping) { + + // can this be done better??? i need only the links with shopping garages + for( Node node : this.fullModalNetwork.getNodes().values() ){ + networkForShopping.addNode( node ); + } + + for (Link l: this.fullModalNetwork.getLinks().values()) { + System.out.println(l.getAttributes().toString()); + if (parkingAllowedForShopping(l) == true) { + networkForShopping.addLink(l); + } + } + + //why is this returning the wrong link + final Link parkingLink = linkChooser.decideOnLink( toFacility, networkForShopping ); + + final Activity parkingActivity = scenario.getPopulation().getFactory().createInteractionActivityFromLinkId( + TripStructureUtils.createStageActivityType( "parking" ), parkingLink.getId() ); + final Facility parkingFacility = FacilitiesUtils.toFacility( parkingActivity, facilities ); + List newTripElements = new ArrayList<>(); + + // trip from origin to parking: + final List carTripElements = tripRouter.calcRoute( routingMode, fromFacility, parkingFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); + // parking interaction: + + newTripElements.addAll(carTripElements ); + newTripElements.add( parkingActivity ); + + // trip from parking to destination: + final List walkTripElements = tripRouter.calcRoute( TransportMode.walk, parkingFacility, toFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); + for( PlanElement tripElement : walkTripElements ){ + if ( tripElement instanceof Leg ) { + TripStructureUtils.setRoutingMode( (Leg) tripElement, TransportMode.car ); + } + } + newTripElements.addAll(walkTripElements); + putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); + TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity() ); + timeTracker.addElements(newTripElements); + + + } + else{ throw new RuntimeException(); // to be implemented! } @@ -178,6 +226,9 @@ private static ParkingType getParkingType( Network fullModalNetwork, Activity or Link link = fullModalNetwork.getLinks().get( originActivity.getLinkId() ); if( parkingIsRestricted( link ) ){ parkingTypeAtOrigin = ParkingType.restricted; + if (originActivity.getType().equals(ActivityTypes.SHOPPING)) { + parkingTypeAtOrigin = ParkingType.shopping; + } } } From e4360b7583543ce3890815a953f70f54490a01cd Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Mon, 8 May 2023 16:34:47 +0200 Subject: [PATCH 017/106] some comments --- src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index 10b3fe17..033204d9 100644 --- a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -219,6 +219,9 @@ enum ParkingType{normal, restricted, shopping} private static ParkingType getParkingType( Network fullModalNetwork, Activity originActivity ){ ParkingType parkingTypeAtOrigin = ParkingType.normal; + // if we find out that there are time restrictions on all the links + //originActivity.getEndTime(); + // check if non-home activity (since otherwise we assume that there is no parking restriction): if( !originActivity.getType().equals( ActivityTypes.HOME ) ){ @@ -227,6 +230,8 @@ private static ParkingType getParkingType( Network fullModalNetwork, Activity or if( parkingIsRestricted( link ) ){ parkingTypeAtOrigin = ParkingType.restricted; if (originActivity.getType().equals(ActivityTypes.SHOPPING)) { + // change this to parking type normal/ unrestricted + // on activity link, unrestricted parkingTypeAtOrigin = ParkingType.shopping; } } From 5ad0ab54121fc0a13cdd63f159d938dfa8baf492 Mon Sep 17 00:00:00 2001 From: simei94 Date: Mon, 8 May 2023 18:26:59 +0200 Subject: [PATCH 018/106] some refactoring --- .../run/LinkAttributeNetworkLinkFilter.java | 3 +++ .../run/SubtourModeChoiceInclParking.java | 3 +++ .../run/prepare/AssignParkingAttributes.java | 2 +- .../org/matsim/run/prepare/LeipzigUtils.java | 25 +++++++++++++------ .../prepare/ParkingCapacitiesAttacher.java | 4 +-- .../matsim/run/prepare/PrepareNetwork.java | 6 ++--- 6 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/matsim/run/LinkAttributeNetworkLinkFilter.java b/src/main/java/org/matsim/run/LinkAttributeNetworkLinkFilter.java index 6ec13354..12344a21 100644 --- a/src/main/java/org/matsim/run/LinkAttributeNetworkLinkFilter.java +++ b/src/main/java/org/matsim/run/LinkAttributeNetworkLinkFilter.java @@ -3,6 +3,9 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.core.network.filter.NetworkLinkFilter; +/** + * Filters network for links with a certain attribute. + */ public class LinkAttributeNetworkLinkFilter implements NetworkLinkFilter { private final String attributeName; diff --git a/src/main/java/org/matsim/run/SubtourModeChoiceInclParking.java b/src/main/java/org/matsim/run/SubtourModeChoiceInclParking.java index 4fbd69ba..f4f91382 100644 --- a/src/main/java/org/matsim/run/SubtourModeChoiceInclParking.java +++ b/src/main/java/org/matsim/run/SubtourModeChoiceInclParking.java @@ -33,6 +33,9 @@ import org.matsim.core.utils.timing.TimeInterpretation; import org.matsim.facilities.ActivityFacilities; +/** + * Scenario-related implementation of SMC which includes a first, rather complicated parking logic. + */ public class SubtourModeChoiceInclParking implements Provider { @Inject private Provider tripRouterProvider; diff --git a/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java b/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java index 38a82b28..ec935add 100644 --- a/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java +++ b/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java @@ -11,7 +11,7 @@ final class AssignParkingAttributes { - AssignParkingAttributes() {} + private AssignParkingAttributes() {} static void addParkingAttributesToPopulation(Population population, ShpOptions shp) { diff --git a/src/main/java/org/matsim/run/prepare/LeipzigUtils.java b/src/main/java/org/matsim/run/prepare/LeipzigUtils.java index b1919301..7d1fed87 100644 --- a/src/main/java/org/matsim/run/prepare/LeipzigUtils.java +++ b/src/main/java/org/matsim/run/prepare/LeipzigUtils.java @@ -2,11 +2,17 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.Person; -import playground.vsp.simpleParkingCostHandler.ParkingCostConfigGroup; -public class LeipzigUtils{ - private LeipzigUtils(){} // do not instantiate +/** + * Utils class to adapt scenario-related person / link attributes. + */ +public final class LeipzigUtils{ + // do not instantiate + private LeipzigUtils(){} + /** + * Check of parking on link is restricted or not. + */ public static boolean parkingIsRestricted( Link link ) { String result = (String) link.getAttributes().getAttribute( "parking" ); if ( result == null ) { @@ -15,6 +21,7 @@ public static boolean parkingIsRestricted( Link link ) { return true; } } + public static void setParkingToRestricted( Link link ){ link.getAttributes().putAttribute( "parking", "restricted" ); } @@ -28,17 +35,19 @@ public static void setParkingToNonRestricted(Person person) { person.getAttributes().putAttribute("parkingType", "nonResidentialParking"); } - //TODO put this into PrepareNetwork.prepareParkingCost after merge, lines 195-197 - //TODO also add in ParkingCapacityAttacher lines 74-79 - public static void setLinkParkingCostAttributes(Link link, String attributeName, double attributeValue) { + public static void setLinkAttribute(Link link, String attributeName, double attributeValue) { link.getAttributes().putAttribute(attributeName, attributeValue); } //TODO i don´t like the name for this - public static void setLinkToParkingForShopping (Link link) { - link.getAttributes().putAttribute("parkingForShopping", "parkingLot"); + //Bbetter? + public static void setParkingToShoppingCenter(Link link) { + link.getAttributes().putAttribute("parkingForShopping", "shoppingCenter"); } + /** + * check if parking for activity type shopping is allowed on a given link. + */ public static boolean parkingAllowedForShopping(Link link) { String result = (String) link.getAttributes().getAttribute( "parkingForShopping" ); if (result == null) { diff --git a/src/main/java/org/matsim/run/prepare/ParkingCapacitiesAttacher.java b/src/main/java/org/matsim/run/prepare/ParkingCapacitiesAttacher.java index e5be3e5c..e6907437 100644 --- a/src/main/java/org/matsim/run/prepare/ParkingCapacitiesAttacher.java +++ b/src/main/java/org/matsim/run/prepare/ParkingCapacitiesAttacher.java @@ -11,7 +11,6 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.application.options.ShpOptions; import org.matsim.core.utils.geometry.geotools.MGC; -import org.matsim.utils.objectattributes.attributable.Attributes; import java.io.BufferedReader; import java.io.FileReader; @@ -74,8 +73,7 @@ public void addParkingInformationToLinks() { if (isInsideParkingArea && linkParkingCapacities.get(link.getId().toString()) != null) { int parkingCapacity = Integer.parseInt(linkParkingCapacities.get(link.getId().toString())); - Attributes linkAttributes = link.getAttributes(); - linkAttributes.putAttribute(capacityAttributeName, parkingCapacity); + LeipzigUtils.setLinkAttribute(link, capacityAttributeName, parkingCapacity); adaptedLinksCount++; } diff --git a/src/main/java/org/matsim/run/prepare/PrepareNetwork.java b/src/main/java/org/matsim/run/prepare/PrepareNetwork.java index 522fcead..289baf52 100644 --- a/src/main/java/org/matsim/run/prepare/PrepareNetwork.java +++ b/src/main/java/org/matsim/run/prepare/PrepareNetwork.java @@ -192,9 +192,9 @@ static void prepareParkingCost(Network network, ShpOptions parkingCostShape) { } } - link.getAttributes().putAttribute(parkingCostConfigGroup.getFirstHourParkingCostLinkAttributeName(), oneHourPCost); - link.getAttributes().putAttribute(parkingCostConfigGroup.getExtraHourParkingCostLinkAttributeName(), extraHourPCost); - link.getAttributes().putAttribute(parkingCostConfigGroup.getResidentialParkingFeeAttributeName(), resPFee); + LeipzigUtils.setLinkAttribute(link, parkingCostConfigGroup.getFirstHourParkingCostLinkAttributeName(), oneHourPCost); + LeipzigUtils.setLinkAttribute(link, parkingCostConfigGroup.getExtraHourParkingCostLinkAttributeName(), extraHourPCost); + LeipzigUtils.setLinkAttribute(link, parkingCostConfigGroup.getResidentialParkingFeeAttributeName(), resPFee); } } From b7fa5f2e967a2d84c9f8ba428b104af9ea2921c2 Mon Sep 17 00:00:00 2001 From: simei94 Date: Mon, 8 May 2023 18:27:38 +0200 Subject: [PATCH 019/106] some refactoring --- src/test/java/org/matsim/run/ChessboardParkingTest.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/test/java/org/matsim/run/ChessboardParkingTest.java b/src/test/java/org/matsim/run/ChessboardParkingTest.java index dc25ce68..dd491be5 100644 --- a/src/test/java/org/matsim/run/ChessboardParkingTest.java +++ b/src/test/java/org/matsim/run/ChessboardParkingTest.java @@ -10,10 +10,7 @@ import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.TransportMode; -import org.matsim.api.core.v01.events.ActivityEndEvent; import org.matsim.api.core.v01.events.ActivityStartEvent; -import org.matsim.api.core.v01.events.Event; -import org.matsim.api.core.v01.events.handler.ActivityEndEventHandler; import org.matsim.api.core.v01.events.handler.ActivityStartEventHandler; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; @@ -262,7 +259,7 @@ static void createExampleParkingPopulation(Population population, Network networ nonResidentInResidentialAreaShop.addPlan(planForShopping); LeipzigUtils.setParkingToRestricted(nonResidentInResidentialAreaShop); LeipzigUtils.setParkingToRestricted(destinationLink); - LeipzigUtils.setLinkToParkingForShopping(linkForShopping); + LeipzigUtils.setParkingToShoppingCenter(linkForShopping); } default -> throw new IllegalStateException("Unexpected value: " + situation); From 17dab7f1a00ec2dcde7f69981f195ae443cf4ec5 Mon Sep 17 00:00:00 2001 From: simei94 Date: Mon, 8 May 2023 19:11:17 +0200 Subject: [PATCH 020/106] set parking link attr names to fixed from LeipzigUtils --- .../run/TimeRestrictedParkingCostHandler.java | 13 +++++++------ .../java/org/matsim/run/prepare/LeipzigUtils.java | 5 +++++ .../java/org/matsim/run/prepare/PrepareNetwork.java | 10 +++------- .../run/TimeRestrictedParkingCostHandlerTest.java | 13 +++++++------ .../org/matsim/run/prepare/NetworkOptionsTest.java | 12 ++++++------ 5 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/matsim/run/TimeRestrictedParkingCostHandler.java b/src/main/java/org/matsim/run/TimeRestrictedParkingCostHandler.java index 2ca3b672..60209348 100644 --- a/src/main/java/org/matsim/run/TimeRestrictedParkingCostHandler.java +++ b/src/main/java/org/matsim/run/TimeRestrictedParkingCostHandler.java @@ -44,6 +44,7 @@ import org.matsim.core.router.StageActivityTypeIdentifier; import com.google.inject.Inject; +import org.matsim.run.prepare.LeipzigUtils; import playground.vsp.simpleParkingCostHandler.ParkingCostConfigGroup; /** @@ -179,8 +180,8 @@ public void handleEvent(PersonEntersVehicleEvent event) { hasAlreadyPaidDailyResidentialParkingCosts.add(event.getPersonId()); double residentialParkingFeePerDay = 0.; - if (link.getAttributes().getAttribute(parkingCostConfigGroup.getResidentialParkingFeeAttributeName()) != null) { - residentialParkingFeePerDay = (double) link.getAttributes().getAttribute(parkingCostConfigGroup.getResidentialParkingFeeAttributeName()); + if (link.getAttributes().getAttribute(LeipzigUtils.RESIDENTIAL_PARKING_FEE_PER_DAY) != null) { + residentialParkingFeePerDay = (double) link.getAttributes().getAttribute(LeipzigUtils.RESIDENTIAL_PARKING_FEE_PER_DAY); } if (residentialParkingFeePerDay > 0.) { @@ -198,13 +199,13 @@ public void handleEvent(PersonEntersVehicleEvent event) { int parkingDurationHrs = (int) Math.ceil((event.getTime() - parkingStartTime) / 3600.); double extraHourParkingCosts = 0.; - if (link.getAttributes().getAttribute(parkingCostConfigGroup.getExtraHourParkingCostLinkAttributeName()) != null) { - extraHourParkingCosts = (double) link.getAttributes().getAttribute(parkingCostConfigGroup.getExtraHourParkingCostLinkAttributeName()); + if (link.getAttributes().getAttribute(LeipzigUtils.EXTRA_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME) != null) { + extraHourParkingCosts = (double) link.getAttributes().getAttribute(LeipzigUtils.EXTRA_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME); } double firstHourParkingCosts = 0.; - if (link.getAttributes().getAttribute(parkingCostConfigGroup.getFirstHourParkingCostLinkAttributeName()) != null) { - firstHourParkingCosts = (double) link.getAttributes().getAttribute(parkingCostConfigGroup.getFirstHourParkingCostLinkAttributeName()); + if (link.getAttributes().getAttribute(LeipzigUtils.FIRST_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME) != null) { + firstHourParkingCosts = (double) link.getAttributes().getAttribute(LeipzigUtils.FIRST_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME); } double dailyParkingCosts = firstHourParkingCosts + 29 * extraHourParkingCosts; diff --git a/src/main/java/org/matsim/run/prepare/LeipzigUtils.java b/src/main/java/org/matsim/run/prepare/LeipzigUtils.java index 7d1fed87..ff78be73 100644 --- a/src/main/java/org/matsim/run/prepare/LeipzigUtils.java +++ b/src/main/java/org/matsim/run/prepare/LeipzigUtils.java @@ -7,6 +7,11 @@ * Utils class to adapt scenario-related person / link attributes. */ public final class LeipzigUtils{ + + public static final String FIRST_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME = "firstHourParkingCostLinkAttributeName"; + public static final String EXTRA_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME = "extraHourParkingCostLinkAttributeName"; + public static final String RESIDENTIAL_PARKING_FEE_PER_DAY = "residentialParkingFeePerDay"; + // do not instantiate private LeipzigUtils(){} diff --git a/src/main/java/org/matsim/run/prepare/PrepareNetwork.java b/src/main/java/org/matsim/run/prepare/PrepareNetwork.java index 289baf52..dce90ee7 100644 --- a/src/main/java/org/matsim/run/prepare/PrepareNetwork.java +++ b/src/main/java/org/matsim/run/prepare/PrepareNetwork.java @@ -10,8 +10,6 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.application.MATSimAppCommand; import org.matsim.application.options.ShpOptions; -import org.matsim.core.config.Config; -import org.matsim.core.config.ConfigUtils; import org.matsim.core.network.NetworkUtils; import org.matsim.core.network.algorithms.MultimodalNetworkCleaner; import org.matsim.core.utils.geometry.geotools.MGC; @@ -19,7 +17,6 @@ import org.matsim.utils.gis.shp2matsim.ShpGeometryUtils; import org.opengis.feature.simple.SimpleFeature; import picocli.CommandLine; -import playground.vsp.simpleParkingCostHandler.ParkingCostConfigGroup; import java.nio.file.Path; import java.util.*; @@ -159,7 +156,6 @@ static void prepareCarFree(Network network, ShpOptions shp, String modes) { * Add parking cost to network links. Therefore, a shape file of the parking area is needed */ static void prepareParkingCost(Network network, ShpOptions parkingCostShape) { - ParkingCostConfigGroup parkingCostConfigGroup = ConfigUtils.addOrGetModule(new Config(), ParkingCostConfigGroup.class); Collection features = ShapeFileReader.getAllFeatures(String.valueOf(parkingCostShape.getShapeFile())); String hourlyParkingCostAttrName = "cost_h"; @@ -192,9 +188,9 @@ static void prepareParkingCost(Network network, ShpOptions parkingCostShape) { } } - LeipzigUtils.setLinkAttribute(link, parkingCostConfigGroup.getFirstHourParkingCostLinkAttributeName(), oneHourPCost); - LeipzigUtils.setLinkAttribute(link, parkingCostConfigGroup.getExtraHourParkingCostLinkAttributeName(), extraHourPCost); - LeipzigUtils.setLinkAttribute(link, parkingCostConfigGroup.getResidentialParkingFeeAttributeName(), resPFee); + LeipzigUtils.setLinkAttribute(link, LeipzigUtils.FIRST_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME, oneHourPCost); + LeipzigUtils.setLinkAttribute(link, LeipzigUtils.EXTRA_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME, extraHourPCost); + LeipzigUtils.setLinkAttribute(link, LeipzigUtils.RESIDENTIAL_PARKING_FEE_PER_DAY, resPFee); } } diff --git a/src/test/java/org/matsim/run/TimeRestrictedParkingCostHandlerTest.java b/src/test/java/org/matsim/run/TimeRestrictedParkingCostHandlerTest.java index 855d43b6..9b6969cc 100644 --- a/src/test/java/org/matsim/run/TimeRestrictedParkingCostHandlerTest.java +++ b/src/test/java/org/matsim/run/TimeRestrictedParkingCostHandlerTest.java @@ -23,6 +23,7 @@ import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.utils.io.IOUtils; import org.matsim.examples.ExamplesUtils; +import org.matsim.run.prepare.LeipzigUtils; import org.matsim.testcases.MatsimTestUtils; import playground.vsp.openberlinscenario.cemdap.output.ActivityTypes; import playground.vsp.simpleParkingCostHandler.ParkingCostConfigGroup; @@ -48,14 +49,14 @@ void createExamplePopulation(Population population, Scenario scenario, Situation Plan plan = factory.createPlan(); Link startLink = scenario.getNetwork().getLinks().get(Id.createLinkId("76")); - startLink.getAttributes().putAttribute(parkingCostConfigGroup.getFirstHourParkingCostLinkAttributeName(), 1.); - startLink.getAttributes().putAttribute(parkingCostConfigGroup.getExtraHourParkingCostLinkAttributeName(), 1.); - startLink.getAttributes().putAttribute(parkingCostConfigGroup.getResidentialParkingFeeAttributeName(), 1.); + startLink.getAttributes().putAttribute(LeipzigUtils.FIRST_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME, 1.); + startLink.getAttributes().putAttribute(LeipzigUtils.EXTRA_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME, 1.); + startLink.getAttributes().putAttribute(LeipzigUtils.RESIDENTIAL_PARKING_FEE_PER_DAY, 1.); Link destinationLink = scenario.getNetwork().getLinks().get(Id.createLinkId("78")); - destinationLink.getAttributes().putAttribute(parkingCostConfigGroup.getFirstHourParkingCostLinkAttributeName(), 1.); - destinationLink.getAttributes().putAttribute(parkingCostConfigGroup.getExtraHourParkingCostLinkAttributeName(), 1.); - destinationLink.getAttributes().putAttribute(parkingCostConfigGroup.getResidentialParkingFeeAttributeName(), 1.); + destinationLink.getAttributes().putAttribute(LeipzigUtils.FIRST_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME, 1.); + destinationLink.getAttributes().putAttribute(LeipzigUtils.EXTRA_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME, 1.); + destinationLink.getAttributes().putAttribute(LeipzigUtils.RESIDENTIAL_PARKING_FEE_PER_DAY, 1.); Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.WORK, startLink.getId()); Activity destinationActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, destinationLink.getId()); diff --git a/src/test/java/org/matsim/run/prepare/NetworkOptionsTest.java b/src/test/java/org/matsim/run/prepare/NetworkOptionsTest.java index fe88c286..422acd87 100644 --- a/src/test/java/org/matsim/run/prepare/NetworkOptionsTest.java +++ b/src/test/java/org/matsim/run/prepare/NetworkOptionsTest.java @@ -119,17 +119,17 @@ public void runParkingCostAreaCreationTest() { //parkingCost values (2.0 and 0.1) are defined in file shpPath Assert.assertEquals(Double.parseDouble(outputNetwork.getLinks().get(Id.createLinkId("-10424519")) - .getAttributes().getAttribute(parkingCostConfigGroup.getFirstHourParkingCostLinkAttributeName()).toString()),2.0, 0); + .getAttributes().getAttribute(LeipzigUtils.FIRST_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME).toString()),2.0, 0); Assert.assertEquals(Double.parseDouble(outputNetwork.getLinks().get(Id.createLinkId("-10424519")) - .getAttributes().getAttribute(parkingCostConfigGroup.getExtraHourParkingCostLinkAttributeName()).toString()),2.0, 0); + .getAttributes().getAttribute(LeipzigUtils.EXTRA_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME).toString()),2.0, 0); Assert.assertEquals(Double.parseDouble(outputNetwork.getLinks().get(Id.createLinkId("-10424519")) - .getAttributes().getAttribute(parkingCostConfigGroup.getResidentialParkingFeeAttributeName()).toString()),0.1, 0); + .getAttributes().getAttribute(LeipzigUtils.RESIDENTIAL_PARKING_FEE_PER_DAY).toString()),0.1, 0); Assert.assertEquals(Double.parseDouble(outputNetwork.getLinks().get(Id.createLinkId("827435967#0")) - .getAttributes().getAttribute(parkingCostConfigGroup.getFirstHourParkingCostLinkAttributeName()).toString()),2.0, 0); + .getAttributes().getAttribute(LeipzigUtils.FIRST_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME).toString()),2.0, 0); Assert.assertEquals(Double.parseDouble(outputNetwork.getLinks().get(Id.createLinkId("827435967#0")) - .getAttributes().getAttribute(parkingCostConfigGroup.getExtraHourParkingCostLinkAttributeName()).toString()),2.0, 0); + .getAttributes().getAttribute(LeipzigUtils.EXTRA_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME).toString()),2.0, 0); Assert.assertEquals(Double.parseDouble(outputNetwork.getLinks().get(Id.createLinkId("827435967#0")) - .getAttributes().getAttribute(parkingCostConfigGroup.getResidentialParkingFeeAttributeName()).toString()),0.1, 0); + .getAttributes().getAttribute(LeipzigUtils.RESIDENTIAL_PARKING_FEE_PER_DAY).toString()),0.1, 0); } From 0ec21c4ca143e5803d3e7f7a86cedeb6d4d36d1e Mon Sep 17 00:00:00 2001 From: simei94 Date: Tue, 9 May 2023 16:12:18 +0200 Subject: [PATCH 021/106] complete switch from parkingCostConfig to LeipzigUtils --- .../run/TimeRestrictedParkingCostHandler.java | 38 +++++++-------- .../org/matsim/run/prepare/LeipzigUtils.java | 47 +++++++++++++++++-- .../matsim/run/prepare/PrepareNetwork.java | 6 +-- .../TimeRestrictedParkingCostHandlerTest.java | 12 ++--- 4 files changed, 70 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/matsim/run/TimeRestrictedParkingCostHandler.java b/src/main/java/org/matsim/run/TimeRestrictedParkingCostHandler.java index 60209348..2a78fd29 100644 --- a/src/main/java/org/matsim/run/TimeRestrictedParkingCostHandler.java +++ b/src/main/java/org/matsim/run/TimeRestrictedParkingCostHandler.java @@ -45,7 +45,6 @@ import com.google.inject.Inject; import org.matsim.run.prepare.LeipzigUtils; -import playground.vsp.simpleParkingCostHandler.ParkingCostConfigGroup; /** * Implementation of ParkingCostHandler with an additional check for time restriction when including parking cost into a simulation. @@ -63,9 +62,6 @@ final class TimeRestrictedParkingCostHandler implements TransitDriverStartsEvent private double parkingCostTimePeriodStart; private double parkingCostTimePeriodEnd; - @Inject - private ParkingCostConfigGroup parkingCostConfigGroup; - @Inject private EventsManager events; @@ -150,7 +146,7 @@ public void handleEvent(PersonDepartureEvent event) { isInRestrictedParkingPeriod = checkTimeRestriction(event.getTime()); if (isInRestrictedParkingPeriod) { - if (! ptDrivers.contains(event.getPersonId()) && event.getLegMode().equals(parkingCostConfigGroup.getMode())) { + if (! ptDrivers.contains(event.getPersonId()) && event.getLegMode().equals(LeipzigUtils.getMode())) { // There might be several departures during a single trip. personId2relevantModeLinkId.put(event.getPersonId(), event.getLinkId()); } @@ -170,18 +166,18 @@ public void handleEvent(PersonEntersVehicleEvent event) { Link link = scenario.getNetwork().getLinks().get(personId2relevantModeLinkId.get(event.getPersonId())); - if (parkingCostConfigGroup.getActivityPrefixesToBeExcludedFromParkingCost().stream() + if (LeipzigUtils.getActivityPrefixesToBeExcludedFromParkingCost().stream() .noneMatch(s -> personId2previousActivity.get(event.getPersonId()).startsWith(s))) { - if (personId2previousActivity.get(event.getPersonId()).startsWith(parkingCostConfigGroup.getActivityPrefixForDailyParkingCosts()) + if (personId2previousActivity.get(event.getPersonId()).startsWith(LeipzigUtils.getActivityPrefixForDailyParkingCosts()) && !hasAlreadyPaidDailyResidentialParkingCosts.contains(event.getPersonId())) { // daily residential parking costs hasAlreadyPaidDailyResidentialParkingCosts.add(event.getPersonId()); double residentialParkingFeePerDay = 0.; - if (link.getAttributes().getAttribute(LeipzigUtils.RESIDENTIAL_PARKING_FEE_PER_DAY) != null) { - residentialParkingFeePerDay = (double) link.getAttributes().getAttribute(LeipzigUtils.RESIDENTIAL_PARKING_FEE_PER_DAY); + if (link.getAttributes().getAttribute(LeipzigUtils.getResidentialParkingFeeAttributeName()) != null) { + residentialParkingFeePerDay = (double) link.getAttributes().getAttribute(LeipzigUtils.getResidentialParkingFeeAttributeName()); } if (residentialParkingFeePerDay > 0.) { @@ -199,33 +195,33 @@ public void handleEvent(PersonEntersVehicleEvent event) { int parkingDurationHrs = (int) Math.ceil((event.getTime() - parkingStartTime) / 3600.); double extraHourParkingCosts = 0.; - if (link.getAttributes().getAttribute(LeipzigUtils.EXTRA_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME) != null) { - extraHourParkingCosts = (double) link.getAttributes().getAttribute(LeipzigUtils.EXTRA_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME); + if (link.getAttributes().getAttribute(LeipzigUtils.getExtraHourParkingCostLinkAttributeName()) != null) { + extraHourParkingCosts = (double) link.getAttributes().getAttribute(LeipzigUtils.getExtraHourParkingCostLinkAttributeName()); } double firstHourParkingCosts = 0.; - if (link.getAttributes().getAttribute(LeipzigUtils.FIRST_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME) != null) { - firstHourParkingCosts = (double) link.getAttributes().getAttribute(LeipzigUtils.FIRST_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME); + if (link.getAttributes().getAttribute(LeipzigUtils.getFirstHourParkingCostLinkAttributeName()) != null) { + firstHourParkingCosts = (double) link.getAttributes().getAttribute(LeipzigUtils.getFirstHourParkingCostLinkAttributeName()); } double dailyParkingCosts = firstHourParkingCosts + 29 * extraHourParkingCosts; - if (link.getAttributes().getAttribute(parkingCostConfigGroup.getDailyParkingCostLinkAttributeName()) != null) { - dailyParkingCosts = (double) link.getAttributes().getAttribute(parkingCostConfigGroup.getDailyParkingCostLinkAttributeName()); + if (link.getAttributes().getAttribute(LeipzigUtils.getDailyParkingCostLinkAttributeName()) != null) { + dailyParkingCosts = (double) link.getAttributes().getAttribute(LeipzigUtils.getDailyParkingCostLinkAttributeName()); } double maxDailyParkingCosts = dailyParkingCosts; - if (link.getAttributes().getAttribute(parkingCostConfigGroup.getMaxDailyParkingCostLinkAttributeName()) != null) { - maxDailyParkingCosts = (double) link.getAttributes().getAttribute(parkingCostConfigGroup.getMaxDailyParkingCostLinkAttributeName()); + if (link.getAttributes().getAttribute(LeipzigUtils.getMaxDailyParkingCostLinkAttributeName()) != null) { + maxDailyParkingCosts = (double) link.getAttributes().getAttribute(LeipzigUtils.getMaxDailyParkingCostLinkAttributeName()); } double maxParkingDurationHrs = 30; - if (link.getAttributes().getAttribute(parkingCostConfigGroup.getMaxParkingDurationAttributeName()) != null) { - maxParkingDurationHrs = (double) link.getAttributes().getAttribute(parkingCostConfigGroup.getMaxParkingDurationAttributeName()); + if (link.getAttributes().getAttribute(LeipzigUtils.getMaxParkingDurationAttributeName()) != null) { + maxParkingDurationHrs = (double) link.getAttributes().getAttribute(LeipzigUtils.getMaxParkingDurationAttributeName()); } double parkingPenalty = 0.; - if (link.getAttributes().getAttribute(parkingCostConfigGroup.getParkingPenaltyAttributeName()) != null) { - parkingPenalty = (double) link.getAttributes().getAttribute(parkingCostConfigGroup.getParkingPenaltyAttributeName()); + if (link.getAttributes().getAttribute(LeipzigUtils.getParkingPenaltyAttributeName()) != null) { + parkingPenalty = (double) link.getAttributes().getAttribute(LeipzigUtils.getParkingPenaltyAttributeName()); } double costs = 0.; diff --git a/src/main/java/org/matsim/run/prepare/LeipzigUtils.java b/src/main/java/org/matsim/run/prepare/LeipzigUtils.java index ff78be73..d2c9beb1 100644 --- a/src/main/java/org/matsim/run/prepare/LeipzigUtils.java +++ b/src/main/java/org/matsim/run/prepare/LeipzigUtils.java @@ -2,15 +2,26 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.Person; +import org.matsim.core.utils.collections.CollectionUtils; + +import java.util.HashSet; +import java.util.Set; /** * Utils class to adapt scenario-related person / link attributes. */ public final class LeipzigUtils{ - public static final String FIRST_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME = "firstHourParkingCostLinkAttributeName"; - public static final String EXTRA_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME = "extraHourParkingCostLinkAttributeName"; - public static final String RESIDENTIAL_PARKING_FEE_PER_DAY = "residentialParkingFeePerDay"; + private static final String mode = "car"; + private static final String dailyParkingCostLinkAttributeName = "dailyPCost"; + private static final String firstHourParkingCostLinkAttributeName = "oneHourPCost"; + private static final String extraHourParkingCostLinkAttributeName = "extraHourPCost"; + private static final String maxDailyParkingCostLinkAttributeName = "maxDailyPCost"; + private static final String maxParkingDurationAttributeName = "maxPDuration"; + private static final String parkingPenaltyAttributeName = "penalty"; + private static final String residentialParkingFeePerDay = "residentialPFee"; + private static final String activityPrefixForDailyParkingCosts = "home"; + private static final Set activityPrefixToBeExcludedFromParkingCost = new HashSet<>(); // do not instantiate private LeipzigUtils(){} @@ -27,6 +38,36 @@ public static boolean parkingIsRestricted( Link link ) { } } + public static String getMode() { return mode; } + + public static String getDailyParkingCostLinkAttributeName() { + return dailyParkingCostLinkAttributeName; + } + + public static String getFirstHourParkingCostLinkAttributeName() { return firstHourParkingCostLinkAttributeName; } + + public static String getExtraHourParkingCostLinkAttributeName() { return extraHourParkingCostLinkAttributeName; } + + public static String getMaxDailyParkingCostLinkAttributeName() { + return maxDailyParkingCostLinkAttributeName; + } + + public static String getMaxParkingDurationAttributeName() { + return maxParkingDurationAttributeName; + } + + public static String getParkingPenaltyAttributeName() { + return parkingPenaltyAttributeName; + } + + public static String getResidentialParkingFeeAttributeName() { return residentialParkingFeePerDay; } + + public static String getActivityPrefixForDailyParkingCosts() { + return activityPrefixForDailyParkingCosts; + } + + public static Set getActivityPrefixesToBeExcludedFromParkingCost() { return activityPrefixToBeExcludedFromParkingCost; } + public static void setParkingToRestricted( Link link ){ link.getAttributes().putAttribute( "parking", "restricted" ); } diff --git a/src/main/java/org/matsim/run/prepare/PrepareNetwork.java b/src/main/java/org/matsim/run/prepare/PrepareNetwork.java index dce90ee7..836aa21b 100644 --- a/src/main/java/org/matsim/run/prepare/PrepareNetwork.java +++ b/src/main/java/org/matsim/run/prepare/PrepareNetwork.java @@ -188,9 +188,9 @@ static void prepareParkingCost(Network network, ShpOptions parkingCostShape) { } } - LeipzigUtils.setLinkAttribute(link, LeipzigUtils.FIRST_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME, oneHourPCost); - LeipzigUtils.setLinkAttribute(link, LeipzigUtils.EXTRA_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME, extraHourPCost); - LeipzigUtils.setLinkAttribute(link, LeipzigUtils.RESIDENTIAL_PARKING_FEE_PER_DAY, resPFee); + LeipzigUtils.setLinkAttribute(link, LeipzigUtils.getFirstHourParkingCostLinkAttributeName(), oneHourPCost); + LeipzigUtils.setLinkAttribute(link, LeipzigUtils.getExtraHourParkingCostLinkAttributeName(), extraHourPCost); + LeipzigUtils.setLinkAttribute(link, LeipzigUtils.getResidentialParkingFeeAttributeName(), resPFee); } } diff --git a/src/test/java/org/matsim/run/TimeRestrictedParkingCostHandlerTest.java b/src/test/java/org/matsim/run/TimeRestrictedParkingCostHandlerTest.java index 9b6969cc..afdeb6b4 100644 --- a/src/test/java/org/matsim/run/TimeRestrictedParkingCostHandlerTest.java +++ b/src/test/java/org/matsim/run/TimeRestrictedParkingCostHandlerTest.java @@ -49,14 +49,14 @@ void createExamplePopulation(Population population, Scenario scenario, Situation Plan plan = factory.createPlan(); Link startLink = scenario.getNetwork().getLinks().get(Id.createLinkId("76")); - startLink.getAttributes().putAttribute(LeipzigUtils.FIRST_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME, 1.); - startLink.getAttributes().putAttribute(LeipzigUtils.EXTRA_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME, 1.); - startLink.getAttributes().putAttribute(LeipzigUtils.RESIDENTIAL_PARKING_FEE_PER_DAY, 1.); + startLink.getAttributes().putAttribute(LeipzigUtils.getFirstHourParkingCostLinkAttributeName(), 1.); + startLink.getAttributes().putAttribute(LeipzigUtils.getExtraHourParkingCostLinkAttributeName(), 1.); + startLink.getAttributes().putAttribute(LeipzigUtils.getResidentialParkingFeeAttributeName(), 1.); Link destinationLink = scenario.getNetwork().getLinks().get(Id.createLinkId("78")); - destinationLink.getAttributes().putAttribute(LeipzigUtils.FIRST_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME, 1.); - destinationLink.getAttributes().putAttribute(LeipzigUtils.EXTRA_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME, 1.); - destinationLink.getAttributes().putAttribute(LeipzigUtils.RESIDENTIAL_PARKING_FEE_PER_DAY, 1.); + destinationLink.getAttributes().putAttribute(LeipzigUtils.getFirstHourParkingCostLinkAttributeName(), 1.); + destinationLink.getAttributes().putAttribute(LeipzigUtils.getExtraHourParkingCostLinkAttributeName(), 1.); + destinationLink.getAttributes().putAttribute(LeipzigUtils.getResidentialParkingFeeAttributeName(), 1.); Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.WORK, startLink.getId()); Activity destinationActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, destinationLink.getId()); From 4e07fd5ec1d50535e3e6d287d4f255c4c3f6ef68 Mon Sep 17 00:00:00 2001 From: simei94 Date: Tue, 9 May 2023 18:17:51 +0200 Subject: [PATCH 022/106] first try to clean up test + algo class and apply a more specific name pattern --- .../run/prepare/AssignParkingAttributes.java | 4 +- .../org/matsim/run/prepare/LeipzigUtils.java | 46 ++++++++++++----- .../org/matsim/run/ChessboardParkingTest.java | 49 +++++++++++-------- .../run/LeipzigRouterPlanAlgorithm.java | 25 +++++----- .../run/prepare/NetworkOptionsTest.java | 15 +++--- 5 files changed, 84 insertions(+), 55 deletions(-) diff --git a/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java b/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java index ec935add..fc8d7998 100644 --- a/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java +++ b/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java @@ -31,9 +31,9 @@ static void addParkingAttributesToPopulation(Population population, ShpOptions s isInsideRestrictedParkingArea = MGC.coord2Point(activity.getCoord()).within(restrictedParkingArea); if (isInsideRestrictedParkingArea) { - LeipzigUtils.setParkingToRestricted(person); + LeipzigUtils.setPersonParkingType(person, LeipzigUtils.PersonParkingType.closestToActivity); } else { - LeipzigUtils.setParkingToNonRestricted(person); + LeipzigUtils.setPersonParkingType(person, LeipzigUtils.PersonParkingType.restrictedForNonResidents); } break; diff --git a/src/main/java/org/matsim/run/prepare/LeipzigUtils.java b/src/main/java/org/matsim/run/prepare/LeipzigUtils.java index d2c9beb1..2d764066 100644 --- a/src/main/java/org/matsim/run/prepare/LeipzigUtils.java +++ b/src/main/java/org/matsim/run/prepare/LeipzigUtils.java @@ -2,7 +2,6 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.Person; -import org.matsim.core.utils.collections.CollectionUtils; import java.util.HashSet; import java.util.Set; @@ -23,6 +22,18 @@ public final class LeipzigUtils{ private static final String activityPrefixForDailyParkingCosts = "home"; private static final Set activityPrefixToBeExcludedFromParkingCost = new HashSet<>(); + /** + * Defines if agent is allowed to park on the closest link to the destination activity (closestToActivity) + * = has residential parking card + * or if the agent does not possess a residential parking card and therefore has to search for + * a parking link on a reduced network (restrictedForNonResidents) + */ + public enum PersonParkingType { + closestToActivity, + restrictedForNonResidents, + shopping + } + // do not instantiate private LeipzigUtils(){} @@ -38,15 +49,21 @@ public static boolean parkingIsRestricted( Link link ) { } } - public static String getMode() { return mode; } + public static String getMode() { + return mode; + } public static String getDailyParkingCostLinkAttributeName() { return dailyParkingCostLinkAttributeName; } - public static String getFirstHourParkingCostLinkAttributeName() { return firstHourParkingCostLinkAttributeName; } + public static String getFirstHourParkingCostLinkAttributeName() { + return firstHourParkingCostLinkAttributeName; + } - public static String getExtraHourParkingCostLinkAttributeName() { return extraHourParkingCostLinkAttributeName; } + public static String getExtraHourParkingCostLinkAttributeName() { + return extraHourParkingCostLinkAttributeName; + } public static String getMaxDailyParkingCostLinkAttributeName() { return maxDailyParkingCostLinkAttributeName; @@ -60,25 +77,30 @@ public static String getParkingPenaltyAttributeName() { return parkingPenaltyAttributeName; } - public static String getResidentialParkingFeeAttributeName() { return residentialParkingFeePerDay; } + public static String getResidentialParkingFeeAttributeName() { + return residentialParkingFeePerDay; + } public static String getActivityPrefixForDailyParkingCosts() { return activityPrefixForDailyParkingCosts; } - public static Set getActivityPrefixesToBeExcludedFromParkingCost() { return activityPrefixToBeExcludedFromParkingCost; } + public static Set getActivityPrefixesToBeExcludedFromParkingCost() { + return activityPrefixToBeExcludedFromParkingCost; + } - public static void setParkingToRestricted( Link link ){ - link.getAttributes().putAttribute( "parking", "restricted" ); + public static void setLinkParkingToNotInResidentialArea(Link link ){ + link.getAttributes().putAttribute( "parking", "notInsideResidentialArea"); } // yy change the logic of the above to enums - public static void setParkingToRestricted(Person person) { - person.getAttributes().putAttribute("parkingType", "residentialParking"); + public static void setLinkToResidentialArea( Link link ) { + link.getAttributes().putAttribute("parkingType", ""); + } - public static void setParkingToNonRestricted(Person person) { - person.getAttributes().putAttribute("parkingType", "nonResidentialParking"); + public static void setPersonParkingType(Person person, PersonParkingType parkingType) { + person.getAttributes().putAttribute("parkingType", parkingType.toString()); } public static void setLinkAttribute(Link link, String attributeName, double attributeValue) { diff --git a/src/test/java/org/matsim/run/ChessboardParkingTest.java b/src/test/java/org/matsim/run/ChessboardParkingTest.java index dd491be5..62f6beca 100644 --- a/src/test/java/org/matsim/run/ChessboardParkingTest.java +++ b/src/test/java/org/matsim/run/ChessboardParkingTest.java @@ -196,32 +196,36 @@ static void createExampleParkingPopulation(Population population, Network networ final Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, originLink.getId()); originActivity.setEndTime(3600.); plan.addActivity(originActivity); - plan.addLeg(factory.createLeg(TransportMode.car)); + plan.addLeg(carLeg); plan.addActivity(factory.createActivityFromLinkId(ActivityTypes.LEISURE, destinationLink.getId())); person.addPlan(plan); population.addPerson(person); switch (situation) { - case normalToNormal -> { - // do nothing - } - case restrictedToNormal -> { - LeipzigUtils.setParkingToRestricted(originLink); - } +// case normalToNormal -> { +// // do nothing +// } +// case restrictedToNormal -> { +// LeipzigUtils.setParkingToRestricted(originLink); +// } +// +// // yy Ich habe den Setup der Fälle, die hier kommen, leider nicht verstanden. Daher habe ich obigen Fall "restrictedToNormal" neu gebaut. Sorry ... kai, apr'23 +// +// case fromShpFile -> { +//// for( Link link : network.getLinks().values() ){ +//// if ( link is in polygon ) { +//// LeipzigUtils.parkingIsRestricted( link ); +//// } +//// } +// } + + //koennen die 3 oberen Faelle = normalToNormal, restrictedToNormal, fromShpFile nicht jetzt weg? - // yy Ich habe den Setup der Fälle, die hier kommen, leider nicht verstanden. Daher habe ich obigen Fall "restrictedToNormal" neu gebaut. Sorry ... kai, apr'23 - case fromShpFile -> { -// for( Link link : network.getLinks().values() ){ -// if ( link is in polygon ) { -// LeipzigUtils.parkingIsRestricted( link ); -// } -// } - } case residentInResidentialArea -> { population.getPersons().clear(); Person residentInResidentialArea = factory.createPerson(Id.createPersonId(situation.toString())); - LeipzigUtils.setParkingToNonRestricted(residentInResidentialArea); + LeipzigUtils.setPersonParkingType(residentInResidentialArea, LeipzigUtils.PersonParkingType.closestToActivity); LeipzigUtils.parkingIsRestricted(originLink); residentInResidentialArea.addPlan(plan); population.addPerson(residentInResidentialArea); @@ -229,6 +233,7 @@ static void createExampleParkingPopulation(Population population, Network networ case residentOutsideResidentialArea -> { population.getPersons().clear(); Person residentOutsideResidentialArea = factory.createPerson(Id.createPersonId(situation.toString())); + LeipzigUtils.setPersonParkingType(residentOutsideResidentialArea, LeipzigUtils.PersonParkingType.closestToActivity); residentOutsideResidentialArea.addPlan(plan); population.addPerson(residentOutsideResidentialArea); } @@ -236,15 +241,16 @@ static void createExampleParkingPopulation(Population population, Network networ population.getPersons().clear(); Person nonResidentInResidentialAreaNoShop = factory.createPerson(Id.createPersonId(situation.toString())); nonResidentInResidentialAreaNoShop.addPlan(plan); - LeipzigUtils.setParkingToRestricted(nonResidentInResidentialAreaNoShop); - LeipzigUtils.setParkingToRestricted(destinationLink); + LeipzigUtils.setPersonParkingType(nonResidentInResidentialAreaNoShop, LeipzigUtils.PersonParkingType.restrictedForNonResidents); + //wrong, we need to clean this up +// LeipzigUtils.setLinkParkingToNotInResidentialArea(destinationLink); population.addPerson(nonResidentInResidentialAreaNoShop); } case nonResidentOutsideResidentialArea -> { population.getPersons().clear(); Person nonResidentOutsideResidentialArea = factory.createPerson(Id.createPersonId(situation.toString())); nonResidentOutsideResidentialArea.addPlan(plan); - LeipzigUtils.setParkingToRestricted(nonResidentOutsideResidentialArea); + LeipzigUtils.setPersonParkingType(nonResidentOutsideResidentialArea, LeipzigUtils.PersonParkingType.restrictedForNonResidents); population.addPerson(nonResidentOutsideResidentialArea); } case nonResidentInResidentialAreaShop -> { @@ -257,8 +263,9 @@ static void createExampleParkingPopulation(Population population, Network networ planForShopping.addActivity(factory.createActivityFromLinkId(ActivityTypes.SHOPPING, destinationLink.getId())); population.addPerson(nonResidentInResidentialAreaShop); nonResidentInResidentialAreaShop.addPlan(planForShopping); - LeipzigUtils.setParkingToRestricted(nonResidentInResidentialAreaShop); - LeipzigUtils.setParkingToRestricted(destinationLink); + LeipzigUtils.setPersonParkingType(nonResidentInResidentialAreaShop, LeipzigUtils.PersonParkingType.restrictedForNonResidents); + //this is wrong!!!!! -sm 090523 +// LeipzigUtils.setParkingToRestricted(destinationLink); LeipzigUtils.setParkingToShoppingCenter(linkForShopping); } diff --git a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index 033204d9..56970f92 100644 --- a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -65,8 +65,9 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ } log.warn( "returning LeipzigPlanRouter" ); } - enum ParkingType{normal, restricted, shopping} - @Override public void run( final Plan plan ){ + + @Override + public void run( final Plan plan ){ final List trips = TripStructureUtils.getTrips( plan ); TimeTracker timeTracker = new TimeTracker( timeInterpretation ); @@ -88,17 +89,17 @@ enum ParkingType{normal, restricted, shopping} // At this point, I only want to deal with residential parking. Shopping comes later (and is simpler). - ParkingType parkingTypeAtOrigin = getParkingType( fullModalNetwork, oldTrip.getOriginActivity() ); - ParkingType parkingTypeAtDestination = getParkingType( fullModalNetwork, oldTrip.getDestinationActivity() ); + LeipzigUtils.PersonParkingType parkingTypeAtOrigin = getParkingType( fullModalNetwork, oldTrip.getOriginActivity() ); + LeipzigUtils.PersonParkingType parkingTypeAtDestination = getParkingType( fullModalNetwork, oldTrip.getDestinationActivity() ); - if( parkingTypeAtOrigin == ParkingType.normal && parkingTypeAtDestination == ParkingType.normal ){ + if( parkingTypeAtOrigin == LeipzigUtils.PersonParkingType.closestToActivity && parkingTypeAtDestination == LeipzigUtils.PersonParkingType.closestToActivity ){ // standard case: final List newTripElements = tripRouter.calcRoute( routingMode, fromFacility, toFacility, timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity() ); timeTracker.addElements( newTripElements ); - } else if( parkingTypeAtOrigin == ParkingType.restricted && parkingTypeAtDestination == ParkingType.normal ){ + } else if( parkingTypeAtOrigin == LeipzigUtils.PersonParkingType.restrictedForNonResidents && parkingTypeAtDestination == LeipzigUtils.PersonParkingType.closestToActivity ){ // restricted parking at origin: // first find parking: // final Link parkingLink = NetworkUtils.getNearestLink( reducedNetwork, oldTrip.getOriginActivity().getCoord() ); @@ -128,7 +129,7 @@ enum ParkingType{normal, restricted, shopping} timeTracker.addElements( newTripElements ); - } else if (parkingTypeAtOrigin == ParkingType.normal && parkingTypeAtDestination == ParkingType.restricted) { + } else if (parkingTypeAtOrigin == LeipzigUtils.PersonParkingType.closestToActivity && parkingTypeAtDestination == LeipzigUtils.PersonParkingType.restrictedForNonResidents) { //parking at destination final Link parkingLink = linkChooser.decideOnLink( toFacility, reducedNetwork ); @@ -158,7 +159,7 @@ enum ParkingType{normal, restricted, shopping} TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity() ); timeTracker.addElements(newTripElements); - } else if (parkingTypeAtOrigin == ParkingType.normal && parkingTypeAtDestination == ParkingType.shopping) { + } else if (parkingTypeAtOrigin == LeipzigUtils.PersonParkingType.closestToActivity && parkingTypeAtDestination == LeipzigUtils.PersonParkingType.shopping) { // can this be done better??? i need only the links with shopping garages for( Node node : this.fullModalNetwork.getNodes().values() ){ @@ -216,8 +217,8 @@ enum ParkingType{normal, restricted, shopping} log.warn( "======" ); } - private static ParkingType getParkingType( Network fullModalNetwork, Activity originActivity ){ - ParkingType parkingTypeAtOrigin = ParkingType.normal; + private static LeipzigUtils.PersonParkingType getParkingType(Network fullModalNetwork, Activity originActivity ){ + LeipzigUtils.PersonParkingType parkingTypeAtOrigin = LeipzigUtils.PersonParkingType.closestToActivity; // if we find out that there are time restrictions on all the links //originActivity.getEndTime(); @@ -228,11 +229,11 @@ private static ParkingType getParkingType( Network fullModalNetwork, Activity or // if non-home activity, check if in restricted zone: Link link = fullModalNetwork.getLinks().get( originActivity.getLinkId() ); if( parkingIsRestricted( link ) ){ - parkingTypeAtOrigin = ParkingType.restricted; + parkingTypeAtOrigin = LeipzigUtils.PersonParkingType.restrictedForNonResidents; if (originActivity.getType().equals(ActivityTypes.SHOPPING)) { // change this to parking type normal/ unrestricted // on activity link, unrestricted - parkingTypeAtOrigin = ParkingType.shopping; + parkingTypeAtOrigin = LeipzigUtils.PersonParkingType.shopping; } } diff --git a/src/test/java/org/matsim/run/prepare/NetworkOptionsTest.java b/src/test/java/org/matsim/run/prepare/NetworkOptionsTest.java index 5e1abed2..c6ec49c0 100644 --- a/src/test/java/org/matsim/run/prepare/NetworkOptionsTest.java +++ b/src/test/java/org/matsim/run/prepare/NetworkOptionsTest.java @@ -78,21 +78,20 @@ public void runParkingCostAreaCreationTest() { new CommandLine(options).parseArgs( "--parking-cost-area", shpPath); options.prepare(network); - ParkingCostConfigGroup parkingCostConfigGroup = ConfigUtils.addOrGetModule(new Config(), ParkingCostConfigGroup.class); - //parkingCost values (2.0 and 0.1) are defined in file shpPath Assert.assertEquals(Double.parseDouble(network.getLinks().get(Id.createLinkId("-10424519")) - .getAttributes().getAttribute(parkingCostConfigGroup.getFirstHourParkingCostLinkAttributeName()).toString()),2.0, 0); + .getAttributes().getAttribute(LeipzigUtils.getFirstHourParkingCostLinkAttributeName()).toString()),2.0, 0); Assert.assertEquals(Double.parseDouble(network.getLinks().get(Id.createLinkId("-10424519")) - .getAttributes().getAttribute(parkingCostConfigGroup.getExtraHourParkingCostLinkAttributeName()).toString()),2.0, 0); + .getAttributes().getAttribute(LeipzigUtils.getExtraHourParkingCostLinkAttributeName()).toString()),2.0, 0); Assert.assertEquals(Double.parseDouble(network.getLinks().get(Id.createLinkId("-10424519")) - .getAttributes().getAttribute(parkingCostConfigGroup.getResidentialParkingFeeAttributeName()).toString()),0.1, 0); + .getAttributes().getAttribute(LeipzigUtils.getResidentialParkingFeeAttributeName()).toString()),0.1, 0); Assert.assertEquals(Double.parseDouble(network.getLinks().get(Id.createLinkId("827435967#0")) - .getAttributes().getAttribute(parkingCostConfigGroup.getFirstHourParkingCostLinkAttributeName()).toString()),2.0, 0); + .getAttributes().getAttribute(LeipzigUtils.getFirstHourParkingCostLinkAttributeName()).toString()),2.0, 0); Assert.assertEquals(Double.parseDouble(network.getLinks().get(Id.createLinkId("827435967#0")) - .getAttributes().getAttribute(parkingCostConfigGroup.getExtraHourParkingCostLinkAttributeName()).toString()),2.0, 0); + .getAttributes().getAttribute(LeipzigUtils.getExtraHourParkingCostLinkAttributeName()).toString()),2.0, 0); Assert.assertEquals(Double.parseDouble(network.getLinks().get(Id.createLinkId("827435967#0")) - .getAttributes().getAttribute(parkingCostConfigGroup.getResidentialParkingFeeAttributeName()).toString()),0.1, 0); + .getAttributes().getAttribute(LeipzigUtils.getResidentialParkingFeeAttributeName()).toString()),0.1, 0); + } From 157a86b21077ce5c5d4a9a2eb5ad51a638e1fa7b Mon Sep 17 00:00:00 2001 From: rakow Date: Fri, 12 May 2023 20:14:55 +0200 Subject: [PATCH 023/106] commercial traffic updates --- Makefile | 4 +- input/v1.2/leipzig-v1.2-25pct.config.xml | 55 +++--- pom.xml | 2 +- .../org/matsim/run/RunLeipzigScenario.java | 48 +++-- .../org/matsim/run/StrategyWeightFadeout.java | 167 ------------------ 5 files changed, 57 insertions(+), 219 deletions(-) delete mode 100644 src/main/java/org/matsim/run/StrategyWeightFadeout.java diff --git a/Makefile b/Makefile index 29e208db..4c49c1c5 100644 --- a/Makefile +++ b/Makefile @@ -76,7 +76,7 @@ input/$V/leipzig-$V-network.xml.gz: input/sumo.net.xml input/$V/leipzig-$V-network-with-pt.xml.gz: input/$V/leipzig-$V-network.xml.gz input/gtfs-lvb.zip $(sc) prepare transit-from-gtfs --network $< $(filter-out $<,$^)\ - --name leipzig-$V --date "2019-06-05" --target-crs $(CRS)\ + --name leipzig-$V --date "2023-04-19" --target-crs $(CRS)\ --output input/$V $(sc) prepare prepare-transit-schedule\ @@ -146,7 +146,7 @@ input/$V/leipzig-$V-25pct.plans-initial.xml.gz: input/plans-longHaulFreight.xml. $(sc) prepare fix-subtour-modes --input $@ --coord-dist 100 --output $@ - $(sc) prepare merge-populations $@ $< --output $@ + $(sc) prepare merge-populations $@ $^ --output $@ $(sc) prepare extract-home-coordinates $@ --csv input/$V/leipzig-$V-homes.csv diff --git a/input/v1.2/leipzig-v1.2-25pct.config.xml b/input/v1.2/leipzig-v1.2-25pct.config.xml index d8de8daf..f99dceb9 100644 --- a/input/v1.2/leipzig-v1.2-25pct.config.xml +++ b/input/v1.2/leipzig-v1.2-25pct.config.xml @@ -23,13 +23,13 @@ - + + value="./leipzig-v1.2-25pct.plans-initial.xml.gz"/> @@ -39,9 +39,9 @@ + value="./leipzig-v1.2-transitSchedule.xml.gz"/> + value="./leipzig-v1.2-transitVehicles.xml.gz"/> @@ -57,8 +57,8 @@ - - + + @@ -70,11 +70,24 @@ - - + + + + + + + + + + + + + + + @@ -87,43 +100,25 @@ - - - - - - - - - - - - - - + - + - - - - - - + @@ -308,4 +303,4 @@ - \ No newline at end of file + diff --git a/pom.xml b/pom.xml index 48580971..263adb3e 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.matsim matsim-all - 16.0-PR2559 + 16.0-PR2579 4.0.0 diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index 47d9b89a..d4876a47 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -47,10 +47,7 @@ import org.matsim.contrib.vsp.scenario.SnzActivities; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.PlansCalcRouteConfigGroup; -import org.matsim.core.config.groups.QSimConfigGroup; -import org.matsim.core.config.groups.SubtourModeChoiceConfigGroup; -import org.matsim.core.config.groups.VspExperimentalConfigGroup; +import org.matsim.core.config.groups.*; import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; import org.matsim.core.replanning.choosers.ForceInnovationStrategyChooser; @@ -98,7 +95,7 @@ public class RunLeipzigScenario extends MATSimApplication { private static final Logger log = LogManager.getLogger(RunLeipzigScenario.class); - static final String VERSION = "1.1"; + static final String VERSION = "1.2"; @CommandLine.Mixin private final SampleOptions sample = new SampleOptions(1, 10, 25); @@ -146,6 +143,28 @@ protected Config prepareConfig(Config config) { SnzActivities.addScoringParams(config); + // Prepare commercial config + config.planCalcScore().addActivityParams(new PlanCalcScoreConfigGroup.ActivityParams("service").setTypicalDuration(3600)); + config.planCalcScore().addActivityParams(new PlanCalcScoreConfigGroup.ActivityParams("start").setTypicalDuration(3600)); + config.planCalcScore().addActivityParams(new PlanCalcScoreConfigGroup.ActivityParams("end").setTypicalDuration(3600)); + + // freight is long-haul freight + // freightTraffic is rather short distance + for (String subpopulation : List.of("freight", "freightTraffic", "businessTraffic", "businessTraffic_service")) { + config.strategy().addStrategySettings( + new StrategyConfigGroup.StrategySettings() + .setStrategyName(DefaultPlanStrategiesModule.DefaultSelector.ChangeExpBeta) + .setWeight(0.95) + .setSubpopulation(subpopulation) + ); + config.strategy().addStrategySettings( + new StrategyConfigGroup.StrategySettings() + .setStrategyName(DefaultPlanStrategiesModule.DefaultStrategy.ReRoute) + .setWeight(0.05) + .setSubpopulation(subpopulation) + ); + } + if (sample.isSet()) { config.controler().setOutputDirectory(sample.adjustName(config.controler().getOutputDirectory())); config.controler().setRunId(sample.adjustName(config.controler().getRunId())); @@ -193,6 +212,10 @@ protected Config prepareConfig(Config config) { ConfigUtils.addOrGetModule(config, ParkingCostConfigGroup.class); } + // Need to initialize even if disabled + ConfigUtils.addOrGetModule(config, DvrpConfigGroup.class); + ConfigUtils.addOrGetModule(config, MultiModeDrtConfigGroup.class); + return config; } @@ -247,20 +270,7 @@ public void install() { install(new PersonMoneyEventsAnalysisModule()); } - addControlerListenerBinding().to(StrategyWeightFadeout.class).in(Singleton.class); - - Multibinder schedules = StrategyWeightFadeout.getBinder(binder()); - - // Mode-choice fades out earlier than the other strategies - // Given a fixed mode, the "less disruptive" choice dimensions will be weighted higher during the end - schedules.addBinding().toInstance(new StrategyWeightFadeout.Schedule(DefaultPlanStrategiesModule.DefaultStrategy.SubtourModeChoice, "person", 0.65, 0.80)); - - // Fades out until 0.9 (innovation switch off) - schedules.addBinding().toInstance(new StrategyWeightFadeout.Schedule(DefaultPlanStrategiesModule.DefaultStrategy.ReRoute, "person", 0.75)); - schedules.addBinding().toInstance(new StrategyWeightFadeout.Schedule(DefaultPlanStrategiesModule.DefaultStrategy.TimeAllocationMutator, "person", 0.75)); - - bind(new TypeLiteral>() { - }).toInstance(new ForceInnovationStrategyChooser<>(10, ForceInnovationStrategyChooser.Permute.yes)); + bind(new TypeLiteral>() {}).toInstance(new ForceInnovationStrategyChooser<>(10, ForceInnovationStrategyChooser.Permute.yes)); } }); diff --git a/src/main/java/org/matsim/run/StrategyWeightFadeout.java b/src/main/java/org/matsim/run/StrategyWeightFadeout.java deleted file mode 100644 index 45563553..00000000 --- a/src/main/java/org/matsim/run/StrategyWeightFadeout.java +++ /dev/null @@ -1,167 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* - * Controler.java - * * - * *********************************************************************** * - * * - * copyright : (C) 2007 by the members listed in the COPYING, * - * LICENSE and WARRANTY file. * - * email : info at matsim dot org * - * * - * *********************************************************************** * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * See also COPYING, LICENSE and WARRANTY file * - * * - * *********************************************************************** */ - -package org.matsim.run; - -import com.google.inject.Binder; -import com.google.inject.Inject; -import com.google.inject.multibindings.Multibinder; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.matsim.api.core.v01.population.Person; -import org.matsim.api.core.v01.population.Plan; -import org.matsim.core.config.Config; -import org.matsim.core.config.groups.StrategyConfigGroup; -import org.matsim.core.controler.events.IterationStartsEvent; -import org.matsim.core.controler.listener.IterationStartsListener; -import org.matsim.core.replanning.GenericPlanStrategy; -import org.matsim.core.replanning.PlanStrategy; -import org.matsim.core.replanning.StrategyManager; - -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -/** - * Fade-out the strategy weight during the simulation. - * This class as well as the {@link Schedule}s has be bound with guice in the controller. - */ -public final class StrategyWeightFadeout implements IterationStartsListener { - - private final Logger log = LogManager.getLogger(StrategyWeightFadeout.class); - - @Inject - private Map planStrategies; - - @Inject - private Config config; - - @Inject - private StrategyManager strategyManager; - - @Inject - private Set schedules; - - @Override - public void notifyIterationStarts(IterationStartsEvent iterationStartsEvent) { - - for (Schedule s : schedules) { - - StrategyConfigGroup.StrategySettings settings = null; - - for (StrategyConfigGroup.StrategySettings strategySettings : planStrategies.keySet()) { - if (strategySettings.getStrategyName().equals(s.name) && strategySettings.getSubpopulation().equals(s.subpopulation)) { - settings = strategySettings; - break; - } - } - - if (settings == null) { - log.info("Strategy settings for {} not found", s.name); - continue; - } - - String strategyName = settings.getStrategyName(); - - if (Double.isNaN(s.initialWeight)) { - s.initialWeight = settings.getWeight(); - s.startIteration = (int) (config.controler().getLastIteration() * s.startAt); - double disable = config.strategy().getFractionOfIterationsToDisableInnovation(); - - // use disable after if it is set - if (!Double.isNaN(s.endAt)) - s.endIteration = (int) (config.controler().getLastIteration() * s.endAt); - else if (settings.getDisableAfter() > 0 && settings.getDisableAfter() < Integer.MAX_VALUE && settings.getDisableAfter() <= disable) - s.endIteration = settings.getDisableAfter(); - else if (Double.isFinite(disable) && disable < Integer.MAX_VALUE) - s.endIteration = (int) (config.controler().getLastIteration() * disable); - else - s.endIteration = settings.getDisableAfter(); - - log.info("{} fadeout from iteration {} to {} with start weight {}", strategyName, s.startIteration, s.endIteration, s.initialWeight); - } - - // Find the implementation to update the strategy weight - List> strategies = strategyManager.getStrategies(s.subpopulation); - Optional> strategy = strategies.stream().filter(st -> st.toString().contains(strategyName)).findFirst(); - - if (strategy.isEmpty()) { - log.warn("Could not find loaded strategy for {}", strategy); - return; - } - - if (iterationStartsEvent.getIteration() > s.startIteration && iterationStartsEvent.getIteration() <= s.endIteration) { - double step = s.initialWeight / (s.endIteration - s.startIteration); - double weight = s.initialWeight + step * (s.startIteration - iterationStartsEvent.getIteration()); - - log.info("Setting {} weight at iteration {} to {}", strategyName, iterationStartsEvent.getIteration(), weight); - - strategyManager.changeWeightOfStrategy(strategy.get(), s.subpopulation, weight); - } - } - } - - /** - * Get the binder which is needed to add {@link Schedule}. - */ - public static Multibinder getBinder(Binder binder) { - return Multibinder.newSetBinder(binder, StrategyWeightFadeout.Schedule.class); - } - - /** - * Defines the fade-out schedule for certain strategies. - */ - public static class Schedule { - - /** - * Start weight for fade-out. - */ - private double initialWeight = Double.NaN; - - /** - * Start and end iteration for fade-out. - */ - private int startIteration; - private int endIteration; - - private final String name; - private final String subpopulation; - private final double startAt; - private final double endAt; - - /** - * Constructor where the end is taken from the config and not given explicitly. - */ - public Schedule(String name, String subpopulation, double startAt) { - this.name = name; - this.subpopulation = subpopulation; - this.startAt = startAt; - this.endAt = Double.NaN; - } - - public Schedule(String name, String subpopulation, double startAt, double endAt) { - this.name = name; - this.subpopulation = subpopulation; - this.startAt = startAt; - this.endAt = endAt; - } - } -} From 8ad10fc7d596cbbcdef698adce87925d0dc9f94f Mon Sep 17 00:00:00 2001 From: rakow Date: Fri, 12 May 2023 20:16:47 +0200 Subject: [PATCH 024/106] remove unused imports --- src/main/java/org/matsim/run/RunLeipzigScenario.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index d4876a47..c5679780 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -5,9 +5,7 @@ import ch.sbb.matsim.routing.pt.raptor.SwissRailRaptorModule; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; -import com.google.inject.Singleton; import com.google.inject.TypeLiteral; -import com.google.inject.multibindings.Multibinder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.matsim.analysis.*; From ff887aa598b55cb36db4c2753a158416b7299dbb Mon Sep 17 00:00:00 2001 From: simei94 Date: Tue, 16 May 2023 17:04:44 +0200 Subject: [PATCH 025/106] Revert "first try to clean up test + algo class and apply a more specific name pattern" This reverts commit 4e07fd5ec1d50535e3e6d287d4f255c4c3f6ef68. --- .../run/prepare/AssignParkingAttributes.java | 4 +- .../org/matsim/run/prepare/LeipzigUtils.java | 46 +++++------------ .../org/matsim/run/ChessboardParkingTest.java | 49 ++++++++----------- .../run/LeipzigRouterPlanAlgorithm.java | 25 +++++----- .../run/prepare/NetworkOptionsTest.java | 15 +++--- 5 files changed, 55 insertions(+), 84 deletions(-) diff --git a/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java b/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java index fc8d7998..ec935add 100644 --- a/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java +++ b/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java @@ -31,9 +31,9 @@ static void addParkingAttributesToPopulation(Population population, ShpOptions s isInsideRestrictedParkingArea = MGC.coord2Point(activity.getCoord()).within(restrictedParkingArea); if (isInsideRestrictedParkingArea) { - LeipzigUtils.setPersonParkingType(person, LeipzigUtils.PersonParkingType.closestToActivity); + LeipzigUtils.setParkingToRestricted(person); } else { - LeipzigUtils.setPersonParkingType(person, LeipzigUtils.PersonParkingType.restrictedForNonResidents); + LeipzigUtils.setParkingToNonRestricted(person); } break; diff --git a/src/main/java/org/matsim/run/prepare/LeipzigUtils.java b/src/main/java/org/matsim/run/prepare/LeipzigUtils.java index 2d764066..d2c9beb1 100644 --- a/src/main/java/org/matsim/run/prepare/LeipzigUtils.java +++ b/src/main/java/org/matsim/run/prepare/LeipzigUtils.java @@ -2,6 +2,7 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.Person; +import org.matsim.core.utils.collections.CollectionUtils; import java.util.HashSet; import java.util.Set; @@ -22,18 +23,6 @@ public final class LeipzigUtils{ private static final String activityPrefixForDailyParkingCosts = "home"; private static final Set activityPrefixToBeExcludedFromParkingCost = new HashSet<>(); - /** - * Defines if agent is allowed to park on the closest link to the destination activity (closestToActivity) - * = has residential parking card - * or if the agent does not possess a residential parking card and therefore has to search for - * a parking link on a reduced network (restrictedForNonResidents) - */ - public enum PersonParkingType { - closestToActivity, - restrictedForNonResidents, - shopping - } - // do not instantiate private LeipzigUtils(){} @@ -49,21 +38,15 @@ public static boolean parkingIsRestricted( Link link ) { } } - public static String getMode() { - return mode; - } + public static String getMode() { return mode; } public static String getDailyParkingCostLinkAttributeName() { return dailyParkingCostLinkAttributeName; } - public static String getFirstHourParkingCostLinkAttributeName() { - return firstHourParkingCostLinkAttributeName; - } + public static String getFirstHourParkingCostLinkAttributeName() { return firstHourParkingCostLinkAttributeName; } - public static String getExtraHourParkingCostLinkAttributeName() { - return extraHourParkingCostLinkAttributeName; - } + public static String getExtraHourParkingCostLinkAttributeName() { return extraHourParkingCostLinkAttributeName; } public static String getMaxDailyParkingCostLinkAttributeName() { return maxDailyParkingCostLinkAttributeName; @@ -77,30 +60,25 @@ public static String getParkingPenaltyAttributeName() { return parkingPenaltyAttributeName; } - public static String getResidentialParkingFeeAttributeName() { - return residentialParkingFeePerDay; - } + public static String getResidentialParkingFeeAttributeName() { return residentialParkingFeePerDay; } public static String getActivityPrefixForDailyParkingCosts() { return activityPrefixForDailyParkingCosts; } - public static Set getActivityPrefixesToBeExcludedFromParkingCost() { - return activityPrefixToBeExcludedFromParkingCost; - } + public static Set getActivityPrefixesToBeExcludedFromParkingCost() { return activityPrefixToBeExcludedFromParkingCost; } - public static void setLinkParkingToNotInResidentialArea(Link link ){ - link.getAttributes().putAttribute( "parking", "notInsideResidentialArea"); + public static void setParkingToRestricted( Link link ){ + link.getAttributes().putAttribute( "parking", "restricted" ); } // yy change the logic of the above to enums - public static void setLinkToResidentialArea( Link link ) { - link.getAttributes().putAttribute("parkingType", ""); - + public static void setParkingToRestricted(Person person) { + person.getAttributes().putAttribute("parkingType", "residentialParking"); } - public static void setPersonParkingType(Person person, PersonParkingType parkingType) { - person.getAttributes().putAttribute("parkingType", parkingType.toString()); + public static void setParkingToNonRestricted(Person person) { + person.getAttributes().putAttribute("parkingType", "nonResidentialParking"); } public static void setLinkAttribute(Link link, String attributeName, double attributeValue) { diff --git a/src/test/java/org/matsim/run/ChessboardParkingTest.java b/src/test/java/org/matsim/run/ChessboardParkingTest.java index 62f6beca..dd491be5 100644 --- a/src/test/java/org/matsim/run/ChessboardParkingTest.java +++ b/src/test/java/org/matsim/run/ChessboardParkingTest.java @@ -196,36 +196,32 @@ static void createExampleParkingPopulation(Population population, Network networ final Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, originLink.getId()); originActivity.setEndTime(3600.); plan.addActivity(originActivity); - plan.addLeg(carLeg); + plan.addLeg(factory.createLeg(TransportMode.car)); plan.addActivity(factory.createActivityFromLinkId(ActivityTypes.LEISURE, destinationLink.getId())); person.addPlan(plan); population.addPerson(person); switch (situation) { -// case normalToNormal -> { -// // do nothing -// } -// case restrictedToNormal -> { -// LeipzigUtils.setParkingToRestricted(originLink); -// } -// -// // yy Ich habe den Setup der Fälle, die hier kommen, leider nicht verstanden. Daher habe ich obigen Fall "restrictedToNormal" neu gebaut. Sorry ... kai, apr'23 -// -// case fromShpFile -> { -//// for( Link link : network.getLinks().values() ){ -//// if ( link is in polygon ) { -//// LeipzigUtils.parkingIsRestricted( link ); -//// } -//// } -// } - - //koennen die 3 oberen Faelle = normalToNormal, restrictedToNormal, fromShpFile nicht jetzt weg? + case normalToNormal -> { + // do nothing + } + case restrictedToNormal -> { + LeipzigUtils.setParkingToRestricted(originLink); + } + // yy Ich habe den Setup der Fälle, die hier kommen, leider nicht verstanden. Daher habe ich obigen Fall "restrictedToNormal" neu gebaut. Sorry ... kai, apr'23 + case fromShpFile -> { +// for( Link link : network.getLinks().values() ){ +// if ( link is in polygon ) { +// LeipzigUtils.parkingIsRestricted( link ); +// } +// } + } case residentInResidentialArea -> { population.getPersons().clear(); Person residentInResidentialArea = factory.createPerson(Id.createPersonId(situation.toString())); - LeipzigUtils.setPersonParkingType(residentInResidentialArea, LeipzigUtils.PersonParkingType.closestToActivity); + LeipzigUtils.setParkingToNonRestricted(residentInResidentialArea); LeipzigUtils.parkingIsRestricted(originLink); residentInResidentialArea.addPlan(plan); population.addPerson(residentInResidentialArea); @@ -233,7 +229,6 @@ static void createExampleParkingPopulation(Population population, Network networ case residentOutsideResidentialArea -> { population.getPersons().clear(); Person residentOutsideResidentialArea = factory.createPerson(Id.createPersonId(situation.toString())); - LeipzigUtils.setPersonParkingType(residentOutsideResidentialArea, LeipzigUtils.PersonParkingType.closestToActivity); residentOutsideResidentialArea.addPlan(plan); population.addPerson(residentOutsideResidentialArea); } @@ -241,16 +236,15 @@ static void createExampleParkingPopulation(Population population, Network networ population.getPersons().clear(); Person nonResidentInResidentialAreaNoShop = factory.createPerson(Id.createPersonId(situation.toString())); nonResidentInResidentialAreaNoShop.addPlan(plan); - LeipzigUtils.setPersonParkingType(nonResidentInResidentialAreaNoShop, LeipzigUtils.PersonParkingType.restrictedForNonResidents); - //wrong, we need to clean this up -// LeipzigUtils.setLinkParkingToNotInResidentialArea(destinationLink); + LeipzigUtils.setParkingToRestricted(nonResidentInResidentialAreaNoShop); + LeipzigUtils.setParkingToRestricted(destinationLink); population.addPerson(nonResidentInResidentialAreaNoShop); } case nonResidentOutsideResidentialArea -> { population.getPersons().clear(); Person nonResidentOutsideResidentialArea = factory.createPerson(Id.createPersonId(situation.toString())); nonResidentOutsideResidentialArea.addPlan(plan); - LeipzigUtils.setPersonParkingType(nonResidentOutsideResidentialArea, LeipzigUtils.PersonParkingType.restrictedForNonResidents); + LeipzigUtils.setParkingToRestricted(nonResidentOutsideResidentialArea); population.addPerson(nonResidentOutsideResidentialArea); } case nonResidentInResidentialAreaShop -> { @@ -263,9 +257,8 @@ static void createExampleParkingPopulation(Population population, Network networ planForShopping.addActivity(factory.createActivityFromLinkId(ActivityTypes.SHOPPING, destinationLink.getId())); population.addPerson(nonResidentInResidentialAreaShop); nonResidentInResidentialAreaShop.addPlan(planForShopping); - LeipzigUtils.setPersonParkingType(nonResidentInResidentialAreaShop, LeipzigUtils.PersonParkingType.restrictedForNonResidents); - //this is wrong!!!!! -sm 090523 -// LeipzigUtils.setParkingToRestricted(destinationLink); + LeipzigUtils.setParkingToRestricted(nonResidentInResidentialAreaShop); + LeipzigUtils.setParkingToRestricted(destinationLink); LeipzigUtils.setParkingToShoppingCenter(linkForShopping); } diff --git a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index 56970f92..033204d9 100644 --- a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -65,9 +65,8 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ } log.warn( "returning LeipzigPlanRouter" ); } - - @Override - public void run( final Plan plan ){ + enum ParkingType{normal, restricted, shopping} + @Override public void run( final Plan plan ){ final List trips = TripStructureUtils.getTrips( plan ); TimeTracker timeTracker = new TimeTracker( timeInterpretation ); @@ -89,17 +88,17 @@ public void run( final Plan plan ){ // At this point, I only want to deal with residential parking. Shopping comes later (and is simpler). - LeipzigUtils.PersonParkingType parkingTypeAtOrigin = getParkingType( fullModalNetwork, oldTrip.getOriginActivity() ); - LeipzigUtils.PersonParkingType parkingTypeAtDestination = getParkingType( fullModalNetwork, oldTrip.getDestinationActivity() ); + ParkingType parkingTypeAtOrigin = getParkingType( fullModalNetwork, oldTrip.getOriginActivity() ); + ParkingType parkingTypeAtDestination = getParkingType( fullModalNetwork, oldTrip.getDestinationActivity() ); - if( parkingTypeAtOrigin == LeipzigUtils.PersonParkingType.closestToActivity && parkingTypeAtDestination == LeipzigUtils.PersonParkingType.closestToActivity ){ + if( parkingTypeAtOrigin == ParkingType.normal && parkingTypeAtDestination == ParkingType.normal ){ // standard case: final List newTripElements = tripRouter.calcRoute( routingMode, fromFacility, toFacility, timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity() ); timeTracker.addElements( newTripElements ); - } else if( parkingTypeAtOrigin == LeipzigUtils.PersonParkingType.restrictedForNonResidents && parkingTypeAtDestination == LeipzigUtils.PersonParkingType.closestToActivity ){ + } else if( parkingTypeAtOrigin == ParkingType.restricted && parkingTypeAtDestination == ParkingType.normal ){ // restricted parking at origin: // first find parking: // final Link parkingLink = NetworkUtils.getNearestLink( reducedNetwork, oldTrip.getOriginActivity().getCoord() ); @@ -129,7 +128,7 @@ public void run( final Plan plan ){ timeTracker.addElements( newTripElements ); - } else if (parkingTypeAtOrigin == LeipzigUtils.PersonParkingType.closestToActivity && parkingTypeAtDestination == LeipzigUtils.PersonParkingType.restrictedForNonResidents) { + } else if (parkingTypeAtOrigin == ParkingType.normal && parkingTypeAtDestination == ParkingType.restricted) { //parking at destination final Link parkingLink = linkChooser.decideOnLink( toFacility, reducedNetwork ); @@ -159,7 +158,7 @@ public void run( final Plan plan ){ TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity() ); timeTracker.addElements(newTripElements); - } else if (parkingTypeAtOrigin == LeipzigUtils.PersonParkingType.closestToActivity && parkingTypeAtDestination == LeipzigUtils.PersonParkingType.shopping) { + } else if (parkingTypeAtOrigin == ParkingType.normal && parkingTypeAtDestination == ParkingType.shopping) { // can this be done better??? i need only the links with shopping garages for( Node node : this.fullModalNetwork.getNodes().values() ){ @@ -217,8 +216,8 @@ public void run( final Plan plan ){ log.warn( "======" ); } - private static LeipzigUtils.PersonParkingType getParkingType(Network fullModalNetwork, Activity originActivity ){ - LeipzigUtils.PersonParkingType parkingTypeAtOrigin = LeipzigUtils.PersonParkingType.closestToActivity; + private static ParkingType getParkingType( Network fullModalNetwork, Activity originActivity ){ + ParkingType parkingTypeAtOrigin = ParkingType.normal; // if we find out that there are time restrictions on all the links //originActivity.getEndTime(); @@ -229,11 +228,11 @@ private static LeipzigUtils.PersonParkingType getParkingType(Network fullModalNe // if non-home activity, check if in restricted zone: Link link = fullModalNetwork.getLinks().get( originActivity.getLinkId() ); if( parkingIsRestricted( link ) ){ - parkingTypeAtOrigin = LeipzigUtils.PersonParkingType.restrictedForNonResidents; + parkingTypeAtOrigin = ParkingType.restricted; if (originActivity.getType().equals(ActivityTypes.SHOPPING)) { // change this to parking type normal/ unrestricted // on activity link, unrestricted - parkingTypeAtOrigin = LeipzigUtils.PersonParkingType.shopping; + parkingTypeAtOrigin = ParkingType.shopping; } } diff --git a/src/test/java/org/matsim/run/prepare/NetworkOptionsTest.java b/src/test/java/org/matsim/run/prepare/NetworkOptionsTest.java index c6ec49c0..5e1abed2 100644 --- a/src/test/java/org/matsim/run/prepare/NetworkOptionsTest.java +++ b/src/test/java/org/matsim/run/prepare/NetworkOptionsTest.java @@ -78,20 +78,21 @@ public void runParkingCostAreaCreationTest() { new CommandLine(options).parseArgs( "--parking-cost-area", shpPath); options.prepare(network); + ParkingCostConfigGroup parkingCostConfigGroup = ConfigUtils.addOrGetModule(new Config(), ParkingCostConfigGroup.class); + //parkingCost values (2.0 and 0.1) are defined in file shpPath Assert.assertEquals(Double.parseDouble(network.getLinks().get(Id.createLinkId("-10424519")) - .getAttributes().getAttribute(LeipzigUtils.getFirstHourParkingCostLinkAttributeName()).toString()),2.0, 0); + .getAttributes().getAttribute(parkingCostConfigGroup.getFirstHourParkingCostLinkAttributeName()).toString()),2.0, 0); Assert.assertEquals(Double.parseDouble(network.getLinks().get(Id.createLinkId("-10424519")) - .getAttributes().getAttribute(LeipzigUtils.getExtraHourParkingCostLinkAttributeName()).toString()),2.0, 0); + .getAttributes().getAttribute(parkingCostConfigGroup.getExtraHourParkingCostLinkAttributeName()).toString()),2.0, 0); Assert.assertEquals(Double.parseDouble(network.getLinks().get(Id.createLinkId("-10424519")) - .getAttributes().getAttribute(LeipzigUtils.getResidentialParkingFeeAttributeName()).toString()),0.1, 0); + .getAttributes().getAttribute(parkingCostConfigGroup.getResidentialParkingFeeAttributeName()).toString()),0.1, 0); Assert.assertEquals(Double.parseDouble(network.getLinks().get(Id.createLinkId("827435967#0")) - .getAttributes().getAttribute(LeipzigUtils.getFirstHourParkingCostLinkAttributeName()).toString()),2.0, 0); + .getAttributes().getAttribute(parkingCostConfigGroup.getFirstHourParkingCostLinkAttributeName()).toString()),2.0, 0); Assert.assertEquals(Double.parseDouble(network.getLinks().get(Id.createLinkId("827435967#0")) - .getAttributes().getAttribute(LeipzigUtils.getExtraHourParkingCostLinkAttributeName()).toString()),2.0, 0); + .getAttributes().getAttribute(parkingCostConfigGroup.getExtraHourParkingCostLinkAttributeName()).toString()),2.0, 0); Assert.assertEquals(Double.parseDouble(network.getLinks().get(Id.createLinkId("827435967#0")) - .getAttributes().getAttribute(LeipzigUtils.getResidentialParkingFeeAttributeName()).toString()),0.1, 0); - + .getAttributes().getAttribute(parkingCostConfigGroup.getResidentialParkingFeeAttributeName()).toString()),0.1, 0); } From bb684f44205bb7f2612887599ca2b9af95bf57a3 Mon Sep 17 00:00:00 2001 From: simei94 Date: Tue, 16 May 2023 18:23:16 +0200 Subject: [PATCH 026/106] make attr names non-editable --- .../org/matsim/run/prepare/LeipzigUtils.java | 37 +++++++++++++++---- .../prepare/ParkingCapacitiesAttacher.java | 3 +- .../matsim/run/prepare/PrepareNetwork.java | 6 +-- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/matsim/run/prepare/LeipzigUtils.java b/src/main/java/org/matsim/run/prepare/LeipzigUtils.java index d2c9beb1..977f284e 100644 --- a/src/main/java/org/matsim/run/prepare/LeipzigUtils.java +++ b/src/main/java/org/matsim/run/prepare/LeipzigUtils.java @@ -2,7 +2,6 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.Person; -import org.matsim.core.utils.collections.CollectionUtils; import java.util.HashSet; import java.util.Set; @@ -38,15 +37,21 @@ public static boolean parkingIsRestricted( Link link ) { } } - public static String getMode() { return mode; } + public static String getMode() { + return mode; + } public static String getDailyParkingCostLinkAttributeName() { return dailyParkingCostLinkAttributeName; } - public static String getFirstHourParkingCostLinkAttributeName() { return firstHourParkingCostLinkAttributeName; } + public static String getFirstHourParkingCostLinkAttributeName() { + return firstHourParkingCostLinkAttributeName; + } - public static String getExtraHourParkingCostLinkAttributeName() { return extraHourParkingCostLinkAttributeName; } + public static String getExtraHourParkingCostLinkAttributeName() { + return extraHourParkingCostLinkAttributeName; + } public static String getMaxDailyParkingCostLinkAttributeName() { return maxDailyParkingCostLinkAttributeName; @@ -60,13 +65,17 @@ public static String getParkingPenaltyAttributeName() { return parkingPenaltyAttributeName; } - public static String getResidentialParkingFeeAttributeName() { return residentialParkingFeePerDay; } + public static String getResidentialParkingFeeAttributeName() { + return residentialParkingFeePerDay; + } public static String getActivityPrefixForDailyParkingCosts() { return activityPrefixForDailyParkingCosts; } - public static Set getActivityPrefixesToBeExcludedFromParkingCost() { return activityPrefixToBeExcludedFromParkingCost; } + public static Set getActivityPrefixesToBeExcludedFromParkingCost() { + return activityPrefixToBeExcludedFromParkingCost; + } public static void setParkingToRestricted( Link link ){ link.getAttributes().putAttribute( "parking", "restricted" ); @@ -81,8 +90,20 @@ public static void setParkingToNonRestricted(Person person) { person.getAttributes().putAttribute("parkingType", "nonResidentialParking"); } - public static void setLinkAttribute(Link link, String attributeName, double attributeValue) { - link.getAttributes().putAttribute(attributeName, attributeValue); + public static void setFirstHourParkingCost(Link link, double parkingCost) { + link.getAttributes().putAttribute(getFirstHourParkingCostLinkAttributeName(), parkingCost); + } + + public static void setExtraHourParkingCost(Link link, double parkingCost) { + link.getAttributes().putAttribute(getExtraHourParkingCostLinkAttributeName(), parkingCost); + } + + public static void setResidentialParkingCost(Link link, double parkingCost) { + link.getAttributes().putAttribute(getResidentialParkingFeeAttributeName(), parkingCost); + } + + public static void setParkingCapacity(Link link, double parkingCapacity) { + link.getAttributes().putAttribute("parkingCapacity", parkingCapacity); } //TODO i don´t like the name for this diff --git a/src/main/java/org/matsim/run/prepare/ParkingCapacitiesAttacher.java b/src/main/java/org/matsim/run/prepare/ParkingCapacitiesAttacher.java index c398fead..50b49152 100644 --- a/src/main/java/org/matsim/run/prepare/ParkingCapacitiesAttacher.java +++ b/src/main/java/org/matsim/run/prepare/ParkingCapacitiesAttacher.java @@ -26,7 +26,6 @@ public final class ParkingCapacitiesAttacher { private static final Logger log = LogManager.getLogger(ParkingCapacitiesAttacher.class); - private String capacityAttributeName = "parkingCapacity"; Network network; private final ShpOptions shp; Path inputParkingCapacities; @@ -73,7 +72,7 @@ public void addParkingInformationToLinks() { if (isInsideParkingArea && linkParkingCapacities.get(link.getId().toString()) != null) { double parkingCapacity = Double.parseDouble(linkParkingCapacities.get(link.getId().toString())); - LeipzigUtils.setLinkAttribute(link, capacityAttributeName, parkingCapacity); + LeipzigUtils.setParkingCapacity(link, parkingCapacity); adaptedLinksCount++; } diff --git a/src/main/java/org/matsim/run/prepare/PrepareNetwork.java b/src/main/java/org/matsim/run/prepare/PrepareNetwork.java index 836aa21b..75268199 100644 --- a/src/main/java/org/matsim/run/prepare/PrepareNetwork.java +++ b/src/main/java/org/matsim/run/prepare/PrepareNetwork.java @@ -188,9 +188,9 @@ static void prepareParkingCost(Network network, ShpOptions parkingCostShape) { } } - LeipzigUtils.setLinkAttribute(link, LeipzigUtils.getFirstHourParkingCostLinkAttributeName(), oneHourPCost); - LeipzigUtils.setLinkAttribute(link, LeipzigUtils.getExtraHourParkingCostLinkAttributeName(), extraHourPCost); - LeipzigUtils.setLinkAttribute(link, LeipzigUtils.getResidentialParkingFeeAttributeName(), resPFee); + LeipzigUtils.setFirstHourParkingCost(link, oneHourPCost); + LeipzigUtils.setExtraHourParkingCost(link, extraHourPCost); + LeipzigUtils.setResidentialParkingCost(link, resPFee); } } From 66120e80c2340866f886051df0e685a969341d74 Mon Sep 17 00:00:00 2001 From: simei94 Date: Sun, 21 May 2023 21:47:02 +0200 Subject: [PATCH 027/106] finish up test case impl: renaming, create missing test cases --- .../run/prepare/AssignParkingAttributes.java | 4 +- .../org/matsim/run/prepare/LeipzigUtils.java | 37 ++- .../org/matsim/run/ChessboardParkingTest.java | 277 +++++++++++------- .../run/LeipzigRouterPlanAlgorithm.java | 226 +++++++------- 4 files changed, 316 insertions(+), 228 deletions(-) diff --git a/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java b/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java index ec935add..e185d657 100644 --- a/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java +++ b/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java @@ -31,9 +31,9 @@ static void addParkingAttributesToPopulation(Population population, ShpOptions s isInsideRestrictedParkingArea = MGC.coord2Point(activity.getCoord()).within(restrictedParkingArea); if (isInsideRestrictedParkingArea) { - LeipzigUtils.setParkingToRestricted(person); +// LeipzigUtils.setParkingToRestricted(person); } else { - LeipzigUtils.setParkingToNonRestricted(person); +// LeipzigUtils.setParkingToNonRestricted(person); } break; diff --git a/src/main/java/org/matsim/run/prepare/LeipzigUtils.java b/src/main/java/org/matsim/run/prepare/LeipzigUtils.java index 977f284e..4326a7c9 100644 --- a/src/main/java/org/matsim/run/prepare/LeipzigUtils.java +++ b/src/main/java/org/matsim/run/prepare/LeipzigUtils.java @@ -26,10 +26,20 @@ public final class LeipzigUtils{ private LeipzigUtils(){} /** - * Check of parking on link is restricted or not. + * Defines the parkingBehaviour of a person. */ - public static boolean parkingIsRestricted( Link link ) { - String result = (String) link.getAttributes().getAttribute( "parking" ); + public enum PersonParkingBehaviour {defaultLogic, parkingSearchLogicLeipzig, @Deprecated shopping} + + /** + * Defines the parkingType of a network link. + */ + private enum LinkParkingType{linkOutsideResidentialArea, linkInResidentialArea} + + /** + * Check if link is inside residential area or not (parking on link is restricted or not). + */ + public static boolean isLinkParkingTypeInsideResidentialArea(Link link ) { + String result = (String) link.getAttributes().getAttribute( "linkParkingType" ); if ( result == null ) { return false ; } else { @@ -77,18 +87,17 @@ public static Set getActivityPrefixesToBeExcludedFromParkingCost() { return activityPrefixToBeExcludedFromParkingCost; } - public static void setParkingToRestricted( Link link ){ - link.getAttributes().putAttribute( "parking", "restricted" ); - } - // yy change the logic of the above to enums - - public static void setParkingToRestricted(Person person) { - person.getAttributes().putAttribute("parkingType", "residentialParking"); + public static void setLinkParkingTypeToInsideResidentialArea(Link link ){ + link.getAttributes().putAttribute( "linkParkingType", LinkParkingType.linkInResidentialArea.toString() ); } - public static void setParkingToNonRestricted(Person person) { - person.getAttributes().putAttribute("parkingType", "nonResidentialParking"); - } +// public static void setParkingToRestricted(Person person) { +// person.getAttributes().putAttribute("parkingType", "residentialParking"); +// } +// +// public static void setParkingToNonRestricted(Person person) { +// person.getAttributes().putAttribute("parkingType", "nonResidentialParking"); +// } public static void setFirstHourParkingCost(Link link, double parkingCost) { link.getAttributes().putAttribute(getFirstHourParkingCostLinkAttributeName(), parkingCost); @@ -106,8 +115,6 @@ public static void setParkingCapacity(Link link, double parkingCapacity) { link.getAttributes().putAttribute("parkingCapacity", parkingCapacity); } - //TODO i don´t like the name for this - //Bbetter? public static void setParkingToShoppingCenter(Link link) { link.getAttributes().putAttribute("parkingForShopping", "shoppingCenter"); } diff --git a/src/test/java/org/matsim/run/ChessboardParkingTest.java b/src/test/java/org/matsim/run/ChessboardParkingTest.java index dd491be5..8f3b1b4c 100644 --- a/src/test/java/org/matsim/run/ChessboardParkingTest.java +++ b/src/test/java/org/matsim/run/ChessboardParkingTest.java @@ -51,16 +51,35 @@ public class ChessboardParkingTest { private static final Logger log = LogManager.getLogger(ChessboardParkingTest.class); @Rule public MatsimTestUtils utils = new MatsimTestUtils(); - private static final String HOME_ZONE_ID = "homeLinkId"; +// private static final String HOME_ZONE_ID = "homeLinkId"; final String RE_ROUTE_LEIPZIG = "ReRouteLeipzig"; enum Situation { - residentInResidentialArea, residentOutsideResidentialArea, nonResidentInResidentialAreaNoShop, - nonResidentInResidentialAreaShop, nonResidentOutsideResidentialArea, restrictedToNormal, normalToNormal, fromShpFile + //1) defaultLogic to defaultLogic + //1.1) defaultLogic linkInResidentialArea (=home act) to defaultLogic linkOutsideResidentialArea + defaultLogicLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea, + //1.2) defaultLogic linkOutsideResidentialArea to defaultLogic linkInResidentialArea (=home act) + defaultLogicLinkOutsideResidentialAreaToDefaultLogicLinkInResidentialArea, + //1.3) defaultLogic linkOutsideResidentialArea to defaultLogic linkOutsideResidentialArea + defaultLogicLinkOutsideResidentialAreaToDefaultLogicLinkOutsideResidentialArea, + + //2) parkingSearchLogicLeipzig to defaultLogic + //2.1) parkingSearchLogicLeipzig linkInResidentialArea (!=home act) to defaultLogic linkOutsideResidentialArea + parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea, + //2.2) parkingSearchLogicLeipzig linkInResidentialArea (!=home act) to defaultLogic linkInResidentialArea (=home act) + parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkInResidentialArea, + + //3) defaultLogic to parkingSearchLogicLeipzig + //3.1) defaultLogic linkOutsideResidentialArea to parkingSearchLogicLeipzig linkInResidentialArea (!=home act) + defaultLogicLinkOutsideResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea, + //3.2) defaultLogic linkInResidentialArea (=home act) to parkingSearchLogicLeipzig linkInResidentialArea (!=home act) + defaultLogicLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea, + + //4) parkingSearchLogicLeipzig to parkingSearchLogicLeipzig + //4.1) parkingSearchLogicLeipzig linkInResidentialArea (!=home act) to parkingSearchLogicLeipzig linkInResidentialArea (!=home act) + parkingSearchLogicLeipzigLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea } - // yyyyyy Bitte auch Tests, wo diese Unterscheidungen am Ziel stattfinden. Danke! kai, apr'23 - @Test public final void runChessboardParkingTest1() { @@ -69,7 +88,7 @@ public final void runChessboardParkingTest1() { for (Situation situation : Situation.values()) { Config config = ConfigUtils.loadConfig(url); config.controler().setLastIteration(1); - config.controler().setOutputDirectory(utils.getOutputDirectory()); + config.controler().setOutputDirectory(utils.getOutputDirectory() + "/" + situation.toString()); config.global().setNumberOfThreads(0); config.qsim().setNumberOfThreads(1); config.controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); @@ -97,11 +116,7 @@ public final void runChessboardParkingTest1() { log.warn("population size=" + scenario.getPopulation().getPersons().size() +" for case" + situation); // System.exit(-1); - -// NetworkOptions networkOptions = new NetworkOptions(); -// // help // yy what does the "help" mean here? -// networkOptions.prepare(scenario.getNetwork()); - // yy I don't know what the above is supposed to do. Thus commented it out. kai, apr'23 + //why ist the above here? Doesn't it end the test / sim so it is of no use here?! -sme0523 Controler controler = new Controler(scenario); controler.addOverridingModule(new AbstractModule() { @@ -113,16 +128,16 @@ public void install() { } }); - TestParkingListener handler = new TestParkingListener(); + TestParkingListener testParkingListener = new TestParkingListener(); controler.addOverridingModule(new AbstractModule() { @Override public void install() { - addEventHandlerBinding().toInstance(handler); + addEventHandlerBinding().toInstance(testParkingListener); } }); controler.run(); - getAssertions(situation, handler); + getAssertions(situation, testParkingListener); } @@ -189,85 +204,114 @@ static void createExampleParkingPopulation(Population population, Network networ Leg carLeg = factory.createLeg(TransportMode.car); Link originLink = network.getLinks().get(Id.createLinkId("80")); Link destinationLink = network.getLinks().get(Id.createLinkId("81")); - Link linkForShopping = network.getLinks().get(Id.createLinkId("86")); Person person = factory.createPerson(Id.createPersonId(situation.toString())); Plan plan = factory.createPlan(); - final Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, originLink.getId()); - originActivity.setEndTime(3600.); - plan.addActivity(originActivity); - plan.addLeg(factory.createLeg(TransportMode.car)); - plan.addActivity(factory.createActivityFromLinkId(ActivityTypes.LEISURE, destinationLink.getId())); - person.addPlan(plan); - population.addPerson(person); switch (situation) { - case normalToNormal -> { - // do nothing - } - case restrictedToNormal -> { - LeipzigUtils.setParkingToRestricted(originLink); + //1.1 + case defaultLogicLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea -> { + Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.HOME, originLink.getId()); + Activity destinationActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, destinationLink.getId()); + originActivity.setEndTime(3600.); + + LeipzigUtils.setLinkParkingTypeToInsideResidentialArea(originLink); + + plan.addActivity(originActivity); + plan.addLeg(carLeg); + plan.addActivity(destinationActivity); } + //1.2 + case defaultLogicLinkOutsideResidentialAreaToDefaultLogicLinkInResidentialArea -> { + Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, originLink.getId()); + Activity destinationActivity = factory.createActivityFromLinkId(ActivityTypes.HOME, destinationLink.getId()); + originActivity.setEndTime(3600.); - // yy Ich habe den Setup der Fälle, die hier kommen, leider nicht verstanden. Daher habe ich obigen Fall "restrictedToNormal" neu gebaut. Sorry ... kai, apr'23 + LeipzigUtils.setLinkParkingTypeToInsideResidentialArea(destinationLink); - case fromShpFile -> { -// for( Link link : network.getLinks().values() ){ -// if ( link is in polygon ) { -// LeipzigUtils.parkingIsRestricted( link ); -// } -// } + plan.addActivity(originActivity); + plan.addLeg(carLeg); + plan.addActivity(destinationActivity); } - case residentInResidentialArea -> { - population.getPersons().clear(); - Person residentInResidentialArea = factory.createPerson(Id.createPersonId(situation.toString())); - LeipzigUtils.setParkingToNonRestricted(residentInResidentialArea); - LeipzigUtils.parkingIsRestricted(originLink); - residentInResidentialArea.addPlan(plan); - population.addPerson(residentInResidentialArea); + //1.3 + case defaultLogicLinkOutsideResidentialAreaToDefaultLogicLinkOutsideResidentialArea -> { + Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, originLink.getId()); + Activity destinationActivity = factory.createActivityFromLinkId(ActivityTypes.WORK, destinationLink.getId()); + originActivity.setEndTime(3600.); + + plan.addActivity(originActivity); + plan.addLeg(carLeg); + plan.addActivity(destinationActivity); } - case residentOutsideResidentialArea -> { - population.getPersons().clear(); - Person residentOutsideResidentialArea = factory.createPerson(Id.createPersonId(situation.toString())); - residentOutsideResidentialArea.addPlan(plan); - population.addPerson(residentOutsideResidentialArea); + //2.1 + case parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea -> { + Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, originLink.getId()); + Activity destinationActivity = factory.createActivityFromLinkId(ActivityTypes.HOME, destinationLink.getId()); + originActivity.setEndTime(3600.); + + LeipzigUtils.setLinkParkingTypeToInsideResidentialArea(originLink); + + plan.addActivity(originActivity); + plan.addLeg(carLeg); + plan.addActivity(destinationActivity); } - case nonResidentInResidentialAreaNoShop -> { - population.getPersons().clear(); - Person nonResidentInResidentialAreaNoShop = factory.createPerson(Id.createPersonId(situation.toString())); - nonResidentInResidentialAreaNoShop.addPlan(plan); - LeipzigUtils.setParkingToRestricted(nonResidentInResidentialAreaNoShop); - LeipzigUtils.setParkingToRestricted(destinationLink); - population.addPerson(nonResidentInResidentialAreaNoShop); + //2.2 + case parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkInResidentialArea -> { + Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, originLink.getId()); + Activity destinationActivity = factory.createActivityFromLinkId(ActivityTypes.HOME, destinationLink.getId()); + originActivity.setEndTime(3600.); + + LeipzigUtils.setLinkParkingTypeToInsideResidentialArea(originLink); + LeipzigUtils.setLinkParkingTypeToInsideResidentialArea(destinationLink); + + plan.addActivity(originActivity); + plan.addLeg(carLeg); + plan.addActivity(destinationActivity); } - case nonResidentOutsideResidentialArea -> { - population.getPersons().clear(); - Person nonResidentOutsideResidentialArea = factory.createPerson(Id.createPersonId(situation.toString())); - nonResidentOutsideResidentialArea.addPlan(plan); - LeipzigUtils.setParkingToRestricted(nonResidentOutsideResidentialArea); - population.addPerson(nonResidentOutsideResidentialArea); + //3.1 + case defaultLogicLinkOutsideResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea -> { + Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, originLink.getId()); + Activity destinationActivity = factory.createActivityFromLinkId(ActivityTypes.WORK, destinationLink.getId()); + originActivity.setEndTime(3600.); + + LeipzigUtils.setLinkParkingTypeToInsideResidentialArea(destinationLink); + + plan.addActivity(originActivity); + plan.addLeg(carLeg); + plan.addActivity(destinationActivity); } - case nonResidentInResidentialAreaShop -> { - population.getPersons().clear(); - Person nonResidentInResidentialAreaShop = factory.createPerson(Id.createPersonId(situation.toString())); + //3.2 + case defaultLogicLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea -> { + Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.HOME, originLink.getId()); + Activity destinationActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, destinationLink.getId()); originActivity.setEndTime(3600.); - Plan planForShopping = factory.createPlan(); - planForShopping.addActivity(originActivity); - planForShopping.addLeg(factory.createLeg(TransportMode.car)); - planForShopping.addActivity(factory.createActivityFromLinkId(ActivityTypes.SHOPPING, destinationLink.getId())); - population.addPerson(nonResidentInResidentialAreaShop); - nonResidentInResidentialAreaShop.addPlan(planForShopping); - LeipzigUtils.setParkingToRestricted(nonResidentInResidentialAreaShop); - LeipzigUtils.setParkingToRestricted(destinationLink); - LeipzigUtils.setParkingToShoppingCenter(linkForShopping); + + LeipzigUtils.setLinkParkingTypeToInsideResidentialArea(originLink); + LeipzigUtils.setLinkParkingTypeToInsideResidentialArea(destinationLink); + + plan.addActivity(originActivity); + plan.addLeg(carLeg); + plan.addActivity(destinationActivity); } + //4.1 + case parkingSearchLogicLeipzigLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea -> { + Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, originLink.getId()); + Activity destinationActivity = factory.createActivityFromLinkId(ActivityTypes.WORK, destinationLink.getId()); + originActivity.setEndTime(3600.); - default -> throw new IllegalStateException("Unexpected value: " + situation); - } + LeipzigUtils.setLinkParkingTypeToInsideResidentialArea(originLink); + LeipzigUtils.setLinkParkingTypeToInsideResidentialArea(destinationLink); + plan.addActivity(originActivity); + plan.addLeg(carLeg); + plan.addActivity(destinationActivity); + } - //residential area is maximum including the following edges (square): 124-126, 34-36, 178-180, 88-90 + default -> throw new IllegalStateException("Unexpected value: " + situation); + } + person.addPlan(plan); + population.addPerson(person); } class TestParkingListener implements ActivityStartEventHandler { @@ -279,7 +323,7 @@ public void handleEvent(ActivityStartEvent activityStartEvent) { if (activityStartEvent.getActType().equals("parking interaction")) { if (!parkingActivities.containsKey(activityStartEvent.getPersonId())) { parkingActivities.put(activityStartEvent.getPersonId(), new ArrayList<>(Arrays.asList(activityStartEvent))); - } else parkingActivities.get(activityStartEvent).add(activityStartEvent); + } else parkingActivities.get(activityStartEvent.getPersonId()).add(activityStartEvent); } } @@ -289,41 +333,72 @@ public void reset(int iteration) { } } - private void getAssertions(Situation situation, TestParkingListener handler) { + private void getAssertions(Situation situation, TestParkingListener listener) { switch (situation) { - case normalToNormal -> { + //1.1 + case defaultLogicLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea -> { + Assert.assertFalse(listener.parkingActivities.containsKey(Id.createPersonId(String.valueOf( + Situation.defaultLogicLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea)))); } - - case restrictedToNormal -> { - Assert.assertTrue(handler.parkingActivities.containsKey(Id.createPersonId(String.valueOf(Situation.restrictedToNormal)))); - Assert.assertEquals("wrong number of parking activites!", 1, handler.parkingActivities.get(Id.createPersonId(String.valueOf(Situation.restrictedToNormal))).size()); - Assert.assertEquals("wrong link", Id.createLinkId("169"), handler.parkingActivities.get(Id.createPersonId(String.valueOf(Situation.restrictedToNormal))).get(0).getLinkId()); + //1.2 + case defaultLogicLinkOutsideResidentialAreaToDefaultLogicLinkInResidentialArea -> { + Assert.assertFalse(listener.parkingActivities.containsKey(Id.createPersonId(String.valueOf( + Situation.defaultLogicLinkOutsideResidentialAreaToDefaultLogicLinkInResidentialArea)))); } - - case residentInResidentialArea -> { - Assert.assertTrue(!handler.parkingActivities.containsKey(Id.createPersonId(String.valueOf(Situation.residentInResidentialArea)))); + //1.3 + case defaultLogicLinkOutsideResidentialAreaToDefaultLogicLinkOutsideResidentialArea -> { + Assert.assertFalse(listener.parkingActivities.containsKey(Id.createPersonId(String.valueOf( + Situation.defaultLogicLinkOutsideResidentialAreaToDefaultLogicLinkOutsideResidentialArea)))); } - - case residentOutsideResidentialArea -> { - Assert.assertTrue(!handler.parkingActivities.containsKey(Id.createPersonId(String.valueOf(Situation.residentOutsideResidentialArea)))); + //2.1 + case parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea -> { + Assert.assertTrue(listener.parkingActivities.containsKey(Id.createPersonId(String.valueOf + (Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea)))); + Assert.assertEquals("wrong number of parking activites!", 1, listener.parkingActivities.get(Id.createPersonId(String.valueOf( + Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea))).size()); + Assert.assertEquals("wrong origin parking link", Id.createLinkId("169"), listener.parkingActivities.get(Id.createPersonId(String.valueOf( + Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea))).get(0).getLinkId()); } - - case nonResidentInResidentialAreaNoShop -> { - Assert.assertTrue(handler.parkingActivities.containsKey(Id.createPersonId(String.valueOf(Situation.nonResidentInResidentialAreaNoShop)))); - Assert.assertEquals("wrong number of parking activites!", 1, handler.parkingActivities.get(Id.createPersonId(String.valueOf(Situation.nonResidentInResidentialAreaNoShop))).size()); - Assert.assertNotEquals("wrong link", Id.createLinkId("81"), handler.parkingActivities.get(Id.createPersonId(String.valueOf(Situation.nonResidentInResidentialAreaNoShop))).get(0).getLinkId()); + //2.2 + case parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkInResidentialArea -> { + Assert.assertTrue(listener.parkingActivities.containsKey(Id.createPersonId(String.valueOf + (Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkInResidentialArea)))); + Assert.assertEquals("wrong number of parking activites!", 1, listener.parkingActivities.get(Id.createPersonId(String.valueOf( + Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkInResidentialArea))).size()); + Assert.assertEquals("wrong origin parking link", Id.createLinkId("169"), listener.parkingActivities.get(Id.createPersonId(String.valueOf( + Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkInResidentialArea))).get(0).getLinkId()); } - - case nonResidentInResidentialAreaShop -> { - Assert.assertTrue(handler.parkingActivities.containsKey(Id.createPersonId(String.valueOf(Situation.nonResidentInResidentialAreaShop)))); - Assert.assertEquals("wrong link", Id.createLinkId("86"),handler.parkingActivities.get(Id.createPersonId(String.valueOf(Situation.nonResidentInResidentialAreaShop))).get(0).getLinkId()); + //3.1 + case defaultLogicLinkOutsideResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea -> { + Assert.assertTrue(listener.parkingActivities.containsKey(Id.createPersonId(String.valueOf + (Situation.defaultLogicLinkOutsideResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea)))); + Assert.assertEquals("wrong number of parking activites!", 1, listener.parkingActivities.get(Id.createPersonId(String.valueOf( + Situation.defaultLogicLinkOutsideResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea))).size()); + Assert.assertEquals("wrong destination parking link", Id.createLinkId("133"), listener.parkingActivities.get(Id.createPersonId(String.valueOf( + Situation.defaultLogicLinkOutsideResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea))).get(0).getLinkId()); + } + //3.2 + case defaultLogicLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea -> { + Assert.assertTrue(listener.parkingActivities.containsKey(Id.createPersonId(String.valueOf + (Situation.defaultLogicLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea)))); + Assert.assertEquals("wrong number of parking activites!", 1, listener.parkingActivities.get(Id.createPersonId(String.valueOf( + Situation.defaultLogicLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea))).size()); + Assert.assertEquals("wrong destination parking link", Id.createLinkId("133"), listener.parkingActivities.get(Id.createPersonId(String.valueOf( + Situation.defaultLogicLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea))).get(0).getLinkId()); + } + //4.1 + case parkingSearchLogicLeipzigLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea -> { + Assert.assertTrue(listener.parkingActivities.containsKey(Id.createPersonId(String.valueOf + (Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea)))); + Assert.assertEquals("wrong number of parking activites!", 2, listener.parkingActivities.get(Id.createPersonId(String.valueOf( + Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea))).size()); + Assert.assertEquals("wrong origin parking link", Id.createLinkId("169"), listener.parkingActivities.get(Id.createPersonId(String.valueOf( + Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea))).get(0).getLinkId()); + Assert.assertEquals("wrong destination parking link", Id.createLinkId("133"), listener.parkingActivities.get(Id.createPersonId(String.valueOf( + Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea))).get(1).getLinkId()); } - - - } - } } diff --git a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index 033204d9..6ba9e9b6 100644 --- a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -29,8 +29,7 @@ import java.util.List; import static org.matsim.core.router.PlanRouter.putVehicleFromOldTripIntoNewTripIfMeaningful; -import static org.matsim.run.prepare.LeipzigUtils.parkingAllowedForShopping; -import static org.matsim.run.prepare.LeipzigUtils.parkingIsRestricted; +import static org.matsim.run.prepare.LeipzigUtils.isLinkParkingTypeInsideResidentialArea; final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ private static final Logger log = LogManager.getLogger(LeipzigRouterPlanAlgorithm.class ); @@ -59,13 +58,13 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ reducedNetwork.addNode( node ); } for( Link link : this.fullModalNetwork.getLinks().values() ){ - if( !LeipzigUtils.parkingIsRestricted( link ) ){ + if( !LeipzigUtils.isLinkParkingTypeInsideResidentialArea( link ) ){ reducedNetwork.addLink( link ); } } log.warn( "returning LeipzigPlanRouter" ); } - enum ParkingType{normal, restricted, shopping} + @Override public void run( final Plan plan ){ final List trips = TripStructureUtils.getTrips( plan ); TimeTracker timeTracker = new TimeTracker( timeInterpretation ); @@ -88,122 +87,58 @@ enum ParkingType{normal, restricted, shopping} // At this point, I only want to deal with residential parking. Shopping comes later (and is simpler). - ParkingType parkingTypeAtOrigin = getParkingType( fullModalNetwork, oldTrip.getOriginActivity() ); - ParkingType parkingTypeAtDestination = getParkingType( fullModalNetwork, oldTrip.getDestinationActivity() ); + LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtOrigin = getParkingBehaviour( fullModalNetwork, oldTrip.getOriginActivity() ); + LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtDestination = getParkingBehaviour( fullModalNetwork, oldTrip.getDestinationActivity() ); - if( parkingTypeAtOrigin == ParkingType.normal && parkingTypeAtDestination == ParkingType.normal ){ + if ( parkingBehaviourAtOrigin == LeipzigUtils.PersonParkingBehaviour.defaultLogic + && parkingBehaviourAtDestination == LeipzigUtils.PersonParkingBehaviour.defaultLogic){ // standard case: final List newTripElements = tripRouter.calcRoute( routingMode, fromFacility, toFacility, timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity() ); timeTracker.addElements( newTripElements ); - } else if( parkingTypeAtOrigin == ParkingType.restricted && parkingTypeAtDestination == ParkingType.normal ){ - // restricted parking at origin: - // first find parking: -// final Link parkingLink = NetworkUtils.getNearestLink( reducedNetwork, oldTrip.getOriginActivity().getCoord() ); - final Link parkingLink = linkChooser.decideOnLink( fromFacility, reducedNetwork ); - - final Activity parkingActivity = scenario.getPopulation().getFactory().createInteractionActivityFromLinkId( - TripStructureUtils.createStageActivityType( "parking" ), parkingLink.getId() ); - final Facility parkingFacility = FacilitiesUtils.toFacility( parkingActivity, facilities ); - List newTripElements = new ArrayList<>(); - // trip from origin to parking: - final List walkTripElements = tripRouter.calcRoute( TransportMode.walk, fromFacility, parkingFacility, - timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); - for( PlanElement tripElement : walkTripElements ){ - if ( tripElement instanceof Leg ) { - TripStructureUtils.setRoutingMode( (Leg) tripElement, TransportMode.car ); - } - } - newTripElements.addAll( walkTripElements ); - // parking interaction: - newTripElements.add( parkingActivity ); - // trip from parking to final destination: - final List carTripElements = tripRouter.calcRoute( routingMode, parkingFacility, toFacility, - timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); - newTripElements.addAll( carTripElements ); - putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); - TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity() ); - timeTracker.addElements( newTripElements ); + } else if ( parkingBehaviourAtOrigin == LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig + && parkingBehaviourAtDestination == LeipzigUtils.PersonParkingBehaviour.defaultLogic){ - } else if (parkingTypeAtOrigin == ParkingType.normal && parkingTypeAtDestination == ParkingType.restricted) { + TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), + createTripForParkingAtOrigin(oldTrip, routingMode, fromFacility, toFacility, plan, timeTracker), oldTrip.getDestinationActivity() ); - //parking at destination - final Link parkingLink = linkChooser.decideOnLink( toFacility, reducedNetwork ); - final Activity parkingActivity = scenario.getPopulation().getFactory().createInteractionActivityFromLinkId( - TripStructureUtils.createStageActivityType( "parking" ), parkingLink.getId() ); - final Facility parkingFacility = FacilitiesUtils.toFacility( parkingActivity, facilities ); - List newTripElements = new ArrayList<>(); + } else if (parkingBehaviourAtOrigin == LeipzigUtils.PersonParkingBehaviour.defaultLogic + && parkingBehaviourAtDestination == LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig) { - // trip from origin to parking: - final List carTripElements = tripRouter.calcRoute( routingMode, fromFacility, parkingFacility, - timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); - // parking interaction: + TripRouter.insertTrip(plan, oldTrip.getOriginActivity(), + createTripForParkingAtDestination(oldTrip, routingMode, fromFacility, toFacility, plan, timeTracker), oldTrip.getDestinationActivity()); - newTripElements.addAll(carTripElements ); - newTripElements.add( parkingActivity ); - // trip from parking to destination: - final List walkTripElements = tripRouter.calcRoute( TransportMode.walk, parkingFacility, toFacility, - timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); - for( PlanElement tripElement : walkTripElements ){ - if ( tripElement instanceof Leg ) { - TripStructureUtils.setRoutingMode( (Leg) tripElement, TransportMode.car ); - } - } - newTripElements.addAll(walkTripElements); - putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); - TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity() ); - timeTracker.addElements(newTripElements); - - } else if (parkingTypeAtOrigin == ParkingType.normal && parkingTypeAtDestination == ParkingType.shopping) { - - // can this be done better??? i need only the links with shopping garages - for( Node node : this.fullModalNetwork.getNodes().values() ){ - networkForShopping.addNode( node ); - } + } else if (parkingBehaviourAtOrigin == LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig + && parkingBehaviourAtDestination == LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig){ - for (Link l: this.fullModalNetwork.getLinks().values()) { - System.out.println(l.getAttributes().toString()); - if (parkingAllowedForShopping(l) == true) { - networkForShopping.addLink(l); - } - } + List parkingAtOriginTrip = createTripForParkingAtOrigin(oldTrip, routingMode, fromFacility, toFacility, plan, timeTracker); - //why is this returning the wrong link - final Link parkingLink = linkChooser.decideOnLink( toFacility, networkForShopping ); + //throw away last leg of the above trip because we need to route to dest parking first + PlanElement removedElement = parkingAtOriginTrip.remove(parkingAtOriginTrip.size() -1); + //timeTracker receives the now deleted leg as an element to track (in the createTripForParkingAtOrigin method) + // it seems like we cannot remove it anymore. Is this a problem? -sme0523 - final Activity parkingActivity = scenario.getPopulation().getFactory().createInteractionActivityFromLinkId( - TripStructureUtils.createStageActivityType( "parking" ), parkingLink.getId() ); - final Facility parkingFacility = FacilitiesUtils.toFacility( parkingActivity, facilities ); - List newTripElements = new ArrayList<>(); + Activity parkingInteraction = null; - // trip from origin to parking: - final List carTripElements = tripRouter.calcRoute( routingMode, fromFacility, parkingFacility, - timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); - // parking interaction: + if (parkingAtOriginTrip.get(parkingAtOriginTrip.size() - 1) instanceof Activity) { + parkingInteraction = (Activity) parkingAtOriginTrip.get(parkingAtOriginTrip.size() - 1); + } else { + throw new RuntimeException(); + } - newTripElements.addAll(carTripElements ); - newTripElements.add( parkingActivity ); + Facility fromFacilityParking = FacilitiesUtils.toFacility( parkingInteraction, facilities ); - // trip from parking to destination: - final List walkTripElements = tripRouter.calcRoute( TransportMode.walk, parkingFacility, toFacility, - timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); - for( PlanElement tripElement : walkTripElements ){ - if ( tripElement instanceof Leg ) { - TripStructureUtils.setRoutingMode( (Leg) tripElement, TransportMode.car ); - } - } - newTripElements.addAll(walkTripElements); - putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); - TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity() ); - timeTracker.addElements(newTripElements); + List parkingAtDestinationTrip = createTripForParkingAtDestination(oldTrip, routingMode, fromFacilityParking, toFacility, plan, timeTracker); + List parkingAtDestinationTrip2 = createTripForParkingAtDestination(oldTrip, routingMode, fromFacilityParking, toFacility, plan, timeTracker); + parkingAtOriginTrip.addAll(parkingAtDestinationTrip); - } - else{ + TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), parkingAtOriginTrip, oldTrip.getDestinationActivity() ); + } else { throw new RuntimeException(); // to be implemented! } @@ -216,28 +151,99 @@ enum ParkingType{normal, restricted, shopping} log.warn( "======" ); } - private static ParkingType getParkingType( Network fullModalNetwork, Activity originActivity ){ - ParkingType parkingTypeAtOrigin = ParkingType.normal; + + private static LeipzigUtils.PersonParkingBehaviour getParkingBehaviour(Network fullModalNetwork, Activity originActivity ){ + LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.defaultLogic; // if we find out that there are time restrictions on all the links //originActivity.getEndTime(); + // an dieser stelle waere es besser abzufragen, ob die person in der naehe wohnt anstatt nur die home act -> residential parking zuzuordnen // check if non-home activity (since otherwise we assume that there is no parking restriction): if( !originActivity.getType().equals( ActivityTypes.HOME ) ){ - // if non-home activity, check if in restricted zone: + // if non-home activity, check if in residential parking area: Link link = fullModalNetwork.getLinks().get( originActivity.getLinkId() ); - if( parkingIsRestricted( link ) ){ - parkingTypeAtOrigin = ParkingType.restricted; - if (originActivity.getType().equals(ActivityTypes.SHOPPING)) { - // change this to parking type normal/ unrestricted - // on activity link, unrestricted - parkingTypeAtOrigin = ParkingType.shopping; - } + if( isLinkParkingTypeInsideResidentialArea( link ) ){ + parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig; +// if (originActivity.getType().equals(ActivityTypes.SHOPPING)) { +// // change this to parking type normal/ unrestricted +// // on activity link, unrestricted +// parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.shopping; +// } } } - return parkingTypeAtOrigin; + return parkingBehaviourAtOrigin; + } + + private List createTripForParkingAtOrigin(TripStructureUtils.Trip oldTrip, String routingMode, Facility fromFacility, Facility toFacility, Plan plan, TimeTracker timeTracker) { + List newTripElements = new ArrayList<>(); + + // restricted parking at origin: + // first find parking: +// final Link parkingLink = NetworkUtils.getNearestLink( reducedNetwork, oldTrip.getOriginActivity().getCoord() ); + final Link parkingLink = linkChooser.decideOnLink( fromFacility, reducedNetwork ); + + final Activity parkingActivity = scenario.getPopulation().getFactory().createInteractionActivityFromLinkId( + TripStructureUtils.createStageActivityType( "parking" ), parkingLink.getId() ); + final Facility parkingFacility = FacilitiesUtils.toFacility( parkingActivity, facilities ); + + // trip from origin to parking: + final List walkTripElements = tripRouter.calcRoute( TransportMode.walk, fromFacility, parkingFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); + for( PlanElement tripElement : walkTripElements ){ + if ( tripElement instanceof Leg ) { + TripStructureUtils.setRoutingMode( (Leg) tripElement, TransportMode.car ); + } + } + + newTripElements.addAll( walkTripElements ); + // parking interaction: + newTripElements.add( parkingActivity ); + // trip from parking to final destination: + final List carTripElements = tripRouter.calcRoute( routingMode, parkingFacility, toFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); + newTripElements.addAll( carTripElements ); + + putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); + timeTracker.addElements( newTripElements ); + + return newTripElements; + } + + private List createTripForParkingAtDestination(TripStructureUtils.Trip oldTrip, String routingMode, Facility fromFacility, Facility toFacility, Plan plan, TimeTracker timeTracker) { + List newTripElements = new ArrayList<>(); + + //parking at destination + final Link parkingLink = linkChooser.decideOnLink(toFacility, reducedNetwork); + final Activity parkingActivity = scenario.getPopulation().getFactory().createInteractionActivityFromLinkId( + TripStructureUtils.createStageActivityType("parking"), parkingLink.getId()); + final Facility parkingFacility = FacilitiesUtils.toFacility(parkingActivity, facilities); + + // trip from origin to parking: + final List carTripElements = tripRouter.calcRoute(routingMode, fromFacility, parkingFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes()); + // parking interaction: + + newTripElements.addAll(carTripElements); + newTripElements.add(parkingActivity); + + // trip from parking to destination: + final List walkTripElements = tripRouter.calcRoute(TransportMode.walk, parkingFacility, toFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes()); + for (PlanElement tripElement : walkTripElements) { + if (tripElement instanceof Leg) { + TripStructureUtils.setRoutingMode((Leg) tripElement, TransportMode.car); + } + } + newTripElements.addAll(walkTripElements); + + putVehicleFromOldTripIntoNewTripIfMeaningful(oldTrip, newTripElements); + + timeTracker.addElements(newTripElements); + + return newTripElements; } From c7ad2291465ab385bdb31bd14dcd16284e4bb7db Mon Sep 17 00:00:00 2001 From: simei94 Date: Mon, 22 May 2023 16:54:57 +0200 Subject: [PATCH 028/106] corrections on case parkingLogicLpz to parkingLogicLpz such that there are no doubled car trips anymore --- .../run/LeipzigRouterPlanAlgorithm.java | 60 ++++++++++++++----- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index 6ba9e9b6..3f264522 100644 --- a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -115,29 +115,59 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ } else if (parkingBehaviourAtOrigin == LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig && parkingBehaviourAtDestination == LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig){ - List parkingAtOriginTrip = createTripForParkingAtOrigin(oldTrip, routingMode, fromFacility, toFacility, plan, timeTracker); + List newTripElements = new ArrayList<>(); - //throw away last leg of the above trip because we need to route to dest parking first - PlanElement removedElement = parkingAtOriginTrip.remove(parkingAtOriginTrip.size() -1); - //timeTracker receives the now deleted leg as an element to track (in the createTripForParkingAtOrigin method) - // it seems like we cannot remove it anymore. Is this a problem? -sme0523 + // restricted parking at origin: + // first find parking: +// final Link parkingLink = NetworkUtils.getNearestLink( reducedNetwork, oldTrip.getOriginActivity().getCoord() ); + final Link originParkingLink = linkChooser.decideOnLink( fromFacility, reducedNetwork ); + + final Activity originParkingActivity = scenario.getPopulation().getFactory().createInteractionActivityFromLinkId( + TripStructureUtils.createStageActivityType( "parking" ), originParkingLink.getId() ); + final Facility originParkingFacility = FacilitiesUtils.toFacility( originParkingActivity, facilities ); + + //parking at destination + final Link destinationParkingLink = linkChooser.decideOnLink(toFacility, reducedNetwork); + + final Activity destinationParkingActivity = scenario.getPopulation().getFactory().createInteractionActivityFromLinkId( + TripStructureUtils.createStageActivityType("parking"), destinationParkingLink.getId()); + final Facility destinationParkingFacility = FacilitiesUtils.toFacility(destinationParkingActivity, facilities); + + // trip from origin to originParking: + final List originWalkTripElements = tripRouter.calcRoute( TransportMode.walk, fromFacility, originParkingFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); + for( PlanElement tripElement : originWalkTripElements ){ + if ( tripElement instanceof Leg ) { + TripStructureUtils.setRoutingMode( (Leg) tripElement, TransportMode.car ); + } + } + + newTripElements.addAll( originWalkTripElements ); + // originParking interaction: + newTripElements.add( originParkingActivity ); + // trip from originParking to destinationParking: + final List carTripElements = tripRouter.calcRoute( routingMode, originParkingFacility, destinationParkingFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); + newTripElements.addAll( carTripElements ); - Activity parkingInteraction = null; + newTripElements.add(destinationParkingActivity); - if (parkingAtOriginTrip.get(parkingAtOriginTrip.size() - 1) instanceof Activity) { - parkingInteraction = (Activity) parkingAtOriginTrip.get(parkingAtOriginTrip.size() - 1); - } else { - throw new RuntimeException(); + // trip from destinationParking to destination: + final List destinationWalkTripElements = tripRouter.calcRoute(TransportMode.walk, destinationParkingFacility, toFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes()); + for (PlanElement tripElement : destinationWalkTripElements) { + if (tripElement instanceof Leg) { + TripStructureUtils.setRoutingMode((Leg) tripElement, TransportMode.car); + } } + newTripElements.addAll(destinationWalkTripElements); - Facility fromFacilityParking = FacilitiesUtils.toFacility( parkingInteraction, facilities ); - List parkingAtDestinationTrip = createTripForParkingAtDestination(oldTrip, routingMode, fromFacilityParking, toFacility, plan, timeTracker); - List parkingAtDestinationTrip2 = createTripForParkingAtDestination(oldTrip, routingMode, fromFacilityParking, toFacility, plan, timeTracker); + putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); + timeTracker.addElements( newTripElements ); - parkingAtOriginTrip.addAll(parkingAtDestinationTrip); + TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity() ); - TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), parkingAtOriginTrip, oldTrip.getDestinationActivity() ); } else { throw new RuntimeException(); // to be implemented! From 1d6dbd5cd5135ff17ee95a6c312ccc495fa0c370 Mon Sep 17 00:00:00 2001 From: simei94 Date: Tue, 23 May 2023 17:04:41 +0200 Subject: [PATCH 029/106] make getters for attr names package private --- .../run/{prepare => }/LeipzigUtils.java | 56 ++++++------------- .../run/TimeRestrictedParkingCostHandler.java | 1 - .../prepare/ParkingCapacitiesAttacher.java | 1 + .../matsim/run/prepare/PrepareNetwork.java | 2 + .../org/matsim/run/ChessboardParkingTest.java | 3 +- .../run/LeipzigRouterPlanAlgorithm.java | 3 +- .../TimeRestrictedParkingCostHandlerTest.java | 1 - 7 files changed, 21 insertions(+), 46 deletions(-) rename src/main/java/org/matsim/run/{prepare => }/LeipzigUtils.java (64%) diff --git a/src/main/java/org/matsim/run/prepare/LeipzigUtils.java b/src/main/java/org/matsim/run/LeipzigUtils.java similarity index 64% rename from src/main/java/org/matsim/run/prepare/LeipzigUtils.java rename to src/main/java/org/matsim/run/LeipzigUtils.java index 4326a7c9..db52311a 100644 --- a/src/main/java/org/matsim/run/prepare/LeipzigUtils.java +++ b/src/main/java/org/matsim/run/LeipzigUtils.java @@ -1,7 +1,6 @@ -package org.matsim.run.prepare; +package org.matsim.run; import org.matsim.api.core.v01.network.Link; -import org.matsim.api.core.v01.population.Person; import java.util.HashSet; import java.util.Set; @@ -28,7 +27,7 @@ private LeipzigUtils(){} /** * Defines the parkingBehaviour of a person. */ - public enum PersonParkingBehaviour {defaultLogic, parkingSearchLogicLeipzig, @Deprecated shopping} + enum PersonParkingBehaviour {defaultLogic, parkingSearchLogicLeipzig, @Deprecated shopping} /** * Defines the parkingType of a network link. @@ -40,50 +39,51 @@ private enum LinkParkingType{linkOutsideResidentialArea, linkInResidentialArea} */ public static boolean isLinkParkingTypeInsideResidentialArea(Link link ) { String result = (String) link.getAttributes().getAttribute( "linkParkingType" ); + boolean insideResidentialArea = true; + if ( result == null ) { - return false ; - } else { - return true; + insideResidentialArea = false; } + return insideResidentialArea; } - public static String getMode() { + static String getMode() { return mode; } - public static String getDailyParkingCostLinkAttributeName() { + static String getDailyParkingCostLinkAttributeName() { return dailyParkingCostLinkAttributeName; } - public static String getFirstHourParkingCostLinkAttributeName() { + static String getFirstHourParkingCostLinkAttributeName() { return firstHourParkingCostLinkAttributeName; } - public static String getExtraHourParkingCostLinkAttributeName() { + static String getExtraHourParkingCostLinkAttributeName() { return extraHourParkingCostLinkAttributeName; } - public static String getMaxDailyParkingCostLinkAttributeName() { + static String getMaxDailyParkingCostLinkAttributeName() { return maxDailyParkingCostLinkAttributeName; } - public static String getMaxParkingDurationAttributeName() { + static String getMaxParkingDurationAttributeName() { return maxParkingDurationAttributeName; } - public static String getParkingPenaltyAttributeName() { + static String getParkingPenaltyAttributeName() { return parkingPenaltyAttributeName; } - public static String getResidentialParkingFeeAttributeName() { + static String getResidentialParkingFeeAttributeName() { return residentialParkingFeePerDay; } - public static String getActivityPrefixForDailyParkingCosts() { + static String getActivityPrefixForDailyParkingCosts() { return activityPrefixForDailyParkingCosts; } - public static Set getActivityPrefixesToBeExcludedFromParkingCost() { + static Set getActivityPrefixesToBeExcludedFromParkingCost() { return activityPrefixToBeExcludedFromParkingCost; } @@ -91,14 +91,6 @@ public static void setLinkParkingTypeToInsideResidentialArea(Link link ){ link.getAttributes().putAttribute( "linkParkingType", LinkParkingType.linkInResidentialArea.toString() ); } -// public static void setParkingToRestricted(Person person) { -// person.getAttributes().putAttribute("parkingType", "residentialParking"); -// } -// -// public static void setParkingToNonRestricted(Person person) { -// person.getAttributes().putAttribute("parkingType", "nonResidentialParking"); -// } - public static void setFirstHourParkingCost(Link link, double parkingCost) { link.getAttributes().putAttribute(getFirstHourParkingCostLinkAttributeName(), parkingCost); } @@ -114,20 +106,4 @@ public static void setResidentialParkingCost(Link link, double parkingCost) { public static void setParkingCapacity(Link link, double parkingCapacity) { link.getAttributes().putAttribute("parkingCapacity", parkingCapacity); } - - public static void setParkingToShoppingCenter(Link link) { - link.getAttributes().putAttribute("parkingForShopping", "shoppingCenter"); - } - - /** - * check if parking for activity type shopping is allowed on a given link. - */ - public static boolean parkingAllowedForShopping(Link link) { - String result = (String) link.getAttributes().getAttribute( "parkingForShopping" ); - if (result == null) { - return false ; - } else { - return true; - } - } } diff --git a/src/main/java/org/matsim/run/TimeRestrictedParkingCostHandler.java b/src/main/java/org/matsim/run/TimeRestrictedParkingCostHandler.java index 2a78fd29..1edeb333 100644 --- a/src/main/java/org/matsim/run/TimeRestrictedParkingCostHandler.java +++ b/src/main/java/org/matsim/run/TimeRestrictedParkingCostHandler.java @@ -44,7 +44,6 @@ import org.matsim.core.router.StageActivityTypeIdentifier; import com.google.inject.Inject; -import org.matsim.run.prepare.LeipzigUtils; /** * Implementation of ParkingCostHandler with an additional check for time restriction when including parking cost into a simulation. diff --git a/src/main/java/org/matsim/run/prepare/ParkingCapacitiesAttacher.java b/src/main/java/org/matsim/run/prepare/ParkingCapacitiesAttacher.java index 50b49152..f1ce0bf9 100644 --- a/src/main/java/org/matsim/run/prepare/ParkingCapacitiesAttacher.java +++ b/src/main/java/org/matsim/run/prepare/ParkingCapacitiesAttacher.java @@ -11,6 +11,7 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.application.options.ShpOptions; import org.matsim.core.utils.geometry.geotools.MGC; +import org.matsim.run.LeipzigUtils; import java.io.BufferedReader; import java.io.FileReader; diff --git a/src/main/java/org/matsim/run/prepare/PrepareNetwork.java b/src/main/java/org/matsim/run/prepare/PrepareNetwork.java index 75268199..614f25ac 100644 --- a/src/main/java/org/matsim/run/prepare/PrepareNetwork.java +++ b/src/main/java/org/matsim/run/prepare/PrepareNetwork.java @@ -14,6 +14,7 @@ import org.matsim.core.network.algorithms.MultimodalNetworkCleaner; import org.matsim.core.utils.geometry.geotools.MGC; import org.matsim.core.utils.gis.ShapeFileReader; +import org.matsim.run.LeipzigUtils; import org.matsim.utils.gis.shp2matsim.ShpGeometryUtils; import org.opengis.feature.simple.SimpleFeature; import picocli.CommandLine; @@ -185,6 +186,7 @@ static void prepareParkingCost(Network network, ShpOptions parkingCostShape) { if (linkInShp && feature.getAttribute(residentialParkingCostAttrName) != null) { resPFee = (Double) feature.getAttribute(residentialParkingCostAttrName); + LeipzigUtils.setLinkParkingTypeToInsideResidentialArea(link); } } diff --git a/src/test/java/org/matsim/run/ChessboardParkingTest.java b/src/test/java/org/matsim/run/ChessboardParkingTest.java index 8f3b1b4c..4f245691 100644 --- a/src/test/java/org/matsim/run/ChessboardParkingTest.java +++ b/src/test/java/org/matsim/run/ChessboardParkingTest.java @@ -34,7 +34,6 @@ import org.matsim.core.utils.timing.TimeInterpretation; import org.matsim.examples.ExamplesUtils; import org.matsim.facilities.ActivityFacilities; -import org.matsim.run.prepare.LeipzigUtils; import org.matsim.testcases.MatsimTestUtils; import playground.vsp.openberlinscenario.cemdap.output.ActivityTypes; @@ -45,7 +44,7 @@ import static org.matsim.core.config.groups.PlanCalcScoreConfigGroup.*; /** - * abc + * Test class to test a parking logic, which included several areas where only residents are allowed to park */ public class ChessboardParkingTest { private static final Logger log = LogManager.getLogger(ChessboardParkingTest.class); diff --git a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index 3f264522..4118a273 100644 --- a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -22,14 +22,13 @@ import org.matsim.facilities.ActivityFacilities; import org.matsim.facilities.FacilitiesUtils; import org.matsim.facilities.Facility; -import org.matsim.run.prepare.LeipzigUtils; import playground.vsp.openberlinscenario.cemdap.output.ActivityTypes; import java.util.ArrayList; import java.util.List; import static org.matsim.core.router.PlanRouter.putVehicleFromOldTripIntoNewTripIfMeaningful; -import static org.matsim.run.prepare.LeipzigUtils.isLinkParkingTypeInsideResidentialArea; +import static org.matsim.run.LeipzigUtils.isLinkParkingTypeInsideResidentialArea; final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ private static final Logger log = LogManager.getLogger(LeipzigRouterPlanAlgorithm.class ); diff --git a/src/test/java/org/matsim/run/TimeRestrictedParkingCostHandlerTest.java b/src/test/java/org/matsim/run/TimeRestrictedParkingCostHandlerTest.java index afdeb6b4..c5303636 100644 --- a/src/test/java/org/matsim/run/TimeRestrictedParkingCostHandlerTest.java +++ b/src/test/java/org/matsim/run/TimeRestrictedParkingCostHandlerTest.java @@ -23,7 +23,6 @@ import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.utils.io.IOUtils; import org.matsim.examples.ExamplesUtils; -import org.matsim.run.prepare.LeipzigUtils; import org.matsim.testcases.MatsimTestUtils; import playground.vsp.openberlinscenario.cemdap.output.ActivityTypes; import playground.vsp.simpleParkingCostHandler.ParkingCostConfigGroup; From 79c228201b5a522ee6f3a92b1ab612b94d0bbbed Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Fri, 26 May 2023 14:35:47 +0200 Subject: [PATCH 030/106] remove unnecessary shoppingNetwork as this is done now differently --- .../org/matsim/run/LeipzigRouterPlanAlgorithm.java | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index 4118a273..4559c8bf 100644 --- a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -39,7 +39,6 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ private final Network reducedNetwork; private final MultimodalLinkChooser linkChooser; private final Scenario scenario; - private final Network networkForShopping; LeipzigRouterPlanAlgorithm( final TripRouter tripRouter, final ActivityFacilities facilities, final TimeInterpretation timeInterpretation, SingleModeNetworksCache singleModeNetworksCache, Scenario scenario, MultimodalLinkChooser linkChooser ){ @@ -52,7 +51,6 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ // yyyy one should look at the networks cache and see how the following is done. And maybe even register it there. this.reducedNetwork = NetworkUtils.createNetwork( scenario.getConfig().network() ); this.linkChooser = linkChooser; - this.networkForShopping = NetworkUtils.createNetwork(scenario.getConfig().network()); for( Node node : this.fullModalNetwork.getNodes().values() ){ reducedNetwork.addNode( node ); } @@ -114,8 +112,6 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ } else if (parkingBehaviourAtOrigin == LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig && parkingBehaviourAtDestination == LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig){ - List newTripElements = new ArrayList<>(); - // restricted parking at origin: // first find parking: // final Link parkingLink = NetworkUtils.getNearestLink( reducedNetwork, oldTrip.getOriginActivity().getCoord() ); @@ -141,7 +137,7 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ } } - newTripElements.addAll( originWalkTripElements ); + List newTripElements = new ArrayList<>(originWalkTripElements); // originParking interaction: newTripElements.add( originParkingActivity ); // trip from originParking to destinationParking: @@ -207,7 +203,6 @@ private static LeipzigUtils.PersonParkingBehaviour getParkingBehaviour(Network f } private List createTripForParkingAtOrigin(TripStructureUtils.Trip oldTrip, String routingMode, Facility fromFacility, Facility toFacility, Plan plan, TimeTracker timeTracker) { - List newTripElements = new ArrayList<>(); // restricted parking at origin: // first find parking: @@ -227,7 +222,7 @@ private List createTripForParkingAtOrigin(TripStructureUtils.Trip o } } - newTripElements.addAll( walkTripElements ); + List newTripElements = new ArrayList<>(walkTripElements); // parking interaction: newTripElements.add( parkingActivity ); // trip from parking to final destination: @@ -242,7 +237,6 @@ private List createTripForParkingAtOrigin(TripStructureUtils.Trip o } private List createTripForParkingAtDestination(TripStructureUtils.Trip oldTrip, String routingMode, Facility fromFacility, Facility toFacility, Plan plan, TimeTracker timeTracker) { - List newTripElements = new ArrayList<>(); //parking at destination final Link parkingLink = linkChooser.decideOnLink(toFacility, reducedNetwork); @@ -255,7 +249,7 @@ private List createTripForParkingAtDestination(TripStructureUtils.T timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes()); // parking interaction: - newTripElements.addAll(carTripElements); + List newTripElements = new ArrayList<>(carTripElements); newTripElements.add(parkingActivity); // trip from parking to destination: From 18be50493d419fc4e7f4008e89fb482fb2216c50 Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Fri, 26 May 2023 16:31:25 +0200 Subject: [PATCH 031/106] moved LeipzigRouterPlanAlgorithm --- .../java/org/matsim/run/LeipzigRouterPlanAlgorithm.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{test => main}/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java (100%) diff --git a/src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java similarity index 100% rename from src/test/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java rename to src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java From 2d36bd2c125b1e6960543b0a7e1b2499524f671d Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Fri, 26 May 2023 16:41:22 +0200 Subject: [PATCH 032/106] extract and move LeipzigRoutingStrategyProvider --- .../run/LeipzigRoutingStrategyProvider.java | 49 +++++++++++++++++++ .../org/matsim/run/ChessboardParkingTest.java | 40 --------------- 2 files changed, 49 insertions(+), 40 deletions(-) create mode 100644 src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java diff --git a/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java b/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java new file mode 100644 index 00000000..1bf8bb99 --- /dev/null +++ b/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java @@ -0,0 +1,49 @@ +package org.matsim.run; + +import com.google.inject.Inject; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.core.config.groups.GlobalConfigGroup; +import org.matsim.core.population.algorithms.PlanAlgorithm; +import org.matsim.core.replanning.PlanStrategy; +import org.matsim.core.replanning.PlanStrategyImpl; +import org.matsim.core.replanning.modules.AbstractMultithreadedModule; +import org.matsim.core.replanning.selectors.RandomPlanSelector; +import org.matsim.core.router.MultimodalLinkChooser; +import org.matsim.core.router.SingleModeNetworksCache; +import org.matsim.core.router.TripRouter; +import org.matsim.core.utils.timing.TimeInterpretation; +import org.matsim.facilities.ActivityFacilities; + +import javax.inject.Provider; + +public class LeipzigRoutingStrategyProvider implements Provider { + // is a provider in matsim core. maybe try without. kai, apr'23 + @Inject + private GlobalConfigGroup globalConfigGroup; + @Inject + private ActivityFacilities facilities; + @Inject + private Provider tripRouterProvider; + @Inject + private SingleModeNetworksCache singleModeNetworksCache; + @Inject + private Scenario scenario; + @Inject + private TimeInterpretation timeInterpretation; + @Inject + MultimodalLinkChooser linkChooser; + + @Override + public PlanStrategy get() { + PlanStrategyImpl.Builder builder = new PlanStrategyImpl.Builder(new RandomPlanSelector()); + builder.addStrategyModule(new AbstractMultithreadedModule(globalConfigGroup) { + @Override + public final PlanAlgorithm getPlanAlgoInstance() { + return new LeipzigRouterPlanAlgorithm(tripRouterProvider.get(), facilities, timeInterpretation, singleModeNetworksCache, scenario, linkChooser); + } + }); + return builder.build(); + } +} diff --git a/src/test/java/org/matsim/run/ChessboardParkingTest.java b/src/test/java/org/matsim/run/ChessboardParkingTest.java index 4f245691..c13688bf 100644 --- a/src/test/java/org/matsim/run/ChessboardParkingTest.java +++ b/src/test/java/org/matsim/run/ChessboardParkingTest.java @@ -1,14 +1,12 @@ package org.matsim.run; -import com.google.inject.Inject; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.TransportMode; import org.matsim.api.core.v01.events.ActivityStartEvent; import org.matsim.api.core.v01.events.handler.ActivityStartEventHandler; @@ -22,22 +20,14 @@ import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy; import org.matsim.core.population.PopulationUtils; -import org.matsim.core.population.algorithms.PlanAlgorithm; -import org.matsim.core.replanning.PlanStrategy; -import org.matsim.core.replanning.PlanStrategyImpl; -import org.matsim.core.replanning.modules.AbstractMultithreadedModule; -import org.matsim.core.replanning.selectors.RandomPlanSelector; import org.matsim.core.router.*; import org.matsim.core.scenario.MutableScenario; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.utils.io.IOUtils; -import org.matsim.core.utils.timing.TimeInterpretation; import org.matsim.examples.ExamplesUtils; -import org.matsim.facilities.ActivityFacilities; import org.matsim.testcases.MatsimTestUtils; import playground.vsp.openberlinscenario.cemdap.output.ActivityTypes; -import javax.inject.Provider; import java.net.URL; import java.util.*; @@ -142,36 +132,6 @@ public void install() { } - static final class LeipzigRoutingStrategyProvider implements Provider { - // is a provider in matsim core. maybe try without. kai, apr'23 - @Inject - private GlobalConfigGroup globalConfigGroup; - @Inject - private ActivityFacilities facilities; - @Inject - private Provider tripRouterProvider; - @Inject - private SingleModeNetworksCache singleModeNetworksCache; - @Inject - private Scenario scenario; - @Inject - private TimeInterpretation timeInterpretation; - @Inject - MultimodalLinkChooser linkChooser; - - @Override - public PlanStrategy get() { - PlanStrategyImpl.Builder builder = new PlanStrategyImpl.Builder(new RandomPlanSelector<>()); - builder.addStrategyModule(new AbstractMultithreadedModule(globalConfigGroup) { - @Override - public final PlanAlgorithm getPlanAlgoInstance() { - return new LeipzigRouterPlanAlgorithm(tripRouterProvider.get(), facilities, timeInterpretation, singleModeNetworksCache, scenario, linkChooser); - } - }); - return builder.build(); - } - } - // static final class LeipzigMultimodalLinkChooser implements MultimodalLinkChooser { // From 2c7f0ae9db8f3cda9bca4288b6f75a0ff8ba8bb5 Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Fri, 26 May 2023 17:33:18 +0200 Subject: [PATCH 033/106] added parking to run class for a test run --- .../org/matsim/run/RunLeipzigScenario.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index 47d9b89a..e753040e 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -47,10 +47,7 @@ import org.matsim.contrib.vsp.scenario.SnzActivities; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.PlansCalcRouteConfigGroup; -import org.matsim.core.config.groups.QSimConfigGroup; -import org.matsim.core.config.groups.SubtourModeChoiceConfigGroup; -import org.matsim.core.config.groups.VspExperimentalConfigGroup; +import org.matsim.core.config.groups.*; import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; import org.matsim.core.replanning.choosers.ForceInnovationStrategyChooser; @@ -58,6 +55,7 @@ import org.matsim.core.replanning.strategies.DefaultPlanStrategiesModule; import org.matsim.core.router.AnalysisMainModeIdentifier; import org.matsim.core.router.MultimodalLinkChooser; +import org.matsim.core.router.TripStructureUtils; import org.matsim.core.scoring.functions.ScoringParametersForPerson; import org.matsim.extensions.pt.fare.intermodalTripFareCompensator.IntermodalTripFareCompensatorConfigGroup; import org.matsim.extensions.pt.fare.intermodalTripFareCompensator.IntermodalTripFareCompensatorsConfigGroup; @@ -97,7 +95,7 @@ public class RunLeipzigScenario extends MATSimApplication { private static final Logger log = LogManager.getLogger(RunLeipzigScenario.class); - + final String RE_ROUTE_LEIPZIG = "ReRouteLeipzig"; static final String VERSION = "1.1"; @CommandLine.Mixin @@ -155,7 +153,7 @@ protected Config prepareConfig(Config config) { config.qsim().setStorageCapFactor(sample.getSize() / 100.0); } - config.vspExperimental().setVspDefaultsCheckingLevel(VspExperimentalConfigGroup.VspDefaultsCheckingLevel.abort); + config.vspExperimental().setVspDefaultsCheckingLevel(VspExperimentalConfigGroup.VspDefaultsCheckingLevel.warn); config.plansCalcRoute().setAccessEgressType(PlansCalcRouteConfigGroup.AccessEgressType.accessEgressModeToLink); // (yyyy what exactly is this doing?) @@ -193,6 +191,8 @@ protected Config prepareConfig(Config config) { ConfigUtils.addOrGetModule(config, ParkingCostConfigGroup.class); } + config.planCalcScore().addActivityParams(new PlanCalcScoreConfigGroup.ActivityParams(TripStructureUtils.createStageActivityType("parking")).setScoringThisActivityAtAll(false)); + return config; } @@ -222,6 +222,16 @@ protected void prepareScenario(Scenario scenario) { protected void prepareControler(Controler controler) { Config config = controler.getConfig(); + + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { +// this.bind( MultimodalLinkChooser.class ).toInstance( new LeipzigMultimodalLinkChooser() ); + this.addPlanStrategyBinding(RE_ROUTE_LEIPZIG).toProvider(LeipzigRoutingStrategyProvider.class); + // yyyy this only uses it during replanning!!! kai, apr'23 + } + }); + controler.addOverridingModule(new AbstractModule() { @Override public void install() { From 81f3c76395cc96e9577ebf316ae4e14276294666 Mon Sep 17 00:00:00 2001 From: simei94 Date: Tue, 30 May 2023 11:42:07 +0200 Subject: [PATCH 034/106] comment + todo on vspDefaults --- src/main/java/org/matsim/run/RunLeipzigScenario.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index e753040e..20bc455f 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -153,6 +153,8 @@ protected Config prepareConfig(Config config) { config.qsim().setStorageCapFactor(sample.getSize() / 100.0); } + //I do not think that we should set this to warn. + //TODO: set to abort again. At the latest this has to be done when merging branch complicatedParking into main!!! -sme0523 config.vspExperimental().setVspDefaultsCheckingLevel(VspExperimentalConfigGroup.VspDefaultsCheckingLevel.warn); config.plansCalcRoute().setAccessEgressType(PlansCalcRouteConfigGroup.AccessEgressType.accessEgressModeToLink); From 98523eada0063fe116f18adfbe90bb57f6eefc51 Mon Sep 17 00:00:00 2001 From: simei94 Date: Tue, 30 May 2023 12:13:10 +0200 Subject: [PATCH 035/106] checkstyle --- .../run/LeipzigRouterPlanAlgorithm.java | 22 +++++++++---------- .../run/LeipzigRoutingStrategyProvider.java | 6 ++++- .../org/matsim/run/RunLeipzigScenario.java | 2 +- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index 4559c8bf..0163d82c 100644 --- a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -41,7 +41,7 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ private final Scenario scenario; LeipzigRouterPlanAlgorithm( final TripRouter tripRouter, final ActivityFacilities facilities, final TimeInterpretation timeInterpretation, - SingleModeNetworksCache singleModeNetworksCache, Scenario scenario, MultimodalLinkChooser linkChooser ){ + SingleModeNetworksCache singleModeNetworksCache, Scenario scenario, MultimodalLinkChooser linkChooser ){ this.tripRouter = tripRouter; this.facilities = facilities; this.timeInterpretation = timeInterpretation; @@ -51,11 +51,11 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ // yyyy one should look at the networks cache and see how the following is done. And maybe even register it there. this.reducedNetwork = NetworkUtils.createNetwork( scenario.getConfig().network() ); this.linkChooser = linkChooser; - for( Node node : this.fullModalNetwork.getNodes().values() ){ + for ( Node node : this.fullModalNetwork.getNodes().values() ){ reducedNetwork.addNode( node ); } - for( Link link : this.fullModalNetwork.getLinks().values() ){ - if( !LeipzigUtils.isLinkParkingTypeInsideResidentialArea( link ) ){ + for ( Link link : this.fullModalNetwork.getLinks().values() ){ + if ( !LeipzigUtils.isLinkParkingTypeInsideResidentialArea( link ) ){ reducedNetwork.addLink( link ); } } @@ -67,12 +67,12 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ TimeTracker timeTracker = new TimeTracker( timeInterpretation ); log.warn( "=== old plan: ===" ); - for( PlanElement tripElement : plan.getPlanElements() ){ + for ( PlanElement tripElement : plan.getPlanElements() ){ log.warn( tripElement ); } log.warn( "======" ); - for( TripStructureUtils.Trip oldTrip : trips ){ + for ( TripStructureUtils.Trip oldTrip : trips ){ final String routingMode = TripStructureUtils.identifyMainMode( oldTrip.getTripElements() ); timeTracker.addActivity( oldTrip.getOriginActivity() ); @@ -131,7 +131,7 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ // trip from origin to originParking: final List originWalkTripElements = tripRouter.calcRoute( TransportMode.walk, fromFacility, originParkingFacility, timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); - for( PlanElement tripElement : originWalkTripElements ){ + for ( PlanElement tripElement : originWalkTripElements ){ if ( tripElement instanceof Leg ) { TripStructureUtils.setRoutingMode( (Leg) tripElement, TransportMode.car ); } @@ -170,7 +170,7 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ } log.warn( "=== new plan: ===" ); - for( PlanElement tripElement : plan.getPlanElements() ){ + for ( PlanElement tripElement : plan.getPlanElements() ){ log.warn( tripElement ); } log.warn( "======" ); @@ -185,11 +185,11 @@ private static LeipzigUtils.PersonParkingBehaviour getParkingBehaviour(Network f // an dieser stelle waere es besser abzufragen, ob die person in der naehe wohnt anstatt nur die home act -> residential parking zuzuordnen // check if non-home activity (since otherwise we assume that there is no parking restriction): - if( !originActivity.getType().equals( ActivityTypes.HOME ) ){ + if ( !originActivity.getType().equals( ActivityTypes.HOME ) ){ // if non-home activity, check if in residential parking area: Link link = fullModalNetwork.getLinks().get( originActivity.getLinkId() ); - if( isLinkParkingTypeInsideResidentialArea( link ) ){ + if ( isLinkParkingTypeInsideResidentialArea( link ) ){ parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig; // if (originActivity.getType().equals(ActivityTypes.SHOPPING)) { // // change this to parking type normal/ unrestricted @@ -216,7 +216,7 @@ private List createTripForParkingAtOrigin(TripStructureUtils.Trip o // trip from origin to parking: final List walkTripElements = tripRouter.calcRoute( TransportMode.walk, fromFacility, parkingFacility, timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); - for( PlanElement tripElement : walkTripElements ){ + for ( PlanElement tripElement : walkTripElements ){ if ( tripElement instanceof Leg ) { TripStructureUtils.setRoutingMode( (Leg) tripElement, TransportMode.car ); } diff --git a/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java b/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java index 1bf8bb99..eeba13fd 100644 --- a/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java +++ b/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java @@ -18,6 +18,10 @@ import javax.inject.Provider; +/** + * This class installs the specific routing algorithm, which is implemented in the Leipzig scenario. + * The algorithm includes a logic for parking vehicles in a specific area. + */ public class LeipzigRoutingStrategyProvider implements Provider { // is a provider in matsim core. maybe try without. kai, apr'23 @Inject @@ -40,7 +44,7 @@ public PlanStrategy get() { PlanStrategyImpl.Builder builder = new PlanStrategyImpl.Builder(new RandomPlanSelector()); builder.addStrategyModule(new AbstractMultithreadedModule(globalConfigGroup) { @Override - public final PlanAlgorithm getPlanAlgoInstance() { + public PlanAlgorithm getPlanAlgoInstance() { return new LeipzigRouterPlanAlgorithm(tripRouterProvider.get(), facilities, timeInterpretation, singleModeNetworksCache, scenario, linkChooser); } }); diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index 20bc455f..4567e2e6 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -95,7 +95,7 @@ public class RunLeipzigScenario extends MATSimApplication { private static final Logger log = LogManager.getLogger(RunLeipzigScenario.class); - final String RE_ROUTE_LEIPZIG = "ReRouteLeipzig"; + static final String RE_ROUTE_LEIPZIG = "ReRouteLeipzig"; static final String VERSION = "1.1"; @CommandLine.Mixin From 0b826f3b8b410c0fed7175cc05d463f92a8e46a1 Mon Sep 17 00:00:00 2001 From: simei94 Date: Tue, 30 May 2023 12:56:34 +0200 Subject: [PATCH 036/106] bugfix --- .../run/prepare/AssignParkingAttributes.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java b/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java index e185d657..43f67b0d 100644 --- a/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java +++ b/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java @@ -6,7 +6,7 @@ import org.matsim.api.core.v01.population.PlanElement; import org.matsim.api.core.v01.population.Population; import org.matsim.application.options.ShpOptions; -import org.matsim.core.utils.geometry.geotools.MGC; +//import org.matsim.core.utils.geometry.geotools.MGC; import playground.vsp.openberlinscenario.cemdap.output.ActivityTypes; final class AssignParkingAttributes { @@ -28,13 +28,13 @@ static void addParkingAttributesToPopulation(Population population, ShpOptions s continue; } - isInsideRestrictedParkingArea = MGC.coord2Point(activity.getCoord()).within(restrictedParkingArea); - - if (isInsideRestrictedParkingArea) { -// LeipzigUtils.setParkingToRestricted(person); - } else { -// LeipzigUtils.setParkingToNonRestricted(person); - } +// isInsideRestrictedParkingArea = MGC.coord2Point(activity.getCoord()).within(restrictedParkingArea); +// +// if (isInsideRestrictedParkingArea) { +//// LeipzigUtils.setParkingToRestricted(person); +// } else { +//// LeipzigUtils.setParkingToNonRestricted(person); +// } break; } From 3e451f42cc16afcefb88605696cca87d77c65529 Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Wed, 31 May 2023 11:09:25 +0200 Subject: [PATCH 037/106] small changes to LeipzigRouterPlanAlgorithm, now getting parking behaviour based on facilities, added everything to run class and the test --- .../matsim/run/LeipzigRouterPlanAlgorithm.java | 10 +++++----- .../org/matsim/run/RunLeipzigScenario.java | 18 ++++++++++++++++-- .../org/matsim/run/prepare/PrepareNetwork.java | 2 +- .../org/matsim/run/ChessboardParkingTest.java | 4 ++++ .../matsim/run/RunLeipzigIntegrationTest.java | 3 ++- 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index 0163d82c..90fdfa6f 100644 --- a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; import static org.matsim.core.router.PlanRouter.putVehicleFromOldTripIntoNewTripIfMeaningful; import static org.matsim.run.LeipzigUtils.isLinkParkingTypeInsideResidentialArea; @@ -84,8 +85,8 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ // At this point, I only want to deal with residential parking. Shopping comes later (and is simpler). - LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtOrigin = getParkingBehaviour( fullModalNetwork, oldTrip.getOriginActivity() ); - LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtDestination = getParkingBehaviour( fullModalNetwork, oldTrip.getDestinationActivity() ); + LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtOrigin = getParkingBehaviour( fullModalNetwork, oldTrip.getOriginActivity(), fromFacility ); + LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtDestination = getParkingBehaviour( fullModalNetwork, oldTrip.getDestinationActivity(), toFacility ); if ( parkingBehaviourAtOrigin == LeipzigUtils.PersonParkingBehaviour.defaultLogic && parkingBehaviourAtDestination == LeipzigUtils.PersonParkingBehaviour.defaultLogic){ @@ -177,7 +178,7 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ } - private static LeipzigUtils.PersonParkingBehaviour getParkingBehaviour(Network fullModalNetwork, Activity originActivity ){ + private static LeipzigUtils.PersonParkingBehaviour getParkingBehaviour(Network fullModalNetwork, Activity originActivity, Facility originFacility){ LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.defaultLogic; // if we find out that there are time restrictions on all the links @@ -187,8 +188,7 @@ private static LeipzigUtils.PersonParkingBehaviour getParkingBehaviour(Network f // check if non-home activity (since otherwise we assume that there is no parking restriction): if ( !originActivity.getType().equals( ActivityTypes.HOME ) ){ - // if non-home activity, check if in residential parking area: - Link link = fullModalNetwork.getLinks().get( originActivity.getLinkId() ); + Link link = fullModalNetwork.getLinks().get( originFacility.getLinkId() ); if ( isLinkParkingTypeInsideResidentialArea( link ) ){ parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig; // if (originActivity.getType().equals(ActivityTypes.SHOPPING)) { diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index 4567e2e6..1df2fda0 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -191,10 +191,24 @@ protected Config prepareConfig(Config config) { if (networkOpt.hasParkingCostArea()) { ConfigUtils.addOrGetModule(config, ParkingCostConfigGroup.class); + config.planCalcScore().addActivityParams(new PlanCalcScoreConfigGroup.ActivityParams(TripStructureUtils.createStageActivityType("parking")).setScoringThisActivityAtAll(false)); + config.strategy().clearStrategySettings(); + StrategyConfigGroup.StrategySettings stratSetsForPErson = new StrategyConfigGroup.StrategySettings(); + stratSetsForPErson.setWeight(1.); + stratSetsForPErson.setStrategyName(RE_ROUTE_LEIPZIG); + stratSetsForPErson.setSubpopulation("person"); + config.strategy().addStrategySettings(stratSetsForPErson); + StrategyConfigGroup.StrategySettings stratSetsForFreight = new StrategyConfigGroup.StrategySettings(); + stratSetsForFreight.setWeight(1.); + stratSetsForFreight.setStrategyName(RE_ROUTE_LEIPZIG); + stratSetsForFreight.setSubpopulation("freight"); + config.strategy().addStrategySettings(stratSetsForFreight); + config.controler().setLastIteration(1); + // this is how it is supposed to be + //config.facilities().setFacilitiesSource(FacilitiesConfigGroup.FacilitiesSource.onePerActivityLinkInPlansFile); + config.facilities().setFacilitiesSource(FacilitiesConfigGroup.FacilitiesSource.none); } - config.planCalcScore().addActivityParams(new PlanCalcScoreConfigGroup.ActivityParams(TripStructureUtils.createStageActivityType("parking")).setScoringThisActivityAtAll(false)); - return config; } diff --git a/src/main/java/org/matsim/run/prepare/PrepareNetwork.java b/src/main/java/org/matsim/run/prepare/PrepareNetwork.java index 614f25ac..e676b5e9 100644 --- a/src/main/java/org/matsim/run/prepare/PrepareNetwork.java +++ b/src/main/java/org/matsim/run/prepare/PrepareNetwork.java @@ -111,7 +111,7 @@ static void prepareDRT(Network network, ShpOptions shp, String modes) { MultimodalNetworkCleaner multimodalNetworkCleaner = new MultimodalNetworkCleaner(network); multimodalNetworkCleaner.run(modesToAdd); - log.log(Level.INFO, "The following modes have been added to the network: %s ", modes); + log.log(Level.INFO, "The following modes have been added to the network: {}", modes); } /** diff --git a/src/test/java/org/matsim/run/ChessboardParkingTest.java b/src/test/java/org/matsim/run/ChessboardParkingTest.java index c13688bf..71fd000a 100644 --- a/src/test/java/org/matsim/run/ChessboardParkingTest.java +++ b/src/test/java/org/matsim/run/ChessboardParkingTest.java @@ -20,6 +20,8 @@ import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy; import org.matsim.core.population.PopulationUtils; +import org.matsim.core.population.algorithms.ParallelPersonAlgorithmUtils; +import org.matsim.core.population.algorithms.PersonPrepareForSim; import org.matsim.core.router.*; import org.matsim.core.scenario.MutableScenario; import org.matsim.core.scenario.ScenarioUtils; @@ -107,6 +109,8 @@ public final void runChessboardParkingTest1() { // System.exit(-1); //why ist the above here? Doesn't it end the test / sim so it is of no use here?! -sme0523 + + Controler controler = new Controler(scenario); controler.addOverridingModule(new AbstractModule() { @Override diff --git a/src/test/java/org/matsim/run/RunLeipzigIntegrationTest.java b/src/test/java/org/matsim/run/RunLeipzigIntegrationTest.java index ea7dd072..f90e00e5 100644 --- a/src/test/java/org/matsim/run/RunLeipzigIntegrationTest.java +++ b/src/test/java/org/matsim/run/RunLeipzigIntegrationTest.java @@ -43,7 +43,8 @@ public final void runPoint1pctIntegrationTest() { config.vehicles().setVehiclesFile(URL + "drt-base-case/leipzig-v1.1-vehicle-types-with-drt-scaledFleet.xml"); MATSimApplication.execute(RunLeipzigScenario.class, config, "run", "--1pct", "--slow-speed-area", exampleShp, - "--slow-speed-relative-change", "0.5","--drt-area", exampleShp, "--drt-modes", "drtNorth,drtSoutheast", "--post-processing", "disabled"); + "--slow-speed-relative-change", "0.5","--drt-area", exampleShp, "--drt-modes", "drtNorth,drtSoutheast", "--post-processing", "disabled", + "--parking-cost-area", "../../shared-svn/projects/NaMAV/data/shapefiles/leipzig_stadt/Leipzig_stadt.shp"); assertThat(output) .exists() From a32798c6ce3d25f1244fa57b469dc82bd77b81e8 Mon Sep 17 00:00:00 2001 From: simei94 Date: Wed, 31 May 2023 14:34:54 +0200 Subject: [PATCH 038/106] bugfix re assignment of parking cost --- src/main/java/org/matsim/run/prepare/PrepareNetwork.java | 8 ++++---- .../java/org/matsim/run/RunLeipzigIntegrationTest.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/matsim/run/prepare/PrepareNetwork.java b/src/main/java/org/matsim/run/prepare/PrepareNetwork.java index e676b5e9..d48b29bd 100644 --- a/src/main/java/org/matsim/run/prepare/PrepareNetwork.java +++ b/src/main/java/org/matsim/run/prepare/PrepareNetwork.java @@ -188,11 +188,11 @@ static void prepareParkingCost(Network network, ShpOptions parkingCostShape) { resPFee = (Double) feature.getAttribute(residentialParkingCostAttrName); LeipzigUtils.setLinkParkingTypeToInsideResidentialArea(link); } - } - LeipzigUtils.setFirstHourParkingCost(link, oneHourPCost); - LeipzigUtils.setExtraHourParkingCost(link, extraHourPCost); - LeipzigUtils.setResidentialParkingCost(link, resPFee); + LeipzigUtils.setFirstHourParkingCost(link, oneHourPCost); + LeipzigUtils.setExtraHourParkingCost(link, extraHourPCost); + LeipzigUtils.setResidentialParkingCost(link, resPFee); + } } } diff --git a/src/test/java/org/matsim/run/RunLeipzigIntegrationTest.java b/src/test/java/org/matsim/run/RunLeipzigIntegrationTest.java index f90e00e5..0be82bde 100644 --- a/src/test/java/org/matsim/run/RunLeipzigIntegrationTest.java +++ b/src/test/java/org/matsim/run/RunLeipzigIntegrationTest.java @@ -44,7 +44,7 @@ public final void runPoint1pctIntegrationTest() { MATSimApplication.execute(RunLeipzigScenario.class, config, "run", "--1pct", "--slow-speed-area", exampleShp, "--slow-speed-relative-change", "0.5","--drt-area", exampleShp, "--drt-modes", "drtNorth,drtSoutheast", "--post-processing", "disabled", - "--parking-cost-area", "../../shared-svn/projects/NaMAV/data/shapefiles/leipzig_stadt/Leipzig_stadt.shp"); + "--parking-cost-area", exampleShp); assertThat(output) .exists() From 1fd646766dd809ebd61ba9dd212febac56b55873 Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Wed, 31 May 2023 14:59:12 +0200 Subject: [PATCH 039/106] add routing mode to parkingBehaviour --- .../org/matsim/run/LeipzigRouterPlanAlgorithm.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index 90fdfa6f..37e035ff 100644 --- a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -85,8 +85,8 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ // At this point, I only want to deal with residential parking. Shopping comes later (and is simpler). - LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtOrigin = getParkingBehaviour( fullModalNetwork, oldTrip.getOriginActivity(), fromFacility ); - LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtDestination = getParkingBehaviour( fullModalNetwork, oldTrip.getDestinationActivity(), toFacility ); + LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtOrigin = getParkingBehaviour( fullModalNetwork, oldTrip.getOriginActivity(), fromFacility, routingMode ); + LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtDestination = getParkingBehaviour( fullModalNetwork, oldTrip.getDestinationActivity(), toFacility, routingMode ); if ( parkingBehaviourAtOrigin == LeipzigUtils.PersonParkingBehaviour.defaultLogic && parkingBehaviourAtDestination == LeipzigUtils.PersonParkingBehaviour.defaultLogic){ @@ -178,8 +178,12 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ } - private static LeipzigUtils.PersonParkingBehaviour getParkingBehaviour(Network fullModalNetwork, Activity originActivity, Facility originFacility){ + private static LeipzigUtils.PersonParkingBehaviour getParkingBehaviour(Network fullModalNetwork, Activity originActivity, Facility originFacility, String routingMode){ LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.defaultLogic; + if (routingMode.equals(TransportMode.pt)) { + + } + // if we find out that there are time restrictions on all the links //originActivity.getEndTime(); @@ -188,7 +192,7 @@ private static LeipzigUtils.PersonParkingBehaviour getParkingBehaviour(Network f // check if non-home activity (since otherwise we assume that there is no parking restriction): if ( !originActivity.getType().equals( ActivityTypes.HOME ) ){ - Link link = fullModalNetwork.getLinks().get( originFacility.getLinkId() ); + Link link = fullModalNetwork.getLinks().get( originActivity.getLinkId() ); if ( isLinkParkingTypeInsideResidentialArea( link ) ){ parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig; // if (originActivity.getType().equals(ActivityTypes.SHOPPING)) { From 4a2be42f667bfb8a704cc918e86ab7d838e4b43c Mon Sep 17 00:00:00 2001 From: simei94 Date: Wed, 31 May 2023 17:28:27 +0200 Subject: [PATCH 040/106] parkingBehaviourType parkingSearchLogicLeipzig only for routingModes car, ride and freight --- .../run/LeipzigRouterPlanAlgorithm.java | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index 37e035ff..a6de86fa 100644 --- a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -170,7 +170,7 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ } } - log.warn( "=== new plan: ===" ); + log.warn( "=== new plan: " + plan.getPerson().getId() + " ===" ); for ( PlanElement tripElement : plan.getPlanElements() ){ log.warn( tripElement ); } @@ -180,28 +180,26 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ private static LeipzigUtils.PersonParkingBehaviour getParkingBehaviour(Network fullModalNetwork, Activity originActivity, Facility originFacility, String routingMode){ LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.defaultLogic; - if (routingMode.equals(TransportMode.pt)) { - - } - // if we find out that there are time restrictions on all the links //originActivity.getEndTime(); - // an dieser stelle waere es besser abzufragen, ob die person in der naehe wohnt anstatt nur die home act -> residential parking zuzuordnen - // check if non-home activity (since otherwise we assume that there is no parking restriction): - if ( !originActivity.getType().equals( ActivityTypes.HOME ) ){ - - Link link = fullModalNetwork.getLinks().get( originActivity.getLinkId() ); - if ( isLinkParkingTypeInsideResidentialArea( link ) ){ - parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig; -// if (originActivity.getType().equals(ActivityTypes.SHOPPING)) { -// // change this to parking type normal/ unrestricted -// // on activity link, unrestricted -// parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.shopping; -// } - } + if ( routingMode.equals(TransportMode.car) || routingMode.equals(TransportMode.ride) || routingMode.equals("freight") ) { + // an dieser stelle waere es besser abzufragen, ob die person in der naehe wohnt anstatt nur die home act -> residential parking zuzuordnen + // check if non-home activity (since otherwise we assume that there is no parking restriction): + if ( !originActivity.getType().equals( ActivityTypes.HOME ) ){ + + Link link = fullModalNetwork.getLinks().get( originActivity.getLinkId() ); + if ( isLinkParkingTypeInsideResidentialArea( link ) ){ + parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig; + // if (originActivity.getType().equals(ActivityTypes.SHOPPING)) { + // // change this to parking type normal/ unrestricted + // // on activity link, unrestricted + // parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.shopping; + // } + } + } } return parkingBehaviourAtOrigin; } From 96462d24a5bf6f67c53069835f4cae6cfdecf967 Mon Sep 17 00:00:00 2001 From: simei94 Date: Thu, 1 Jun 2023 16:05:28 +0200 Subject: [PATCH 041/106] enable parkingLogicLeipzig only for mode car --- src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index a6de86fa..6792c26e 100644 --- a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -184,7 +184,7 @@ private static LeipzigUtils.PersonParkingBehaviour getParkingBehaviour(Network f // if we find out that there are time restrictions on all the links //originActivity.getEndTime(); - if ( routingMode.equals(TransportMode.car) || routingMode.equals(TransportMode.ride) || routingMode.equals("freight") ) { + if ( routingMode.equals(TransportMode.car) ) { // an dieser stelle waere es besser abzufragen, ob die person in der naehe wohnt anstatt nur die home act -> residential parking zuzuordnen // check if non-home activity (since otherwise we assume that there is no parking restriction): From 2e97a22504dc542f0cbb43d176a9cffc0eaf6071 Mon Sep 17 00:00:00 2001 From: simei94 Date: Thu, 1 Jun 2023 16:31:05 +0200 Subject: [PATCH 042/106] checkstyle --- src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index 6792c26e..1fff7863 100644 --- a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -26,7 +26,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Objects; import static org.matsim.core.router.PlanRouter.putVehicleFromOldTripIntoNewTripIfMeaningful; import static org.matsim.run.LeipzigUtils.isLinkParkingTypeInsideResidentialArea; From 1b29d1ddf12dfdd2dd6e7c4eadd5442c25311db0 Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Sun, 4 Jun 2023 23:12:51 +0200 Subject: [PATCH 043/106] better integration of the new ReRoute Strategy into the run class --- .../org/matsim/run/RunLeipzigScenario.java | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index 1df2fda0..e02c73ca 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -192,18 +192,23 @@ protected Config prepareConfig(Config config) { if (networkOpt.hasParkingCostArea()) { ConfigUtils.addOrGetModule(config, ParkingCostConfigGroup.class); config.planCalcScore().addActivityParams(new PlanCalcScoreConfigGroup.ActivityParams(TripStructureUtils.createStageActivityType("parking")).setScoringThisActivityAtAll(false)); + Collection modifiableCollectionOfOldStrategySettings = new ArrayList<>( config.strategy().getStrategySettings()); config.strategy().clearStrategySettings(); - StrategyConfigGroup.StrategySettings stratSetsForPErson = new StrategyConfigGroup.StrategySettings(); - stratSetsForPErson.setWeight(1.); - stratSetsForPErson.setStrategyName(RE_ROUTE_LEIPZIG); - stratSetsForPErson.setSubpopulation("person"); - config.strategy().addStrategySettings(stratSetsForPErson); - StrategyConfigGroup.StrategySettings stratSetsForFreight = new StrategyConfigGroup.StrategySettings(); - stratSetsForFreight.setWeight(1.); - stratSetsForFreight.setStrategyName(RE_ROUTE_LEIPZIG); - stratSetsForFreight.setSubpopulation("freight"); - config.strategy().addStrategySettings(stratSetsForFreight); - config.controler().setLastIteration(1); + Iterator iterator = modifiableCollectionOfOldStrategySettings.iterator(); + + while (iterator.hasNext()) { + StrategyConfigGroup.StrategySettings strategySetting = iterator.next(); + if (strategySetting.getStrategyName().equals("ReRoute")) { + StrategyConfigGroup.StrategySettings newReRouteStrategy = new StrategyConfigGroup.StrategySettings(); + newReRouteStrategy.setStrategyName(RE_ROUTE_LEIPZIG); + newReRouteStrategy.setSubpopulation(strategySetting.getSubpopulation()); + newReRouteStrategy.setWeight(strategySetting.getWeight()); + newReRouteStrategy.setDisableAfter(strategySetting.getDisableAfter()); + config.strategy().addStrategySettings(newReRouteStrategy); + } else { + config.strategy().addStrategySettings(strategySetting); + } + } // this is how it is supposed to be //config.facilities().setFacilitiesSource(FacilitiesConfigGroup.FacilitiesSource.onePerActivityLinkInPlansFile); config.facilities().setFacilitiesSource(FacilitiesConfigGroup.FacilitiesSource.none); @@ -282,6 +287,7 @@ public void install() { schedules.addBinding().toInstance(new StrategyWeightFadeout.Schedule(DefaultPlanStrategiesModule.DefaultStrategy.SubtourModeChoice, "person", 0.65, 0.80)); // Fades out until 0.9 (innovation switch off) + //TODO switch no new ReRoute!!!! schedules.addBinding().toInstance(new StrategyWeightFadeout.Schedule(DefaultPlanStrategiesModule.DefaultStrategy.ReRoute, "person", 0.75)); schedules.addBinding().toInstance(new StrategyWeightFadeout.Schedule(DefaultPlanStrategiesModule.DefaultStrategy.TimeAllocationMutator, "person", 0.75)); From b6b75e1dec382dc60ad48116297e2200793f970a Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Mon, 5 Jun 2023 12:34:47 +0200 Subject: [PATCH 044/106] set name of new ReRouteStrategy in LeipzigRoutingStrategyProvider --- .../org/matsim/run/LeipzigRoutingStrategyProvider.java | 1 + src/main/java/org/matsim/run/RunLeipzigScenario.java | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java b/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java index eeba13fd..10397f79 100644 --- a/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java +++ b/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java @@ -23,6 +23,7 @@ * The algorithm includes a logic for parking vehicles in a specific area. */ public class LeipzigRoutingStrategyProvider implements Provider { + public static final String STRATEGY_NAME = "ReRouteLeipzig"; // is a provider in matsim core. maybe try without. kai, apr'23 @Inject private GlobalConfigGroup globalConfigGroup; diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index e02c73ca..1a8972d3 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -95,7 +95,6 @@ public class RunLeipzigScenario extends MATSimApplication { private static final Logger log = LogManager.getLogger(RunLeipzigScenario.class); - static final String RE_ROUTE_LEIPZIG = "ReRouteLeipzig"; static final String VERSION = "1.1"; @CommandLine.Mixin @@ -200,7 +199,7 @@ protected Config prepareConfig(Config config) { StrategyConfigGroup.StrategySettings strategySetting = iterator.next(); if (strategySetting.getStrategyName().equals("ReRoute")) { StrategyConfigGroup.StrategySettings newReRouteStrategy = new StrategyConfigGroup.StrategySettings(); - newReRouteStrategy.setStrategyName(RE_ROUTE_LEIPZIG); + newReRouteStrategy.setStrategyName(LeipzigRoutingStrategyProvider.STRATEGY_NAME); newReRouteStrategy.setSubpopulation(strategySetting.getSubpopulation()); newReRouteStrategy.setWeight(strategySetting.getWeight()); newReRouteStrategy.setDisableAfter(strategySetting.getDisableAfter()); @@ -248,7 +247,7 @@ protected void prepareControler(Controler controler) { @Override public void install() { // this.bind( MultimodalLinkChooser.class ).toInstance( new LeipzigMultimodalLinkChooser() ); - this.addPlanStrategyBinding(RE_ROUTE_LEIPZIG).toProvider(LeipzigRoutingStrategyProvider.class); + this.addPlanStrategyBinding(LeipzigRoutingStrategyProvider.STRATEGY_NAME).toProvider(LeipzigRoutingStrategyProvider.class); // yyyy this only uses it during replanning!!! kai, apr'23 } }); @@ -288,7 +287,7 @@ public void install() { // Fades out until 0.9 (innovation switch off) //TODO switch no new ReRoute!!!! - schedules.addBinding().toInstance(new StrategyWeightFadeout.Schedule(DefaultPlanStrategiesModule.DefaultStrategy.ReRoute, "person", 0.75)); + schedules.addBinding().toInstance(new StrategyWeightFadeout.Schedule(LeipzigRoutingStrategyProvider.STRATEGY_NAME, "person", 0.75)); schedules.addBinding().toInstance(new StrategyWeightFadeout.Schedule(DefaultPlanStrategiesModule.DefaultStrategy.TimeAllocationMutator, "person", 0.75)); bind(new TypeLiteral>() { From 009acc695907ccd1b7d0a572c9bbda44092b3a6b Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Mon, 5 Jun 2023 12:42:30 +0200 Subject: [PATCH 045/106] remove logging statements meant for debugging and add todo to the run class --- .../org/matsim/run/LeipzigRouterPlanAlgorithm.java | 13 +------------ .../java/org/matsim/run/RunLeipzigScenario.java | 1 + 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index 1fff7863..6f009a9d 100644 --- a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -59,18 +59,12 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ reducedNetwork.addLink( link ); } } - log.warn( "returning LeipzigPlanRouter" ); } @Override public void run( final Plan plan ){ final List trips = TripStructureUtils.getTrips( plan ); TimeTracker timeTracker = new TimeTracker( timeInterpretation ); - log.warn( "=== old plan: ===" ); - for ( PlanElement tripElement : plan.getPlanElements() ){ - log.warn( tripElement ); - } - log.warn( "======" ); for ( TripStructureUtils.Trip oldTrip : trips ){ final String routingMode = TripStructureUtils.identifyMainMode( oldTrip.getTripElements() ); @@ -79,7 +73,7 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ final Facility fromFacility = FacilitiesUtils.toFacility( oldTrip.getOriginActivity(), facilities ); final Facility toFacility = FacilitiesUtils.toFacility( oldTrip.getDestinationActivity(), facilities ); - log.warn("fromFacility=" + fromFacility); + // System.exit(-1); // At this point, I only want to deal with residential parking. Shopping comes later (and is simpler). @@ -169,11 +163,6 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ } } - log.warn( "=== new plan: " + plan.getPerson().getId() + " ===" ); - for ( PlanElement tripElement : plan.getPlanElements() ){ - log.warn( tripElement ); - } - log.warn( "======" ); } diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index 1a8972d3..a13ac307 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -191,6 +191,7 @@ protected Config prepareConfig(Config config) { if (networkOpt.hasParkingCostArea()) { ConfigUtils.addOrGetModule(config, ParkingCostConfigGroup.class); config.planCalcScore().addActivityParams(new PlanCalcScoreConfigGroup.ActivityParams(TripStructureUtils.createStageActivityType("parking")).setScoringThisActivityAtAll(false)); + //TODO put into a static method Collection modifiableCollectionOfOldStrategySettings = new ArrayList<>( config.strategy().getStrategySettings()); config.strategy().clearStrategySettings(); Iterator iterator = modifiableCollectionOfOldStrategySettings.iterator(); From ce2d73c9481048ed11cc985b5291307edf0d25f8 Mon Sep 17 00:00:00 2001 From: rakow Date: Mon, 5 Jun 2023 13:04:38 +0200 Subject: [PATCH 046/106] add leipzig prepare person for parking --- .../org/matsim/run/RunLeipzigScenario.java | 15 +- .../run/SubtourModeChoiceInclParking.java | 57 --- .../run/parking/PreparePersonForParking.java | 470 ++++++++++++++++++ 3 files changed, 476 insertions(+), 66 deletions(-) delete mode 100644 src/main/java/org/matsim/run/SubtourModeChoiceInclParking.java create mode 100644 src/main/java/org/matsim/run/parking/PreparePersonForParking.java diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index e02c73ca..bbd93aa5 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -50,6 +50,7 @@ import org.matsim.core.config.groups.*; import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; +import org.matsim.core.controler.PrepareForSim; import org.matsim.core.replanning.choosers.ForceInnovationStrategyChooser; import org.matsim.core.replanning.choosers.StrategyChooser; import org.matsim.core.replanning.strategies.DefaultPlanStrategiesModule; @@ -66,6 +67,7 @@ import org.matsim.optDRT.MultiModeOptDrtConfigGroup; import org.matsim.optDRT.OptDrt; import org.matsim.optDRT.OptDrtConfigGroup; +import org.matsim.run.parking.PreparePersonForParking; import org.matsim.run.prepare.*; import org.matsim.smallScaleCommercialTrafficGeneration.CreateSmallScaleCommercialTrafficDemand; import picocli.CommandLine; @@ -244,15 +246,6 @@ protected void prepareControler(Controler controler) { Config config = controler.getConfig(); - controler.addOverridingModule(new AbstractModule() { - @Override - public void install() { -// this.bind( MultimodalLinkChooser.class ).toInstance( new LeipzigMultimodalLinkChooser() ); - this.addPlanStrategyBinding(RE_ROUTE_LEIPZIG).toProvider(LeipzigRoutingStrategyProvider.class); - // yyyy this only uses it during replanning!!! kai, apr'23 - } - }); - controler.addOverridingModule(new AbstractModule() { @Override public void install() { @@ -276,6 +269,10 @@ public void install() { if (networkOpt.hasParkingCostArea()) { addEventHandlerBinding().toInstance(new TimeRestrictedParkingCostHandler(parkingCostTimePeriodStart, parkingCostTimePeriodEnd)); install(new PersonMoneyEventsAnalysisModule()); + + bind(PrepareForSim.class).to(PreparePersonForParking.class); + + this.addPlanStrategyBinding(RE_ROUTE_LEIPZIG).toProvider(LeipzigRoutingStrategyProvider.class); } addControlerListenerBinding().to(StrategyWeightFadeout.class).in(Singleton.class); diff --git a/src/main/java/org/matsim/run/SubtourModeChoiceInclParking.java b/src/main/java/org/matsim/run/SubtourModeChoiceInclParking.java deleted file mode 100644 index f4f91382..00000000 --- a/src/main/java/org/matsim/run/SubtourModeChoiceInclParking.java +++ /dev/null @@ -1,57 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* - * * - * *********************************************************************** * - * * - * copyright : (C) 2008 by the members listed in the COPYING, * - * LICENSE and WARRANTY file. * - * email : info at matsim dot org * - * * - * *********************************************************************** * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * See also COPYING, LICENSE and WARRANTY file * - * * - * *********************************************************************** */ - -package org.matsim.run; - -import javax.inject.Inject; -import javax.inject.Provider; -import org.matsim.core.config.groups.GlobalConfigGroup; -import org.matsim.core.config.groups.SubtourModeChoiceConfigGroup; -import org.matsim.core.population.algorithms.PermissibleModesCalculator; -import org.matsim.core.replanning.PlanStrategy; -import org.matsim.core.replanning.PlanStrategyImpl; -import org.matsim.core.replanning.PlanStrategyImpl.Builder; -import org.matsim.core.replanning.modules.ReRoute; -import org.matsim.core.replanning.selectors.RandomPlanSelector; -import org.matsim.core.router.TripRouter; -import org.matsim.core.utils.timing.TimeInterpretation; -import org.matsim.facilities.ActivityFacilities; - -/** - * Scenario-related implementation of SMC which includes a first, rather complicated parking logic. - */ -public class SubtourModeChoiceInclParking implements Provider { - - @Inject private Provider tripRouterProvider; - @Inject private GlobalConfigGroup globalConfigGroup; - @Inject private SubtourModeChoiceConfigGroup subtourModeChoiceConfigGroup; - @Inject private ActivityFacilities facilities; - @Inject private PermissibleModesCalculator permissibleModesCalculator; - @Inject private TimeInterpretation timeInterpretation; - - @Override - public PlanStrategy get() { - PlanStrategyImpl.Builder builder = new Builder(new RandomPlanSelector<>()); - builder.addStrategyModule(new org.matsim.core.replanning.modules.SubtourModeChoice(globalConfigGroup, subtourModeChoiceConfigGroup, permissibleModesCalculator)); -// builder.addStrategyModule( new ParkingCorrectionMultithreadedModule( globalConfigGroup ) ); - builder.addStrategyModule(new ReRoute(facilities, tripRouterProvider, globalConfigGroup, timeInterpretation)); - return builder.build(); - } - -} diff --git a/src/main/java/org/matsim/run/parking/PreparePersonForParking.java b/src/main/java/org/matsim/run/parking/PreparePersonForParking.java new file mode 100644 index 00000000..30171f6d --- /dev/null +++ b/src/main/java/org/matsim/run/parking/PreparePersonForParking.java @@ -0,0 +1,470 @@ + +/* *********************************************************************** * + * project: org.matsim.* + * PrepareForSimImpl.java + * * + * *********************************************************************** * + * * + * copyright : (C) 2019 by the members listed in the COPYING, * + * LICENSE and WARRANTY file. * + * email : info at matsim dot org * + * * + * *********************************************************************** * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * See also COPYING, LICENSE and WARRANTY file * + * * + * *********************************************************************** */ + +package org.matsim.run.parking; + + +import com.google.inject.Inject; +import com.google.inject.Provider; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.api.core.v01.population.Population; +import org.matsim.core.config.groups.FacilitiesConfigGroup; +import org.matsim.core.config.groups.GlobalConfigGroup; +import org.matsim.core.config.groups.PlansConfigGroup; +import org.matsim.core.config.groups.PlansConfigGroup.HandlingOfPlansWithoutRoutingMode; +import org.matsim.core.config.groups.QSimConfigGroup; +import org.matsim.core.controler.PrepareForMobsim; +import org.matsim.core.controler.PrepareForSim; +import org.matsim.core.controler.XY2LinksForFacilities; +import org.matsim.core.gbl.Gbl; +import org.matsim.core.network.NetworkUtils; +import org.matsim.core.network.algorithms.TransportModeNetworkFilter; +import org.matsim.core.population.algorithms.ParallelPersonAlgorithmUtils; +import org.matsim.core.population.algorithms.PersonPrepareForSim; +import org.matsim.core.population.routes.NetworkRoute; +import org.matsim.core.router.MainModeIdentifier; +import org.matsim.core.router.PlanRouter; +import org.matsim.core.router.TripRouter; +import org.matsim.core.router.TripStructureUtils; +import org.matsim.core.router.TripStructureUtils.Trip; +import org.matsim.core.scenario.Lockable; +import org.matsim.core.utils.collections.Tuple; +import org.matsim.core.utils.timing.TimeInterpretation; +import org.matsim.facilities.ActivityFacilities; +import org.matsim.facilities.FacilitiesFromPopulation; +import org.matsim.vehicles.Vehicle; +import org.matsim.vehicles.VehicleType; +import org.matsim.vehicles.VehicleUtils; + +import java.util.*; +import java.util.stream.Collectors; + +import static org.matsim.core.config.groups.QSimConfigGroup.VehiclesSource.modeVehicleTypesFromVehiclesData; + +public final class PreparePersonForParking implements PrepareForSim, PrepareForMobsim { + // I think it is ok to have this public final. Since one may want to use it as a delegate. kai, may'18 + // but how should that work with a non-public constructor? kai, jun'18 + // Well, I guess it can be injected as well?! + // bind( PrepareForSimImpl.class ) ; + // bind( PrepareForSim.class ).to( MyPrepareForSimImpl.class ) ; + + private static Logger log = LogManager.getLogger(PrepareForSim.class); + + private final GlobalConfigGroup globalConfigGroup; + private final Scenario scenario; + private final Network network; + private final Population population; + private final ActivityFacilities activityFacilities; + private final Provider tripRouterProvider; + private final QSimConfigGroup qSimConfigGroup; + private final FacilitiesConfigGroup facilitiesConfigGroup; + private final PlansConfigGroup plansConfigGroup; + private final MainModeIdentifier backwardCompatibilityMainModeIdentifier; + private final TimeInterpretation timeInterpretation; + + /** + * backwardCompatibilityMainModeIdentifier should be a separate MainModeidentifier, neither the routing mode identifier from TripStructureUtils, + * nor the AnalysisMainModeidentifier used for analysis (ModeStats etc.). + */ + @Inject + PreparePersonForParking(GlobalConfigGroup globalConfigGroup, Scenario scenario, Network network, + Population population, ActivityFacilities activityFacilities, Provider tripRouterProvider, + QSimConfigGroup qSimConfigGroup, FacilitiesConfigGroup facilitiesConfigGroup, + PlansConfigGroup plansConfigGroup, + MainModeIdentifier backwardCompatibilityMainModeIdentifier, + TimeInterpretation timeInterpretation) { + this.globalConfigGroup = globalConfigGroup; + this.scenario = scenario; + this.network = network; + this.population = population; + this.activityFacilities = activityFacilities; + this.tripRouterProvider = tripRouterProvider; + this.qSimConfigGroup = qSimConfigGroup; + this.facilitiesConfigGroup = facilitiesConfigGroup; + this.plansConfigGroup = plansConfigGroup; + this.backwardCompatibilityMainModeIdentifier = backwardCompatibilityMainModeIdentifier; + this.timeInterpretation = timeInterpretation; + } + + + @Override + public void run() { + /* + * Create single-mode network here and hand it over to PersonPrepareForSim. Otherwise, each instance would create its + * own single-mode network. However, this assumes that the main mode is car - which PersonPrepareForSim also does. Should + * be probably adapted in a way that other main modes are possible as well. cdobler, oct'15. + */ + final Network carOnlyNetwork; + if (NetworkUtils.isMultimodal(network)) { + log.info("Network seems to be multimodal. Create car-only network which is handed over to PersonPrepareForSim."); + TransportModeNetworkFilter filter = new TransportModeNetworkFilter(network); + carOnlyNetwork = NetworkUtils.createNetwork(scenario.getConfig().network()); + HashSet modes = new HashSet<>(); + modes.add(TransportMode.car); + filter.filter(carOnlyNetwork, modes); + } else { + carOnlyNetwork = network; + } + + //matsim-724 + switch(this.facilitiesConfigGroup.getFacilitiesSource()){ + case none: +// Gbl.assertIf( this.activityFacilities.getFacilities().isEmpty() ); + // I have at least one use case where people use the facilities as some kind + // of database for stuff, but don't run the activities off them. I have thus + // disabled the above check. yy We need to think about what we want to + // do in such cases; might want to auto-generate our facilities as below + // and _add_ them to the existing facilities. kai, feb'18 + break; + case fromFile: + case setInScenario: + Gbl.assertIf(! this.activityFacilities.getFacilities().isEmpty() ); + break; + case onePerActivityLinkInPlansFile: + /* fall-through */ // switch is inside "FacilitiesFromPopulation" method! + case onePerActivityLocationInPlansFile: +// FacilitiesFromPopulation facilitiesFromPopulation = new FacilitiesFromPopulation(activityFacilities, facilitiesConfigGroup); + FacilitiesFromPopulation facilitiesFromPopulation = new FacilitiesFromPopulation(scenario); + +// facilitiesFromPopulation.setAssignLinksToFacilitiesIfMissing(true, network); + // (yy not sure if the false setting makes sense at all. kai, jul'18) + +// facilitiesFromPopulation.assignOpeningTimes(facilitiesConfigGroup.isAssigningOpeningTime(), scenario.getConfig().planCalcScore()); + facilitiesFromPopulation.run(population); + // Note that location choice, when switched on, should now either use the facilities generated here, + // or come with explicit pre-existing facilities. kai, jul'18 + break; + default: + throw new RuntimeException("Facilities source '"+this.facilitiesConfigGroup.getFacilitiesSource()+"' is not implemented."); + } + + // get links for facilities + // using car only network to get the links for facilities. Amit July'18 + XY2LinksForFacilities.run(carOnlyNetwork, this.activityFacilities); + + // yyyy from a behavioral perspective, the vehicle must be somehow linked to + // the person (maybe via the household). kai, feb'18 + // each agent receives a vehicle for each main mode now. janek, aug'19 + createAndAddVehiclesForEveryNetworkMode(); + + adaptOutdatedPlansForRoutingMode(); + + // FIXME: Run Leipzig parking router + // TODO yyyyyyyyyy + + + // make sure all routes are calculated. + // the above creation of vehicles per agent has to be run before executing the initial routing here. janek, aug'19 + // At least xy2links is needed here, i.e. earlier than PrepareForMobsimImpl. It could, however, presumably be separated out + // (i.e. we introduce a separate PersonPrepareForMobsim). kai, jul'18 + ParallelPersonAlgorithmUtils.run(population, globalConfigGroup.getNumberOfThreads(), + () -> new PersonPrepareForSim(new PlanRouter(tripRouterProvider.get(), activityFacilities, timeInterpretation), scenario, + carOnlyNetwork) + ); + + if (scenario instanceof Lockable) { + ((Lockable)scenario).setLocked(); + // see comment in ScenarioImpl. kai, sep'14 + } + + if (population instanceof Lockable) { + ((Lockable) population).setLocked(); + } + + if ( network instanceof Lockable ) { + ((Lockable) network).setLocked(); + } + + if (activityFacilities instanceof Lockable) { + ((Lockable) activityFacilities).setLocked(); + } + + // (yyyy means that if someone replaces prepareForSim and does not add the above lines, the containers are not locked. kai, nov'16) + } + + // only warn once that legacy vehicle id is used + private static boolean hasWarned = false; + + private void createAndAddVehiclesForEveryNetworkMode() { + + final Map modeVehicleTypes = getVehicleTypesForAllNetworkAndMainModes(); + + for (Person person : scenario.getPopulation().getPersons().values()) { + + var modeToVehicle = modeVehicleTypes.entrySet().stream() + // map mode type to vehicle id + .map(modeType -> Tuple.of(modeType, createVehicleId(person, modeType.getKey()))) + // create a corresponding vehicle + .peek(tuple -> createAndAddVehicleIfNecessary(tuple.getSecond(), tuple.getFirst().getValue())) + // write mode-string to vehicle-id into a map + .collect(Collectors.toMap(tuple -> tuple.getFirst().getKey(), Tuple::getSecond)); + + VehicleUtils.insertVehicleIdsIntoAttributes(person, modeToVehicle); + } + } + + private Id createVehicleId(Person person, String modeType) { + + if (qSimConfigGroup.getUsePersonIdForMissingVehicleId() && TransportMode.car.equals(modeType)) { + if (!hasWarned) { + log.warn("'usePersonIdForMissingVehicleId' is deprecated. It will be removed soon."); + hasWarned = true; + } + + return Id.createVehicleId(person.getId()); + } + + return VehicleUtils.createVehicleId(person, modeType); + } + + private Map getVehicleTypesForAllNetworkAndMainModes() { + + Map modeVehicleTypes = new HashMap<>(); + + if (qSimConfigGroup.getVehiclesSource().equals(QSimConfigGroup.VehiclesSource.fromVehiclesData)) { + // in this case the user has to do everything on their own and we can short circuit here. + return modeVehicleTypes; + } + + Set modes = new HashSet<>(qSimConfigGroup.getMainModes()); + modes.addAll(scenario.getConfig().plansCalcRoute().getNetworkModes()); + + for (String mode : modes) { + VehicleType type; + switch (qSimConfigGroup.getVehiclesSource()) { + case defaultVehicle: + type = VehicleUtils.getDefaultVehicleType(); + if (!scenario.getVehicles().getVehicleTypes().containsKey(type.getId())){ + scenario.getVehicles().addVehicleType( type ); + } + break; + case modeVehicleTypesFromVehiclesData: + type = scenario.getVehicles().getVehicleTypes().get(Id.create(mode, VehicleType.class)); + if ( type==null ) { + log.fatal( "Could not find requested vehicle type =" + mode + ". With config setting " + modeVehicleTypesFromVehiclesData.toString() + ", you need"); + log.fatal( "to add, for each mode that performs network routing and/or is used as network/main mode in the qsim, a vehicle type for that mode." ); + throw new RuntimeException("Could not find requested vehicle type = " + mode + ". See above."); + } + break; + default: + throw new RuntimeException(qSimConfigGroup.getVehiclesSource().toString() + " is not implemented yet."); + } + Gbl.assertNotNull(type); + modeVehicleTypes.put(mode, type); + } + return modeVehicleTypes; + } + + private void createAndAddVehicleIfNecessary(Id vehicleId, VehicleType vehicleType) { + + if (!scenario.getVehicles().getVehicles().containsKey(vehicleId)) { + + switch (qSimConfigGroup.getVehiclesSource()) { + case defaultVehicle: + case modeVehicleTypesFromVehiclesData: + Vehicle vehicle = scenario.getVehicles().getFactory().createVehicle(vehicleId, vehicleType); + scenario.getVehicles().addVehicle(vehicle); + break; + default: + throw new RuntimeException("Expecting a vehicle id which is missing in the vehicles database: " + vehicleId); + } + } + } + + private static boolean insistingOnPlansWithoutRoutingModeLogWarnNotShownYet = true; + + private void adaptOutdatedPlansForRoutingMode() { + for (Person person : population.getPersons().values()) { + for (Plan plan : person.getPlans()) { + for (Trip trip : TripStructureUtils.getTrips(plan.getPlanElements())) { + List legs = trip.getLegsOnly(); + if (legs.size() >= 1) { + String routingMode = TripStructureUtils.getRoutingMode(legs.get(0)); + + for (Leg leg : legs) { + // 1. check all legs either have the same routing mode or all have routingMode==null + if (TripStructureUtils.getRoutingMode(leg) == null) { + if (routingMode != null) { + String errorMessage = "Found a mixed trip having some legs with routingMode set and others without. " + + "This is inconsistent. Agent id: " + person.getId().toString() + + "\nTrip: " + trip.getTripElements().toString(); + log.error(errorMessage); + throw new RuntimeException(errorMessage); + } + + } else { + if (routingMode.equals(TripStructureUtils.getRoutingMode(leg))) { + TripStructureUtils.setRoutingMode(leg, routingMode); + } else { + String errorMessage = "Found a trip whose legs have different routingModes. " + + "This is inconsistent. Agent id: " + person.getId().toString() + + "\nTrip: " + trip.getTripElements().toString(); + log.error(errorMessage); + throw new RuntimeException(errorMessage); + } + } + } + + // add routing mode + if (routingMode == null) { + if (legs.size() == 1) { + // there is only a single leg (e.g. after Trips2Legs and a mode choice replanning + // module) + + String oldMainMode = replaceOutdatedFallbackModesAndReturnOldMainMode(legs.get(0), + null); + if (oldMainMode != null) { + routingMode = oldMainMode; + TripStructureUtils.setRoutingMode(legs.get(0), routingMode); + } else { + // leg has a real mode (not an outdated fallback mode) + routingMode = legs.get(0).getMode(); + TripStructureUtils.setRoutingMode(legs.get(0), routingMode); + } + } else { + if (plansConfigGroup.getHandlingOfPlansWithoutRoutingMode().equals(HandlingOfPlansWithoutRoutingMode.useMainModeIdentifier)) { + for (Leg leg : legs) { + replaceOutdatedAccessEgressWalkModes(leg, routingMode); + } + routingMode = getAndAddRoutingModeFromBackwardCompatibilityMainModeIdentifier( + person, trip); + } else { + String errorMessage = "Found a trip with multiple legs and no routingMode. " + + "Person id " + person.getId().toString() + + "\nTrip: " + trip.getTripElements().toString() + + "\nTerminating. Take care to inject an adequate MainModeIdentifier and set config switch " + + "plansConfigGroup.setHandlingOfPlansWithoutRoutingMode(" + + HandlingOfPlansWithoutRoutingMode.useMainModeIdentifier.toString() + ")."; + log.error(errorMessage); + throw new RuntimeException(errorMessage); + } + } + } + + for (Leg leg : legs) { + // check before replaceOutdatedAccessEgressHelperModes + if (leg.getMode().equals(TransportMode.walk) && leg.getRoute() instanceof NetworkRoute) { + log.error( + "Found a walk leg with a NetworkRoute. This is the only allowed use case of having " + + "non_network_walk as an access/egress mode. PrepareForSimImpl replaces " + + "non_network_walk with walk, because access/egress to modes other than walk should " + + "use the walk Router. If this causes any problem please report to gleich or kai -nov'19"); + } + } + + for (Leg leg : legs) { + replaceOutdatedAccessEgressWalkModes(leg, routingMode); + replaceOutdatedNonNetworkWalk(leg, routingMode); + replaceOutdatedFallbackModesAndReturnOldMainMode(leg, routingMode); + } + } + } + } + } + } + + private String getAndAddRoutingModeFromBackwardCompatibilityMainModeIdentifier(Person person, Trip trip) { + String routingMode; + if (insistingOnPlansWithoutRoutingModeLogWarnNotShownYet) { + log.warn( + "Insisting on using backward compatibility MainModeIdentifier instead of setting routingMode directly."); + insistingOnPlansWithoutRoutingModeLogWarnNotShownYet = false; + } + if (backwardCompatibilityMainModeIdentifier == null) { + log.error( + "Found a trip without routingMode, but there is no MainModeIdentifier set up for PrepareForSim, so cannot infer the routing mode from a MainModeIdentifier. Trip: " + + trip.getTripElements()); + throw new RuntimeException("no MainModeIdentifier set up for PrepareForSim"); + } + routingMode = backwardCompatibilityMainModeIdentifier.identifyMainMode(trip.getTripElements()); + if (routingMode != null) { + for (Leg leg : trip.getLegsOnly()) { + TripStructureUtils.setRoutingMode(leg, routingMode); + } + } else { + String errorMessage = "Found a trip whose legs had no routingMode. " + + "The backwardCompatibilityMainModeIdentifier could not identify the mode. " + "Agent id: " + + person.getId().toString() + "\nTrip: " + trip.getTripElements().toString(); + log.error(errorMessage); + throw new RuntimeException(errorMessage); + } + return routingMode; + } + + private void replaceOutdatedAccessEgressWalkModes(Leg leg, String routingMode) { + // access_walk and egress_walk were replaced by non_network_walk + if (leg.getMode().equals("access_walk") || leg.getMode().equals("egress_walk")) { + leg.setMode(TransportMode.non_network_walk); + TripStructureUtils.setRoutingMode(leg, routingMode); + } + } + + // non_network_walk as access/egress to modes other than walk on the network was replaced by walk. - + // kn/gl-nov'19 + private void replaceOutdatedNonNetworkWalk(Leg leg, String routingMode) { + if (leg.getMode().equals(TransportMode.non_network_walk)) { + leg.setMode(TransportMode.walk); + TripStructureUtils.setRoutingMode(leg, routingMode); + } + } + + /** + * Method to replace outdated TransportModes such as drt_fallback or transit_walk. + * Some of those were also used as access/egress to pt/drt helper modes. + * + * @param routingMode new routingMode which will be set at the leg + * @return null if no fallback mode was found or the main mode of the fallback mode found + */ + private String replaceOutdatedFallbackModesAndReturnOldMainMode(Leg leg, String routingMode) { + // transit_walk was replaced by walk (formerly fallback and access/egress/transfer to pt mode) + if (leg.getMode().equals(TransportMode.transit_walk)) { + leg.setMode(TransportMode.walk); + TripStructureUtils.setRoutingMode(leg, routingMode); + return TransportMode.pt; + } + + // replace drt_walk etc. (formerly fallback and access/egress to drt modes) + if (leg.getMode().endsWith("_walk") && !leg.getMode().equals(TransportMode.non_network_walk)) { + String oldMainMode = leg.getMode().substring(0, leg.getMode().length() - 5); + leg.setMode(TransportMode.walk); + TripStructureUtils.setRoutingMode(leg, routingMode); + return oldMainMode; + } + + // replace drt_fallback etc. (formerly fallback for drt modes) + if (leg.getMode().endsWith("_fallback")) { + String oldMainMode = leg.getMode().substring(0, leg.getMode().length() - 9); + leg.setMode(TransportMode.walk); + TripStructureUtils.setRoutingMode(leg, routingMode); + return oldMainMode; + } + + return null; + } +} From 0aca2928b72f13d39fec49148d12d76b8b618e6f Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Tue, 6 Jun 2023 20:26:01 +0200 Subject: [PATCH 047/106] modified PreparePersonForParking so we have the special parking logic already in iteration 0 --- .../PreparePersonForParking.java | 44 +++++++++++++------ .../org/matsim/run/RunLeipzigScenario.java | 3 -- 2 files changed, 30 insertions(+), 17 deletions(-) rename src/main/java/org/matsim/run/{parking => }/PreparePersonForParking.java (91%) diff --git a/src/main/java/org/matsim/run/parking/PreparePersonForParking.java b/src/main/java/org/matsim/run/PreparePersonForParking.java similarity index 91% rename from src/main/java/org/matsim/run/parking/PreparePersonForParking.java rename to src/main/java/org/matsim/run/PreparePersonForParking.java index 30171f6d..1dbdc315 100644 --- a/src/main/java/org/matsim/run/parking/PreparePersonForParking.java +++ b/src/main/java/org/matsim/run/PreparePersonForParking.java @@ -19,21 +19,20 @@ * * * *********************************************************************** */ -package org.matsim.run.parking; +package org.matsim.run; import com.google.inject.Inject; import com.google.inject.Provider; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.poi.ss.formula.functions.Single; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; -import org.matsim.api.core.v01.population.Leg; -import org.matsim.api.core.v01.population.Person; -import org.matsim.api.core.v01.population.Plan; -import org.matsim.api.core.v01.population.Population; +import org.matsim.api.core.v01.population.*; import org.matsim.core.config.groups.FacilitiesConfigGroup; import org.matsim.core.config.groups.GlobalConfigGroup; import org.matsim.core.config.groups.PlansConfigGroup; @@ -48,16 +47,12 @@ import org.matsim.core.population.algorithms.ParallelPersonAlgorithmUtils; import org.matsim.core.population.algorithms.PersonPrepareForSim; import org.matsim.core.population.routes.NetworkRoute; -import org.matsim.core.router.MainModeIdentifier; -import org.matsim.core.router.PlanRouter; -import org.matsim.core.router.TripRouter; -import org.matsim.core.router.TripStructureUtils; +import org.matsim.core.router.*; import org.matsim.core.router.TripStructureUtils.Trip; import org.matsim.core.scenario.Lockable; import org.matsim.core.utils.collections.Tuple; import org.matsim.core.utils.timing.TimeInterpretation; -import org.matsim.facilities.ActivityFacilities; -import org.matsim.facilities.FacilitiesFromPopulation; +import org.matsim.facilities.*; import org.matsim.vehicles.Vehicle; import org.matsim.vehicles.VehicleType; import org.matsim.vehicles.VehicleUtils; @@ -87,6 +82,7 @@ public final class PreparePersonForParking implements PrepareForSim, PrepareForM private final PlansConfigGroup plansConfigGroup; private final MainModeIdentifier backwardCompatibilityMainModeIdentifier; private final TimeInterpretation timeInterpretation; + private final SingleModeNetworksCache singleModeNetworksCache; /** * backwardCompatibilityMainModeIdentifier should be a separate MainModeidentifier, neither the routing mode identifier from TripStructureUtils, @@ -98,7 +94,7 @@ public final class PreparePersonForParking implements PrepareForSim, PrepareForM QSimConfigGroup qSimConfigGroup, FacilitiesConfigGroup facilitiesConfigGroup, PlansConfigGroup plansConfigGroup, MainModeIdentifier backwardCompatibilityMainModeIdentifier, - TimeInterpretation timeInterpretation) { + TimeInterpretation timeInterpretation, SingleModeNetworksCache singleModeNetworkCache) { this.globalConfigGroup = globalConfigGroup; this.scenario = scenario; this.network = network; @@ -110,6 +106,7 @@ public final class PreparePersonForParking implements PrepareForSim, PrepareForM this.plansConfigGroup = plansConfigGroup; this.backwardCompatibilityMainModeIdentifier = backwardCompatibilityMainModeIdentifier; this.timeInterpretation = timeInterpretation; + this.singleModeNetworksCache = singleModeNetworkCache; } @@ -175,8 +172,27 @@ public void run() { adaptOutdatedPlansForRoutingMode(); - // FIXME: Run Leipzig parking router - // TODO yyyyyyyyyy + MultimodalLinkChooser carfreeMultimodalLinkChooser = new CarfreeMultimodalLinkChooser(); + + //this is somehow needed as otherwise the activities will have no link assigned to them --> isLinkParkingTypeInsideResidentialArea will fail + for (Person person: population.getPersons().values()) { + for (PlanElement elements : person.getSelectedPlan().getPlanElements()) { + if (elements instanceof Activity) { + Id link = FacilitiesUtils.decideOnLinkId(FacilitiesUtils.toFacility((Activity) elements, activityFacilities), carOnlyNetwork); + ((Activity) elements).setLinkId(link); + //Facility facility = FacilitiesUtils.toFacility((Activity) elements, activityFacilities); + //ActivityFacility activityFacility = new ActivityFacilityImpl().createAndAddActivityOption(); + //activityFacilities.addActivityFacility((ActivityFacility) facility); + //FacilitiesUtils.setLinkID( facility,link); + } + } + } + + // we run this so even in iteration 0 we will have parking integrated + LeipzigRouterPlanAlgorithm leipzigRouterPlanAlgorithm = new LeipzigRouterPlanAlgorithm(tripRouterProvider.get(), activityFacilities, timeInterpretation,singleModeNetworksCache,scenario, carfreeMultimodalLinkChooser); + for (Person person: population.getPersons().values()) { + leipzigRouterPlanAlgorithm.run(person.getSelectedPlan()); + } // make sure all routes are calculated. diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index 5f15f34c..7c9a47a6 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -67,7 +67,6 @@ import org.matsim.optDRT.MultiModeOptDrtConfigGroup; import org.matsim.optDRT.OptDrt; import org.matsim.optDRT.OptDrtConfigGroup; -import org.matsim.run.parking.PreparePersonForParking; import org.matsim.run.prepare.*; import org.matsim.smallScaleCommercialTrafficGeneration.CreateSmallScaleCommercialTrafficDemand; import picocli.CommandLine; @@ -269,9 +268,7 @@ public void install() { if (networkOpt.hasParkingCostArea()) { addEventHandlerBinding().toInstance(new TimeRestrictedParkingCostHandler(parkingCostTimePeriodStart, parkingCostTimePeriodEnd)); install(new PersonMoneyEventsAnalysisModule()); - bind(PrepareForSim.class).to(PreparePersonForParking.class); - this.addPlanStrategyBinding(LeipzigRoutingStrategyProvider.STRATEGY_NAME).toProvider(LeipzigRoutingStrategyProvider.class); } From 55e0a507b9b3fbc2c8af30516369d18d3fc56a7a Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Mon, 12 Jun 2023 12:23:58 +0200 Subject: [PATCH 048/106] fixed some style issues and suppressed the rest --- .../matsim/run/PreparePersonForParking.java | 100 ++++++++++-------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/src/main/java/org/matsim/run/PreparePersonForParking.java b/src/main/java/org/matsim/run/PreparePersonForParking.java index 1dbdc315..5fe4712e 100644 --- a/src/main/java/org/matsim/run/PreparePersonForParking.java +++ b/src/main/java/org/matsim/run/PreparePersonForParking.java @@ -26,7 +26,6 @@ import com.google.inject.Provider; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.apache.poi.ss.formula.functions.Single; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.TransportMode; @@ -52,7 +51,9 @@ import org.matsim.core.scenario.Lockable; import org.matsim.core.utils.collections.Tuple; import org.matsim.core.utils.timing.TimeInterpretation; -import org.matsim.facilities.*; +import org.matsim.facilities.ActivityFacilities; +import org.matsim.facilities.FacilitiesFromPopulation; +import org.matsim.facilities.FacilitiesUtils; import org.matsim.vehicles.Vehicle; import org.matsim.vehicles.VehicleType; import org.matsim.vehicles.VehicleUtils; @@ -62,6 +63,11 @@ import static org.matsim.core.config.groups.QSimConfigGroup.VehiclesSource.modeVehicleTypesFromVehiclesData; +/** +We needed to make a copy (of PreparePersonForSimImpl) here because in iteration zero our LeipzigRouterPlanAlgorithm was not used, and we need to have it + here because in certain policy cases we need to delete the routes initially. + **/ +@SuppressWarnings({"TrailingComment", "DeclarationOrder"}) public final class PreparePersonForParking implements PrepareForSim, PrepareForMobsim { // I think it is ok to have this public final. Since one may want to use it as a delegate. kai, may'18 // but how should that work with a non-public constructor? kai, jun'18 @@ -69,7 +75,7 @@ public final class PreparePersonForParking implements PrepareForSim, PrepareForM // bind( PrepareForSimImpl.class ) ; // bind( PrepareForSim.class ).to( MyPrepareForSimImpl.class ) ; - private static Logger log = LogManager.getLogger(PrepareForSim.class); + private static final Logger log = LogManager.getLogger(PrepareForSim.class); private final GlobalConfigGroup globalConfigGroup; private final Scenario scenario; @@ -122,7 +128,7 @@ public void run() { log.info("Network seems to be multimodal. Create car-only network which is handed over to PersonPrepareForSim."); TransportModeNetworkFilter filter = new TransportModeNetworkFilter(network); carOnlyNetwork = NetworkUtils.createNetwork(scenario.getConfig().network()); - HashSet modes = new HashSet<>(); + Set modes = new HashSet<>(); modes.add(TransportMode.car); filter.filter(carOnlyNetwork, modes); } else { @@ -130,7 +136,7 @@ public void run() { } //matsim-724 - switch(this.facilitiesConfigGroup.getFacilitiesSource()){ + switch (this.facilitiesConfigGroup.getFacilitiesSource()) { case none: // Gbl.assertIf( this.activityFacilities.getFacilities().isEmpty() ); // I have at least one use case where people use the facilities as some kind @@ -141,7 +147,7 @@ public void run() { break; case fromFile: case setInScenario: - Gbl.assertIf(! this.activityFacilities.getFacilities().isEmpty() ); + Gbl.assertIf(!this.activityFacilities.getFacilities().isEmpty()); break; case onePerActivityLinkInPlansFile: /* fall-through */ // switch is inside "FacilitiesFromPopulation" method! @@ -158,7 +164,7 @@ public void run() { // or come with explicit pre-existing facilities. kai, jul'18 break; default: - throw new RuntimeException("Facilities source '"+this.facilitiesConfigGroup.getFacilitiesSource()+"' is not implemented."); + throw new RuntimeException("Facilities source '" + this.facilitiesConfigGroup.getFacilitiesSource() + "' is not implemented."); } // get links for facilities @@ -175,8 +181,8 @@ public void run() { MultimodalLinkChooser carfreeMultimodalLinkChooser = new CarfreeMultimodalLinkChooser(); //this is somehow needed as otherwise the activities will have no link assigned to them --> isLinkParkingTypeInsideResidentialArea will fail - for (Person person: population.getPersons().values()) { - for (PlanElement elements : person.getSelectedPlan().getPlanElements()) { + for (Person person : population.getPersons().values()) { + for (PlanElement elements : person.getSelectedPlan().getPlanElements()) { if (elements instanceof Activity) { Id link = FacilitiesUtils.decideOnLinkId(FacilitiesUtils.toFacility((Activity) elements, activityFacilities), carOnlyNetwork); ((Activity) elements).setLinkId(link); @@ -189,8 +195,8 @@ public void run() { } // we run this so even in iteration 0 we will have parking integrated - LeipzigRouterPlanAlgorithm leipzigRouterPlanAlgorithm = new LeipzigRouterPlanAlgorithm(tripRouterProvider.get(), activityFacilities, timeInterpretation,singleModeNetworksCache,scenario, carfreeMultimodalLinkChooser); - for (Person person: population.getPersons().values()) { + LeipzigRouterPlanAlgorithm leipzigRouterPlanAlgorithm = new LeipzigRouterPlanAlgorithm(tripRouterProvider.get(), activityFacilities, timeInterpretation, singleModeNetworksCache, scenario, carfreeMultimodalLinkChooser); + for (Person person : population.getPersons().values()) { leipzigRouterPlanAlgorithm.run(person.getSelectedPlan()); } @@ -200,12 +206,12 @@ public void run() { // At least xy2links is needed here, i.e. earlier than PrepareForMobsimImpl. It could, however, presumably be separated out // (i.e. we introduce a separate PersonPrepareForMobsim). kai, jul'18 ParallelPersonAlgorithmUtils.run(population, globalConfigGroup.getNumberOfThreads(), - () -> new PersonPrepareForSim(new PlanRouter(tripRouterProvider.get(), activityFacilities, timeInterpretation), scenario, - carOnlyNetwork) + () -> new PersonPrepareForSim(new PlanRouter(tripRouterProvider.get(), activityFacilities, timeInterpretation), scenario, + carOnlyNetwork) ); if (scenario instanceof Lockable) { - ((Lockable)scenario).setLocked(); + ((Lockable) scenario).setLocked(); // see comment in ScenarioImpl. kai, sep'14 } @@ -213,11 +219,11 @@ public void run() { ((Lockable) population).setLocked(); } - if ( network instanceof Lockable ) { + if (network instanceof Lockable) { ((Lockable) network).setLocked(); } - if (activityFacilities instanceof Lockable) { + if (activityFacilities instanceof Lockable) { ((Lockable) activityFacilities).setLocked(); } @@ -234,12 +240,12 @@ private void createAndAddVehiclesForEveryNetworkMode() { for (Person person : scenario.getPopulation().getPersons().values()) { var modeToVehicle = modeVehicleTypes.entrySet().stream() - // map mode type to vehicle id - .map(modeType -> Tuple.of(modeType, createVehicleId(person, modeType.getKey()))) - // create a corresponding vehicle - .peek(tuple -> createAndAddVehicleIfNecessary(tuple.getSecond(), tuple.getFirst().getValue())) - // write mode-string to vehicle-id into a map - .collect(Collectors.toMap(tuple -> tuple.getFirst().getKey(), Tuple::getSecond)); + // map mode type to vehicle id + .map(modeType -> Tuple.of(modeType, createVehicleId(person, modeType.getKey()))) + // create a corresponding vehicle + .peek(tuple -> createAndAddVehicleIfNecessary(tuple.getSecond(), tuple.getFirst().getValue())) + // write mode-string to vehicle-id into a map + .collect(Collectors.toMap(tuple -> tuple.getFirst().getKey(), Tuple::getSecond)); VehicleUtils.insertVehicleIdsIntoAttributes(person, modeToVehicle); } @@ -276,15 +282,15 @@ private Map getVehicleTypesForAllNetworkAndMainModes() { switch (qSimConfigGroup.getVehiclesSource()) { case defaultVehicle: type = VehicleUtils.getDefaultVehicleType(); - if (!scenario.getVehicles().getVehicleTypes().containsKey(type.getId())){ - scenario.getVehicles().addVehicleType( type ); + if (!scenario.getVehicles().getVehicleTypes().containsKey(type.getId())) { + scenario.getVehicles().addVehicleType(type); } break; case modeVehicleTypesFromVehiclesData: type = scenario.getVehicles().getVehicleTypes().get(Id.create(mode, VehicleType.class)); - if ( type==null ) { - log.fatal( "Could not find requested vehicle type =" + mode + ". With config setting " + modeVehicleTypesFromVehiclesData.toString() + ", you need"); - log.fatal( "to add, for each mode that performs network routing and/or is used as network/main mode in the qsim, a vehicle type for that mode." ); + if (type == null) { + log.fatal("Could not find requested vehicle type =" + mode + ". With config setting " + modeVehicleTypesFromVehiclesData.toString() + ", you need"); + log.fatal("to add, for each mode that performs network routing and/or is used as network/main mode in the qsim, a vehicle type for that mode."); throw new RuntimeException("Could not find requested vehicle type = " + mode + ". See above."); } break; @@ -328,8 +334,8 @@ private void adaptOutdatedPlansForRoutingMode() { if (TripStructureUtils.getRoutingMode(leg) == null) { if (routingMode != null) { String errorMessage = "Found a mixed trip having some legs with routingMode set and others without. " - + "This is inconsistent. Agent id: " + person.getId().toString() - + "\nTrip: " + trip.getTripElements().toString(); + + "This is inconsistent. Agent id: " + person.getId().toString() + + "\nTrip: " + trip.getTripElements().toString(); log.error(errorMessage); throw new RuntimeException(errorMessage); } @@ -339,8 +345,8 @@ private void adaptOutdatedPlansForRoutingMode() { TripStructureUtils.setRoutingMode(leg, routingMode); } else { String errorMessage = "Found a trip whose legs have different routingModes. " - + "This is inconsistent. Agent id: " + person.getId().toString() - + "\nTrip: " + trip.getTripElements().toString(); + + "This is inconsistent. Agent id: " + person.getId().toString() + + "\nTrip: " + trip.getTripElements().toString(); log.error(errorMessage); throw new RuntimeException(errorMessage); } @@ -354,7 +360,7 @@ private void adaptOutdatedPlansForRoutingMode() { // module) String oldMainMode = replaceOutdatedFallbackModesAndReturnOldMainMode(legs.get(0), - null); + null); if (oldMainMode != null) { routingMode = oldMainMode; TripStructureUtils.setRoutingMode(legs.get(0), routingMode); @@ -369,14 +375,14 @@ private void adaptOutdatedPlansForRoutingMode() { replaceOutdatedAccessEgressWalkModes(leg, routingMode); } routingMode = getAndAddRoutingModeFromBackwardCompatibilityMainModeIdentifier( - person, trip); + person, trip); } else { String errorMessage = "Found a trip with multiple legs and no routingMode. " - + "Person id " + person.getId().toString() - + "\nTrip: " + trip.getTripElements().toString() - + "\nTerminating. Take care to inject an adequate MainModeIdentifier and set config switch " - + "plansConfigGroup.setHandlingOfPlansWithoutRoutingMode(" - + HandlingOfPlansWithoutRoutingMode.useMainModeIdentifier.toString() + ")."; + + "Person id " + person.getId().toString() + + "\nTrip: " + trip.getTripElements().toString() + + "\nTerminating. Take care to inject an adequate MainModeIdentifier and set config switch " + + "plansConfigGroup.setHandlingOfPlansWithoutRoutingMode(" + + HandlingOfPlansWithoutRoutingMode.useMainModeIdentifier.toString() + ")."; log.error(errorMessage); throw new RuntimeException(errorMessage); } @@ -387,10 +393,10 @@ private void adaptOutdatedPlansForRoutingMode() { // check before replaceOutdatedAccessEgressHelperModes if (leg.getMode().equals(TransportMode.walk) && leg.getRoute() instanceof NetworkRoute) { log.error( - "Found a walk leg with a NetworkRoute. This is the only allowed use case of having " - + "non_network_walk as an access/egress mode. PrepareForSimImpl replaces " - + "non_network_walk with walk, because access/egress to modes other than walk should " - + "use the walk Router. If this causes any problem please report to gleich or kai -nov'19"); + "Found a walk leg with a NetworkRoute. This is the only allowed use case of having " + + "non_network_walk as an access/egress mode. PrepareForSimImpl replaces " + + "non_network_walk with walk, because access/egress to modes other than walk should " + + "use the walk Router. If this causes any problem please report to gleich or kai -nov'19"); } } @@ -409,13 +415,13 @@ private String getAndAddRoutingModeFromBackwardCompatibilityMainModeIdentifier(P String routingMode; if (insistingOnPlansWithoutRoutingModeLogWarnNotShownYet) { log.warn( - "Insisting on using backward compatibility MainModeIdentifier instead of setting routingMode directly."); + "Insisting on using backward compatibility MainModeIdentifier instead of setting routingMode directly."); insistingOnPlansWithoutRoutingModeLogWarnNotShownYet = false; } if (backwardCompatibilityMainModeIdentifier == null) { log.error( - "Found a trip without routingMode, but there is no MainModeIdentifier set up for PrepareForSim, so cannot infer the routing mode from a MainModeIdentifier. Trip: " - + trip.getTripElements()); + "Found a trip without routingMode, but there is no MainModeIdentifier set up for PrepareForSim, so cannot infer the routing mode from a MainModeIdentifier. Trip: " + + trip.getTripElements()); throw new RuntimeException("no MainModeIdentifier set up for PrepareForSim"); } routingMode = backwardCompatibilityMainModeIdentifier.identifyMainMode(trip.getTripElements()); @@ -425,8 +431,8 @@ private String getAndAddRoutingModeFromBackwardCompatibilityMainModeIdentifier(P } } else { String errorMessage = "Found a trip whose legs had no routingMode. " - + "The backwardCompatibilityMainModeIdentifier could not identify the mode. " + "Agent id: " - + person.getId().toString() + "\nTrip: " + trip.getTripElements().toString(); + + "The backwardCompatibilityMainModeIdentifier could not identify the mode. " + "Agent id: " + + person.getId().toString() + "\nTrip: " + trip.getTripElements().toString(); log.error(errorMessage); throw new RuntimeException(errorMessage); } From f4f0687d5a6c4721dfee242739260cf00743ec98 Mon Sep 17 00:00:00 2001 From: simei94 Date: Mon, 19 Jun 2023 11:46:56 +0200 Subject: [PATCH 049/106] fix wrong import --- .../java/org/matsim/run/LeipzigRoutingStrategyProvider.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java b/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java index 10397f79..d45406c5 100644 --- a/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java +++ b/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java @@ -1,6 +1,7 @@ package org.matsim.run; import com.google.inject.Inject; +import com.google.inject.Provider; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Plan; @@ -16,8 +17,6 @@ import org.matsim.core.utils.timing.TimeInterpretation; import org.matsim.facilities.ActivityFacilities; -import javax.inject.Provider; - /** * This class installs the specific routing algorithm, which is implemented in the Leipzig scenario. * The algorithm includes a logic for parking vehicles in a specific area. From 40e7999f1b208b9349dd213e3ccd0549f429d8c9 Mon Sep 17 00:00:00 2001 From: rakow Date: Mon, 19 Jun 2023 15:53:35 +0200 Subject: [PATCH 050/106] cleanup --- .../run/LeipzigRouterPlanAlgorithm.java | 206 ++++---- .../run/LeipzigRoutingStrategyProvider.java | 4 +- .../run/LinkAttributeNetworkLinkFilter.java | 28 - .../ParkingCorrectionMultithreadedModule.java | 100 ---- .../matsim/run/PreparePersonForParking.java | 492 ------------------ .../org/matsim/run/RunLeipzigScenario.java | 22 +- .../run/TimeRestrictedParkingCostHandler.java | 4 +- 7 files changed, 115 insertions(+), 741 deletions(-) delete mode 100644 src/main/java/org/matsim/run/LinkAttributeNetworkLinkFilter.java delete mode 100644 src/main/java/org/matsim/run/ParkingCorrectionMultithreadedModule.java delete mode 100644 src/main/java/org/matsim/run/PreparePersonForParking.java diff --git a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index 6f009a9d..cfb803cc 100644 --- a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -1,5 +1,6 @@ package org.matsim.run; +import com.google.inject.Inject; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.Scenario; @@ -7,10 +8,8 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.network.Node; -import org.matsim.api.core.v01.population.Activity; -import org.matsim.api.core.v01.population.Leg; -import org.matsim.api.core.v01.population.Plan; -import org.matsim.api.core.v01.population.PlanElement; +import org.matsim.api.core.v01.population.*; +import org.matsim.core.controler.PersonPrepareForSimAlgorithm; import org.matsim.core.network.NetworkUtils; import org.matsim.core.population.algorithms.PlanAlgorithm; import org.matsim.core.router.MultimodalLinkChooser; @@ -30,8 +29,8 @@ import static org.matsim.core.router.PlanRouter.putVehicleFromOldTripIntoNewTripIfMeaningful; import static org.matsim.run.LeipzigUtils.isLinkParkingTypeInsideResidentialArea; -final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ - private static final Logger log = LogManager.getLogger(LeipzigRouterPlanAlgorithm.class ); +final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm, PersonPrepareForSimAlgorithm { + private static final Logger log = LogManager.getLogger(LeipzigRouterPlanAlgorithm.class); private final TripRouter tripRouter; private final ActivityFacilities facilities; private final TimeInterpretation timeInterpretation; @@ -40,110 +39,138 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ private final MultimodalLinkChooser linkChooser; private final Scenario scenario; - LeipzigRouterPlanAlgorithm( final TripRouter tripRouter, final ActivityFacilities facilities, final TimeInterpretation timeInterpretation, - SingleModeNetworksCache singleModeNetworksCache, Scenario scenario, MultimodalLinkChooser linkChooser ){ + @Inject + LeipzigRouterPlanAlgorithm(final TripRouter tripRouter, final ActivityFacilities facilities, final TimeInterpretation timeInterpretation, + SingleModeNetworksCache singleModeNetworksCache, Scenario scenario, MultimodalLinkChooser linkChooser) { this.tripRouter = tripRouter; this.facilities = facilities; this.timeInterpretation = timeInterpretation; this.scenario = scenario; - this.fullModalNetwork = singleModeNetworksCache.getSingleModeNetworksCache().get( TransportMode.car ); + this.fullModalNetwork = singleModeNetworksCache.getSingleModeNetworksCache().get(TransportMode.car); // yyyy one should look at the networks cache and see how the following is done. And maybe even register it there. - this.reducedNetwork = NetworkUtils.createNetwork( scenario.getConfig().network() ); + this.reducedNetwork = NetworkUtils.createNetwork(scenario.getConfig().network()); this.linkChooser = linkChooser; - for ( Node node : this.fullModalNetwork.getNodes().values() ){ - reducedNetwork.addNode( node ); + for (Node node : this.fullModalNetwork.getNodes().values()) { + reducedNetwork.addNode(node); } - for ( Link link : this.fullModalNetwork.getLinks().values() ){ - if ( !LeipzigUtils.isLinkParkingTypeInsideResidentialArea( link ) ){ - reducedNetwork.addLink( link ); + for (Link link : this.fullModalNetwork.getLinks().values()) { + if (!LeipzigUtils.isLinkParkingTypeInsideResidentialArea(link)) { + reducedNetwork.addLink(link); } } } - @Override public void run( final Plan plan ){ - final List trips = TripStructureUtils.getTrips( plan ); - TimeTracker timeTracker = new TimeTracker( timeInterpretation ); + private static LeipzigUtils.PersonParkingBehaviour getParkingBehaviour(Network fullModalNetwork, Activity originActivity, Facility originFacility, String routingMode) { + LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.defaultLogic; + + // if we find out that there are time restrictions on all the links + //originActivity.getEndTime(); + if (routingMode.equals(TransportMode.car)) { - for ( TripStructureUtils.Trip oldTrip : trips ){ - final String routingMode = TripStructureUtils.identifyMainMode( oldTrip.getTripElements() ); - timeTracker.addActivity( oldTrip.getOriginActivity() ); + // an dieser stelle waere es besser abzufragen, ob die person in der naehe wohnt anstatt nur die home act -> residential parking zuzuordnen + // check if non-home activity (since otherwise we assume that there is no parking restriction): + if (!originActivity.getType().equals(ActivityTypes.HOME)) { - final Facility fromFacility = FacilitiesUtils.toFacility( oldTrip.getOriginActivity(), facilities ); - final Facility toFacility = FacilitiesUtils.toFacility( oldTrip.getDestinationActivity(), facilities ); + Link link = fullModalNetwork.getLinks().get(originActivity.getLinkId()); + if (isLinkParkingTypeInsideResidentialArea(link)) { + parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig; + // if (originActivity.getType().equals(ActivityTypes.SHOPPING)) { + // // change this to parking type normal/ unrestricted + // // on activity link, unrestricted + // parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.shopping; + // } + } + } + } + return parkingBehaviourAtOrigin; + } + + @Override + public void run(final Plan plan) { + final List trips = TripStructureUtils.getTrips(plan); + TimeTracker timeTracker = new TimeTracker(timeInterpretation); + + + for (TripStructureUtils.Trip oldTrip : trips) { + final String routingMode = TripStructureUtils.identifyMainMode(oldTrip.getTripElements()); + timeTracker.addActivity(oldTrip.getOriginActivity()); + + final Facility fromFacility = FacilitiesUtils.toFacility(oldTrip.getOriginActivity(), facilities); + final Facility toFacility = FacilitiesUtils.toFacility(oldTrip.getDestinationActivity(), facilities); // System.exit(-1); // At this point, I only want to deal with residential parking. Shopping comes later (and is simpler). - LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtOrigin = getParkingBehaviour( fullModalNetwork, oldTrip.getOriginActivity(), fromFacility, routingMode ); - LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtDestination = getParkingBehaviour( fullModalNetwork, oldTrip.getDestinationActivity(), toFacility, routingMode ); + LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtOrigin = getParkingBehaviour(fullModalNetwork, oldTrip.getOriginActivity(), fromFacility, routingMode); + LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtDestination = getParkingBehaviour(fullModalNetwork, oldTrip.getDestinationActivity(), toFacility, routingMode); - if ( parkingBehaviourAtOrigin == LeipzigUtils.PersonParkingBehaviour.defaultLogic - && parkingBehaviourAtDestination == LeipzigUtils.PersonParkingBehaviour.defaultLogic){ + if (parkingBehaviourAtOrigin == LeipzigUtils.PersonParkingBehaviour.defaultLogic + && parkingBehaviourAtDestination == LeipzigUtils.PersonParkingBehaviour.defaultLogic) { // standard case: - final List newTripElements = tripRouter.calcRoute( routingMode, fromFacility, toFacility, - timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); - putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); - TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity() ); - timeTracker.addElements( newTripElements ); + final List newTripElements = tripRouter.calcRoute(routingMode, fromFacility, toFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes()); + putVehicleFromOldTripIntoNewTripIfMeaningful(oldTrip, newTripElements); + TripRouter.insertTrip(plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity()); + timeTracker.addElements(newTripElements); - } else if ( parkingBehaviourAtOrigin == LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig - && parkingBehaviourAtDestination == LeipzigUtils.PersonParkingBehaviour.defaultLogic){ + } else if (parkingBehaviourAtOrigin == LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig + && parkingBehaviourAtDestination == LeipzigUtils.PersonParkingBehaviour.defaultLogic) { - TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), - createTripForParkingAtOrigin(oldTrip, routingMode, fromFacility, toFacility, plan, timeTracker), oldTrip.getDestinationActivity() ); + TripRouter.insertTrip(plan, oldTrip.getOriginActivity(), + createTripForParkingAtOrigin(oldTrip, routingMode, fromFacility, toFacility, plan, timeTracker), oldTrip.getDestinationActivity()); } else if (parkingBehaviourAtOrigin == LeipzigUtils.PersonParkingBehaviour.defaultLogic - && parkingBehaviourAtDestination == LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig) { + && parkingBehaviourAtDestination == LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig) { TripRouter.insertTrip(plan, oldTrip.getOriginActivity(), - createTripForParkingAtDestination(oldTrip, routingMode, fromFacility, toFacility, plan, timeTracker), oldTrip.getDestinationActivity()); + createTripForParkingAtDestination(oldTrip, routingMode, fromFacility, toFacility, plan, timeTracker), oldTrip.getDestinationActivity()); } else if (parkingBehaviourAtOrigin == LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig - && parkingBehaviourAtDestination == LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig){ + && parkingBehaviourAtDestination == LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig) { // restricted parking at origin: // first find parking: // final Link parkingLink = NetworkUtils.getNearestLink( reducedNetwork, oldTrip.getOriginActivity().getCoord() ); - final Link originParkingLink = linkChooser.decideOnLink( fromFacility, reducedNetwork ); + final Link originParkingLink = linkChooser.decideOnLink(fromFacility, reducedNetwork); final Activity originParkingActivity = scenario.getPopulation().getFactory().createInteractionActivityFromLinkId( - TripStructureUtils.createStageActivityType( "parking" ), originParkingLink.getId() ); - final Facility originParkingFacility = FacilitiesUtils.toFacility( originParkingActivity, facilities ); + TripStructureUtils.createStageActivityType("parking"), originParkingLink.getId()); + final Facility originParkingFacility = FacilitiesUtils.toFacility(originParkingActivity, facilities); //parking at destination final Link destinationParkingLink = linkChooser.decideOnLink(toFacility, reducedNetwork); final Activity destinationParkingActivity = scenario.getPopulation().getFactory().createInteractionActivityFromLinkId( - TripStructureUtils.createStageActivityType("parking"), destinationParkingLink.getId()); + TripStructureUtils.createStageActivityType("parking"), destinationParkingLink.getId()); final Facility destinationParkingFacility = FacilitiesUtils.toFacility(destinationParkingActivity, facilities); // trip from origin to originParking: - final List originWalkTripElements = tripRouter.calcRoute( TransportMode.walk, fromFacility, originParkingFacility, - timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); - for ( PlanElement tripElement : originWalkTripElements ){ - if ( tripElement instanceof Leg ) { - TripStructureUtils.setRoutingMode( (Leg) tripElement, TransportMode.car ); + final List originWalkTripElements = tripRouter.calcRoute(TransportMode.walk, fromFacility, originParkingFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes()); + for (PlanElement tripElement : originWalkTripElements) { + if (tripElement instanceof Leg) { + TripStructureUtils.setRoutingMode((Leg) tripElement, TransportMode.car); } } List newTripElements = new ArrayList<>(originWalkTripElements); // originParking interaction: - newTripElements.add( originParkingActivity ); + newTripElements.add(originParkingActivity); // trip from originParking to destinationParking: - final List carTripElements = tripRouter.calcRoute( routingMode, originParkingFacility, destinationParkingFacility, - timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); - newTripElements.addAll( carTripElements ); + final List carTripElements = tripRouter.calcRoute(routingMode, originParkingFacility, destinationParkingFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes()); + newTripElements.addAll(carTripElements); newTripElements.add(destinationParkingActivity); // trip from destinationParking to destination: final List destinationWalkTripElements = tripRouter.calcRoute(TransportMode.walk, destinationParkingFacility, toFacility, - timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes()); + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes()); for (PlanElement tripElement : destinationWalkTripElements) { if (tripElement instanceof Leg) { TripStructureUtils.setRoutingMode((Leg) tripElement, TransportMode.car); @@ -152,10 +179,10 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ newTripElements.addAll(destinationWalkTripElements); - putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); - timeTracker.addElements( newTripElements ); + putVehicleFromOldTripIntoNewTripIfMeaningful(oldTrip, newTripElements); + timeTracker.addElements(newTripElements); - TripRouter.insertTrip( plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity() ); + TripRouter.insertTrip(plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity()); } else { throw new RuntimeException(); @@ -166,62 +193,36 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm{ } - private static LeipzigUtils.PersonParkingBehaviour getParkingBehaviour(Network fullModalNetwork, Activity originActivity, Facility originFacility, String routingMode){ - LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.defaultLogic; - - // if we find out that there are time restrictions on all the links - //originActivity.getEndTime(); - - if ( routingMode.equals(TransportMode.car) ) { - - // an dieser stelle waere es besser abzufragen, ob die person in der naehe wohnt anstatt nur die home act -> residential parking zuzuordnen - // check if non-home activity (since otherwise we assume that there is no parking restriction): - if ( !originActivity.getType().equals( ActivityTypes.HOME ) ){ - - Link link = fullModalNetwork.getLinks().get( originActivity.getLinkId() ); - if ( isLinkParkingTypeInsideResidentialArea( link ) ){ - parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig; - // if (originActivity.getType().equals(ActivityTypes.SHOPPING)) { - // // change this to parking type normal/ unrestricted - // // on activity link, unrestricted - // parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.shopping; - // } - } - } - } - return parkingBehaviourAtOrigin; - } - private List createTripForParkingAtOrigin(TripStructureUtils.Trip oldTrip, String routingMode, Facility fromFacility, Facility toFacility, Plan plan, TimeTracker timeTracker) { // restricted parking at origin: // first find parking: // final Link parkingLink = NetworkUtils.getNearestLink( reducedNetwork, oldTrip.getOriginActivity().getCoord() ); - final Link parkingLink = linkChooser.decideOnLink( fromFacility, reducedNetwork ); + final Link parkingLink = linkChooser.decideOnLink(fromFacility, reducedNetwork); final Activity parkingActivity = scenario.getPopulation().getFactory().createInteractionActivityFromLinkId( - TripStructureUtils.createStageActivityType( "parking" ), parkingLink.getId() ); - final Facility parkingFacility = FacilitiesUtils.toFacility( parkingActivity, facilities ); + TripStructureUtils.createStageActivityType("parking"), parkingLink.getId()); + final Facility parkingFacility = FacilitiesUtils.toFacility(parkingActivity, facilities); // trip from origin to parking: - final List walkTripElements = tripRouter.calcRoute( TransportMode.walk, fromFacility, parkingFacility, - timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); - for ( PlanElement tripElement : walkTripElements ){ - if ( tripElement instanceof Leg ) { - TripStructureUtils.setRoutingMode( (Leg) tripElement, TransportMode.car ); + final List walkTripElements = tripRouter.calcRoute(TransportMode.walk, fromFacility, parkingFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes()); + for (PlanElement tripElement : walkTripElements) { + if (tripElement instanceof Leg) { + TripStructureUtils.setRoutingMode((Leg) tripElement, TransportMode.car); } } List newTripElements = new ArrayList<>(walkTripElements); // parking interaction: - newTripElements.add( parkingActivity ); + newTripElements.add(parkingActivity); // trip from parking to final destination: - final List carTripElements = tripRouter.calcRoute( routingMode, parkingFacility, toFacility, - timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes() ); - newTripElements.addAll( carTripElements ); + final List carTripElements = tripRouter.calcRoute(routingMode, parkingFacility, toFacility, + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes()); + newTripElements.addAll(carTripElements); - putVehicleFromOldTripIntoNewTripIfMeaningful( oldTrip, newTripElements ); - timeTracker.addElements( newTripElements ); + putVehicleFromOldTripIntoNewTripIfMeaningful(oldTrip, newTripElements); + timeTracker.addElements(newTripElements); return newTripElements; } @@ -231,12 +232,12 @@ private List createTripForParkingAtDestination(TripStructureUtils.T //parking at destination final Link parkingLink = linkChooser.decideOnLink(toFacility, reducedNetwork); final Activity parkingActivity = scenario.getPopulation().getFactory().createInteractionActivityFromLinkId( - TripStructureUtils.createStageActivityType("parking"), parkingLink.getId()); + TripStructureUtils.createStageActivityType("parking"), parkingLink.getId()); final Facility parkingFacility = FacilitiesUtils.toFacility(parkingActivity, facilities); // trip from origin to parking: final List carTripElements = tripRouter.calcRoute(routingMode, fromFacility, parkingFacility, - timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes()); + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes()); // parking interaction: List newTripElements = new ArrayList<>(carTripElements); @@ -244,7 +245,7 @@ private List createTripForParkingAtDestination(TripStructureUtils.T // trip from parking to destination: final List walkTripElements = tripRouter.calcRoute(TransportMode.walk, parkingFacility, toFacility, - timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes()); + timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes()); for (PlanElement tripElement : walkTripElements) { if (tripElement instanceof Leg) { TripStructureUtils.setRoutingMode((Leg) tripElement, TransportMode.car); @@ -259,5 +260,10 @@ private List createTripForParkingAtDestination(TripStructureUtils.T return newTripElements; } - + @Override + public void run(Person person) { + for (Plan plan : person.getPlans()) { + run(plan); + } + } } diff --git a/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java b/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java index d45406c5..f22605b0 100644 --- a/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java +++ b/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java @@ -3,8 +3,6 @@ import com.google.inject.Inject; import com.google.inject.Provider; import org.matsim.api.core.v01.Scenario; -import org.matsim.api.core.v01.population.Person; -import org.matsim.api.core.v01.population.Plan; import org.matsim.core.config.groups.GlobalConfigGroup; import org.matsim.core.population.algorithms.PlanAlgorithm; import org.matsim.core.replanning.PlanStrategy; @@ -41,7 +39,7 @@ public class LeipzigRoutingStrategyProvider implements Provider { @Override public PlanStrategy get() { - PlanStrategyImpl.Builder builder = new PlanStrategyImpl.Builder(new RandomPlanSelector()); + PlanStrategyImpl.Builder builder = new PlanStrategyImpl.Builder(new RandomPlanSelector<>()); builder.addStrategyModule(new AbstractMultithreadedModule(globalConfigGroup) { @Override public PlanAlgorithm getPlanAlgoInstance() { diff --git a/src/main/java/org/matsim/run/LinkAttributeNetworkLinkFilter.java b/src/main/java/org/matsim/run/LinkAttributeNetworkLinkFilter.java deleted file mode 100644 index 12344a21..00000000 --- a/src/main/java/org/matsim/run/LinkAttributeNetworkLinkFilter.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.matsim.run; - -import org.matsim.api.core.v01.network.Link; -import org.matsim.core.network.filter.NetworkLinkFilter; - -/** - * Filters network for links with a certain attribute. - */ -public class LinkAttributeNetworkLinkFilter implements NetworkLinkFilter { - - private final String attributeName; - private final String attribute; - - LinkAttributeNetworkLinkFilter(String attributeName, String attribute) { - - this.attributeName = attributeName; - this.attribute = attribute; - } - - - @Override - public boolean judgeLink(Link l) { - - - - return false; - } -} diff --git a/src/main/java/org/matsim/run/ParkingCorrectionMultithreadedModule.java b/src/main/java/org/matsim/run/ParkingCorrectionMultithreadedModule.java deleted file mode 100644 index 826a1290..00000000 --- a/src/main/java/org/matsim/run/ParkingCorrectionMultithreadedModule.java +++ /dev/null @@ -1,100 +0,0 @@ -//package org.matsim.run; -// -//import com.google.inject.Inject; -//import org.apache.logging.log4j.LogManager; -//import org.apache.logging.log4j.Logger; -//import org.matsim.api.core.v01.Scenario; -//import org.matsim.api.core.v01.TransportMode; -//import org.matsim.api.core.v01.network.Network; -//import org.matsim.api.core.v01.population.Plan; -//import org.matsim.api.core.v01.population.PlanElement; -//import org.matsim.api.core.v01.population.PopulationFactory; -//import org.matsim.core.config.groups.GlobalConfigGroup; -//import org.matsim.core.network.filter.NetworkFilterManager; -//import org.matsim.core.population.algorithms.PlanAlgorithm; -//import org.matsim.core.replanning.modules.AbstractMultithreadedModule; -//import org.matsim.core.router.TripRouter; -//import org.matsim.core.router.TripStructureUtils; -//import playground.vsp.simpleParkingCostHandler.ParkingCostConfigGroup; -// -//import java.util.ArrayList; -//import java.util.List; -// -//class ParkingCorrectionMultithreadedModule extends AbstractMultithreadedModule { -// -// @Inject -// Scenario scenario; -// -// @Inject -// ParkingCostConfigGroup parkingCostConfigGroup; -// -// -// private static final Logger log = LogManager.getLogger(ParkingCorrectionMultithreadedModule.class ); -// private final PopulationFactory pf; -// -// public ParkingCorrectionMultithreadedModule( GlobalConfigGroup globalConfigGroup, PopulationFactory pf ){ -// super( globalConfigGroup ); -// this.pf = pf; -// -// NetworkFilterManager managerResidential = new NetworkFilterManager(scenario.getNetwork(), scenario.getConfig().network()); -// managerResidential.addLinkFilter(new LinkAttributeNetworkLinkFilter(parkingCostConfigGroup.getResidentialParkingFeeAttributeName(), "0.")); -// -// Network nonResidentialParkingNetwork = managerResidential.applyFilters(); -// -// NetworkFilterManager managerShop = new NetworkFilterManager(scenario.getNetwork(), scenario.getConfig().network()); -// //this is kind of ugly, but otherwise we would need a shp file here to filter for the links with shopping garages -sme0423 -// //maybe put this into a facility instead of networkAttribute -// managerShop.addLinkFilter(new LinkAttributeNetworkLinkFilter("shoppingGarage", "true")); -// -// Network shoppingNetwork = managerShop.applyFilters(); -// -// -// } -// @Override public PlanAlgorithm getPlanAlgoInstance(){ -// return new PlanAlgorithm(){ -// @Override public void run( Plan plan ){ -// -// List trips = TripStructureUtils.getTrips( plan.getPlanElements() ); -// -// for( TripStructureUtils.Trip oldTrip : trips ){ -// //we dont need to check if the trip is routed because only plans affected by SMC will come here -// // -> only plans with non-routed trips -//// if ( isRouted(oldTrip ) ) { -//// continue;; -//// } -// // else check if parking is affected etc. -// -// oldTrip.getOriginActivity(); -// -// oldTrip.getDestinationActivity(); -// -// -// List newTrip = new ArrayList<>(); -// -//// newTrip.add( oldTrip.getOriginActivity() ); -// -// newTrip.add( pf.createLeg( TransportMode.walk ) ); -// -// newTrip.add( pf.createInteractionActivityFromLinkId( ... ) ); -// -// newTrip .add( pf.createLeg( TransportMode.car ); -// -// newTrip.add( pf.createInteractionActivityFromLinkId( ... ) ); -// -// newTrip.add( pf.createLeg( )) -// -//// newTrip.add( oldTrip.getDestinationActivity() ); -// -// -// TripRouter.insertTrip( -// plan, -// oldTrip.getOriginActivity(), -// newTrip, -// oldTrip.getDestinationActivity() ); -// -// } -// -// } -// }; -// } -//} diff --git a/src/main/java/org/matsim/run/PreparePersonForParking.java b/src/main/java/org/matsim/run/PreparePersonForParking.java deleted file mode 100644 index 5fe4712e..00000000 --- a/src/main/java/org/matsim/run/PreparePersonForParking.java +++ /dev/null @@ -1,492 +0,0 @@ - -/* *********************************************************************** * - * project: org.matsim.* - * PrepareForSimImpl.java - * * - * *********************************************************************** * - * * - * copyright : (C) 2019 by the members listed in the COPYING, * - * LICENSE and WARRANTY file. * - * email : info at matsim dot org * - * * - * *********************************************************************** * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * See also COPYING, LICENSE and WARRANTY file * - * * - * *********************************************************************** */ - -package org.matsim.run; - - -import com.google.inject.Inject; -import com.google.inject.Provider; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.Scenario; -import org.matsim.api.core.v01.TransportMode; -import org.matsim.api.core.v01.network.Link; -import org.matsim.api.core.v01.network.Network; -import org.matsim.api.core.v01.population.*; -import org.matsim.core.config.groups.FacilitiesConfigGroup; -import org.matsim.core.config.groups.GlobalConfigGroup; -import org.matsim.core.config.groups.PlansConfigGroup; -import org.matsim.core.config.groups.PlansConfigGroup.HandlingOfPlansWithoutRoutingMode; -import org.matsim.core.config.groups.QSimConfigGroup; -import org.matsim.core.controler.PrepareForMobsim; -import org.matsim.core.controler.PrepareForSim; -import org.matsim.core.controler.XY2LinksForFacilities; -import org.matsim.core.gbl.Gbl; -import org.matsim.core.network.NetworkUtils; -import org.matsim.core.network.algorithms.TransportModeNetworkFilter; -import org.matsim.core.population.algorithms.ParallelPersonAlgorithmUtils; -import org.matsim.core.population.algorithms.PersonPrepareForSim; -import org.matsim.core.population.routes.NetworkRoute; -import org.matsim.core.router.*; -import org.matsim.core.router.TripStructureUtils.Trip; -import org.matsim.core.scenario.Lockable; -import org.matsim.core.utils.collections.Tuple; -import org.matsim.core.utils.timing.TimeInterpretation; -import org.matsim.facilities.ActivityFacilities; -import org.matsim.facilities.FacilitiesFromPopulation; -import org.matsim.facilities.FacilitiesUtils; -import org.matsim.vehicles.Vehicle; -import org.matsim.vehicles.VehicleType; -import org.matsim.vehicles.VehicleUtils; - -import java.util.*; -import java.util.stream.Collectors; - -import static org.matsim.core.config.groups.QSimConfigGroup.VehiclesSource.modeVehicleTypesFromVehiclesData; - -/** -We needed to make a copy (of PreparePersonForSimImpl) here because in iteration zero our LeipzigRouterPlanAlgorithm was not used, and we need to have it - here because in certain policy cases we need to delete the routes initially. - **/ -@SuppressWarnings({"TrailingComment", "DeclarationOrder"}) -public final class PreparePersonForParking implements PrepareForSim, PrepareForMobsim { - // I think it is ok to have this public final. Since one may want to use it as a delegate. kai, may'18 - // but how should that work with a non-public constructor? kai, jun'18 - // Well, I guess it can be injected as well?! - // bind( PrepareForSimImpl.class ) ; - // bind( PrepareForSim.class ).to( MyPrepareForSimImpl.class ) ; - - private static final Logger log = LogManager.getLogger(PrepareForSim.class); - - private final GlobalConfigGroup globalConfigGroup; - private final Scenario scenario; - private final Network network; - private final Population population; - private final ActivityFacilities activityFacilities; - private final Provider tripRouterProvider; - private final QSimConfigGroup qSimConfigGroup; - private final FacilitiesConfigGroup facilitiesConfigGroup; - private final PlansConfigGroup plansConfigGroup; - private final MainModeIdentifier backwardCompatibilityMainModeIdentifier; - private final TimeInterpretation timeInterpretation; - private final SingleModeNetworksCache singleModeNetworksCache; - - /** - * backwardCompatibilityMainModeIdentifier should be a separate MainModeidentifier, neither the routing mode identifier from TripStructureUtils, - * nor the AnalysisMainModeidentifier used for analysis (ModeStats etc.). - */ - @Inject - PreparePersonForParking(GlobalConfigGroup globalConfigGroup, Scenario scenario, Network network, - Population population, ActivityFacilities activityFacilities, Provider tripRouterProvider, - QSimConfigGroup qSimConfigGroup, FacilitiesConfigGroup facilitiesConfigGroup, - PlansConfigGroup plansConfigGroup, - MainModeIdentifier backwardCompatibilityMainModeIdentifier, - TimeInterpretation timeInterpretation, SingleModeNetworksCache singleModeNetworkCache) { - this.globalConfigGroup = globalConfigGroup; - this.scenario = scenario; - this.network = network; - this.population = population; - this.activityFacilities = activityFacilities; - this.tripRouterProvider = tripRouterProvider; - this.qSimConfigGroup = qSimConfigGroup; - this.facilitiesConfigGroup = facilitiesConfigGroup; - this.plansConfigGroup = plansConfigGroup; - this.backwardCompatibilityMainModeIdentifier = backwardCompatibilityMainModeIdentifier; - this.timeInterpretation = timeInterpretation; - this.singleModeNetworksCache = singleModeNetworkCache; - } - - - @Override - public void run() { - /* - * Create single-mode network here and hand it over to PersonPrepareForSim. Otherwise, each instance would create its - * own single-mode network. However, this assumes that the main mode is car - which PersonPrepareForSim also does. Should - * be probably adapted in a way that other main modes are possible as well. cdobler, oct'15. - */ - final Network carOnlyNetwork; - if (NetworkUtils.isMultimodal(network)) { - log.info("Network seems to be multimodal. Create car-only network which is handed over to PersonPrepareForSim."); - TransportModeNetworkFilter filter = new TransportModeNetworkFilter(network); - carOnlyNetwork = NetworkUtils.createNetwork(scenario.getConfig().network()); - Set modes = new HashSet<>(); - modes.add(TransportMode.car); - filter.filter(carOnlyNetwork, modes); - } else { - carOnlyNetwork = network; - } - - //matsim-724 - switch (this.facilitiesConfigGroup.getFacilitiesSource()) { - case none: -// Gbl.assertIf( this.activityFacilities.getFacilities().isEmpty() ); - // I have at least one use case where people use the facilities as some kind - // of database for stuff, but don't run the activities off them. I have thus - // disabled the above check. yy We need to think about what we want to - // do in such cases; might want to auto-generate our facilities as below - // and _add_ them to the existing facilities. kai, feb'18 - break; - case fromFile: - case setInScenario: - Gbl.assertIf(!this.activityFacilities.getFacilities().isEmpty()); - break; - case onePerActivityLinkInPlansFile: - /* fall-through */ // switch is inside "FacilitiesFromPopulation" method! - case onePerActivityLocationInPlansFile: -// FacilitiesFromPopulation facilitiesFromPopulation = new FacilitiesFromPopulation(activityFacilities, facilitiesConfigGroup); - FacilitiesFromPopulation facilitiesFromPopulation = new FacilitiesFromPopulation(scenario); - -// facilitiesFromPopulation.setAssignLinksToFacilitiesIfMissing(true, network); - // (yy not sure if the false setting makes sense at all. kai, jul'18) - -// facilitiesFromPopulation.assignOpeningTimes(facilitiesConfigGroup.isAssigningOpeningTime(), scenario.getConfig().planCalcScore()); - facilitiesFromPopulation.run(population); - // Note that location choice, when switched on, should now either use the facilities generated here, - // or come with explicit pre-existing facilities. kai, jul'18 - break; - default: - throw new RuntimeException("Facilities source '" + this.facilitiesConfigGroup.getFacilitiesSource() + "' is not implemented."); - } - - // get links for facilities - // using car only network to get the links for facilities. Amit July'18 - XY2LinksForFacilities.run(carOnlyNetwork, this.activityFacilities); - - // yyyy from a behavioral perspective, the vehicle must be somehow linked to - // the person (maybe via the household). kai, feb'18 - // each agent receives a vehicle for each main mode now. janek, aug'19 - createAndAddVehiclesForEveryNetworkMode(); - - adaptOutdatedPlansForRoutingMode(); - - MultimodalLinkChooser carfreeMultimodalLinkChooser = new CarfreeMultimodalLinkChooser(); - - //this is somehow needed as otherwise the activities will have no link assigned to them --> isLinkParkingTypeInsideResidentialArea will fail - for (Person person : population.getPersons().values()) { - for (PlanElement elements : person.getSelectedPlan().getPlanElements()) { - if (elements instanceof Activity) { - Id link = FacilitiesUtils.decideOnLinkId(FacilitiesUtils.toFacility((Activity) elements, activityFacilities), carOnlyNetwork); - ((Activity) elements).setLinkId(link); - //Facility facility = FacilitiesUtils.toFacility((Activity) elements, activityFacilities); - //ActivityFacility activityFacility = new ActivityFacilityImpl().createAndAddActivityOption(); - //activityFacilities.addActivityFacility((ActivityFacility) facility); - //FacilitiesUtils.setLinkID( facility,link); - } - } - } - - // we run this so even in iteration 0 we will have parking integrated - LeipzigRouterPlanAlgorithm leipzigRouterPlanAlgorithm = new LeipzigRouterPlanAlgorithm(tripRouterProvider.get(), activityFacilities, timeInterpretation, singleModeNetworksCache, scenario, carfreeMultimodalLinkChooser); - for (Person person : population.getPersons().values()) { - leipzigRouterPlanAlgorithm.run(person.getSelectedPlan()); - } - - - // make sure all routes are calculated. - // the above creation of vehicles per agent has to be run before executing the initial routing here. janek, aug'19 - // At least xy2links is needed here, i.e. earlier than PrepareForMobsimImpl. It could, however, presumably be separated out - // (i.e. we introduce a separate PersonPrepareForMobsim). kai, jul'18 - ParallelPersonAlgorithmUtils.run(population, globalConfigGroup.getNumberOfThreads(), - () -> new PersonPrepareForSim(new PlanRouter(tripRouterProvider.get(), activityFacilities, timeInterpretation), scenario, - carOnlyNetwork) - ); - - if (scenario instanceof Lockable) { - ((Lockable) scenario).setLocked(); - // see comment in ScenarioImpl. kai, sep'14 - } - - if (population instanceof Lockable) { - ((Lockable) population).setLocked(); - } - - if (network instanceof Lockable) { - ((Lockable) network).setLocked(); - } - - if (activityFacilities instanceof Lockable) { - ((Lockable) activityFacilities).setLocked(); - } - - // (yyyy means that if someone replaces prepareForSim and does not add the above lines, the containers are not locked. kai, nov'16) - } - - // only warn once that legacy vehicle id is used - private static boolean hasWarned = false; - - private void createAndAddVehiclesForEveryNetworkMode() { - - final Map modeVehicleTypes = getVehicleTypesForAllNetworkAndMainModes(); - - for (Person person : scenario.getPopulation().getPersons().values()) { - - var modeToVehicle = modeVehicleTypes.entrySet().stream() - // map mode type to vehicle id - .map(modeType -> Tuple.of(modeType, createVehicleId(person, modeType.getKey()))) - // create a corresponding vehicle - .peek(tuple -> createAndAddVehicleIfNecessary(tuple.getSecond(), tuple.getFirst().getValue())) - // write mode-string to vehicle-id into a map - .collect(Collectors.toMap(tuple -> tuple.getFirst().getKey(), Tuple::getSecond)); - - VehicleUtils.insertVehicleIdsIntoAttributes(person, modeToVehicle); - } - } - - private Id createVehicleId(Person person, String modeType) { - - if (qSimConfigGroup.getUsePersonIdForMissingVehicleId() && TransportMode.car.equals(modeType)) { - if (!hasWarned) { - log.warn("'usePersonIdForMissingVehicleId' is deprecated. It will be removed soon."); - hasWarned = true; - } - - return Id.createVehicleId(person.getId()); - } - - return VehicleUtils.createVehicleId(person, modeType); - } - - private Map getVehicleTypesForAllNetworkAndMainModes() { - - Map modeVehicleTypes = new HashMap<>(); - - if (qSimConfigGroup.getVehiclesSource().equals(QSimConfigGroup.VehiclesSource.fromVehiclesData)) { - // in this case the user has to do everything on their own and we can short circuit here. - return modeVehicleTypes; - } - - Set modes = new HashSet<>(qSimConfigGroup.getMainModes()); - modes.addAll(scenario.getConfig().plansCalcRoute().getNetworkModes()); - - for (String mode : modes) { - VehicleType type; - switch (qSimConfigGroup.getVehiclesSource()) { - case defaultVehicle: - type = VehicleUtils.getDefaultVehicleType(); - if (!scenario.getVehicles().getVehicleTypes().containsKey(type.getId())) { - scenario.getVehicles().addVehicleType(type); - } - break; - case modeVehicleTypesFromVehiclesData: - type = scenario.getVehicles().getVehicleTypes().get(Id.create(mode, VehicleType.class)); - if (type == null) { - log.fatal("Could not find requested vehicle type =" + mode + ". With config setting " + modeVehicleTypesFromVehiclesData.toString() + ", you need"); - log.fatal("to add, for each mode that performs network routing and/or is used as network/main mode in the qsim, a vehicle type for that mode."); - throw new RuntimeException("Could not find requested vehicle type = " + mode + ". See above."); - } - break; - default: - throw new RuntimeException(qSimConfigGroup.getVehiclesSource().toString() + " is not implemented yet."); - } - Gbl.assertNotNull(type); - modeVehicleTypes.put(mode, type); - } - return modeVehicleTypes; - } - - private void createAndAddVehicleIfNecessary(Id vehicleId, VehicleType vehicleType) { - - if (!scenario.getVehicles().getVehicles().containsKey(vehicleId)) { - - switch (qSimConfigGroup.getVehiclesSource()) { - case defaultVehicle: - case modeVehicleTypesFromVehiclesData: - Vehicle vehicle = scenario.getVehicles().getFactory().createVehicle(vehicleId, vehicleType); - scenario.getVehicles().addVehicle(vehicle); - break; - default: - throw new RuntimeException("Expecting a vehicle id which is missing in the vehicles database: " + vehicleId); - } - } - } - - private static boolean insistingOnPlansWithoutRoutingModeLogWarnNotShownYet = true; - - private void adaptOutdatedPlansForRoutingMode() { - for (Person person : population.getPersons().values()) { - for (Plan plan : person.getPlans()) { - for (Trip trip : TripStructureUtils.getTrips(plan.getPlanElements())) { - List legs = trip.getLegsOnly(); - if (legs.size() >= 1) { - String routingMode = TripStructureUtils.getRoutingMode(legs.get(0)); - - for (Leg leg : legs) { - // 1. check all legs either have the same routing mode or all have routingMode==null - if (TripStructureUtils.getRoutingMode(leg) == null) { - if (routingMode != null) { - String errorMessage = "Found a mixed trip having some legs with routingMode set and others without. " - + "This is inconsistent. Agent id: " + person.getId().toString() - + "\nTrip: " + trip.getTripElements().toString(); - log.error(errorMessage); - throw new RuntimeException(errorMessage); - } - - } else { - if (routingMode.equals(TripStructureUtils.getRoutingMode(leg))) { - TripStructureUtils.setRoutingMode(leg, routingMode); - } else { - String errorMessage = "Found a trip whose legs have different routingModes. " - + "This is inconsistent. Agent id: " + person.getId().toString() - + "\nTrip: " + trip.getTripElements().toString(); - log.error(errorMessage); - throw new RuntimeException(errorMessage); - } - } - } - - // add routing mode - if (routingMode == null) { - if (legs.size() == 1) { - // there is only a single leg (e.g. after Trips2Legs and a mode choice replanning - // module) - - String oldMainMode = replaceOutdatedFallbackModesAndReturnOldMainMode(legs.get(0), - null); - if (oldMainMode != null) { - routingMode = oldMainMode; - TripStructureUtils.setRoutingMode(legs.get(0), routingMode); - } else { - // leg has a real mode (not an outdated fallback mode) - routingMode = legs.get(0).getMode(); - TripStructureUtils.setRoutingMode(legs.get(0), routingMode); - } - } else { - if (plansConfigGroup.getHandlingOfPlansWithoutRoutingMode().equals(HandlingOfPlansWithoutRoutingMode.useMainModeIdentifier)) { - for (Leg leg : legs) { - replaceOutdatedAccessEgressWalkModes(leg, routingMode); - } - routingMode = getAndAddRoutingModeFromBackwardCompatibilityMainModeIdentifier( - person, trip); - } else { - String errorMessage = "Found a trip with multiple legs and no routingMode. " - + "Person id " + person.getId().toString() - + "\nTrip: " + trip.getTripElements().toString() - + "\nTerminating. Take care to inject an adequate MainModeIdentifier and set config switch " - + "plansConfigGroup.setHandlingOfPlansWithoutRoutingMode(" - + HandlingOfPlansWithoutRoutingMode.useMainModeIdentifier.toString() + ")."; - log.error(errorMessage); - throw new RuntimeException(errorMessage); - } - } - } - - for (Leg leg : legs) { - // check before replaceOutdatedAccessEgressHelperModes - if (leg.getMode().equals(TransportMode.walk) && leg.getRoute() instanceof NetworkRoute) { - log.error( - "Found a walk leg with a NetworkRoute. This is the only allowed use case of having " - + "non_network_walk as an access/egress mode. PrepareForSimImpl replaces " - + "non_network_walk with walk, because access/egress to modes other than walk should " - + "use the walk Router. If this causes any problem please report to gleich or kai -nov'19"); - } - } - - for (Leg leg : legs) { - replaceOutdatedAccessEgressWalkModes(leg, routingMode); - replaceOutdatedNonNetworkWalk(leg, routingMode); - replaceOutdatedFallbackModesAndReturnOldMainMode(leg, routingMode); - } - } - } - } - } - } - - private String getAndAddRoutingModeFromBackwardCompatibilityMainModeIdentifier(Person person, Trip trip) { - String routingMode; - if (insistingOnPlansWithoutRoutingModeLogWarnNotShownYet) { - log.warn( - "Insisting on using backward compatibility MainModeIdentifier instead of setting routingMode directly."); - insistingOnPlansWithoutRoutingModeLogWarnNotShownYet = false; - } - if (backwardCompatibilityMainModeIdentifier == null) { - log.error( - "Found a trip without routingMode, but there is no MainModeIdentifier set up for PrepareForSim, so cannot infer the routing mode from a MainModeIdentifier. Trip: " - + trip.getTripElements()); - throw new RuntimeException("no MainModeIdentifier set up for PrepareForSim"); - } - routingMode = backwardCompatibilityMainModeIdentifier.identifyMainMode(trip.getTripElements()); - if (routingMode != null) { - for (Leg leg : trip.getLegsOnly()) { - TripStructureUtils.setRoutingMode(leg, routingMode); - } - } else { - String errorMessage = "Found a trip whose legs had no routingMode. " - + "The backwardCompatibilityMainModeIdentifier could not identify the mode. " + "Agent id: " - + person.getId().toString() + "\nTrip: " + trip.getTripElements().toString(); - log.error(errorMessage); - throw new RuntimeException(errorMessage); - } - return routingMode; - } - - private void replaceOutdatedAccessEgressWalkModes(Leg leg, String routingMode) { - // access_walk and egress_walk were replaced by non_network_walk - if (leg.getMode().equals("access_walk") || leg.getMode().equals("egress_walk")) { - leg.setMode(TransportMode.non_network_walk); - TripStructureUtils.setRoutingMode(leg, routingMode); - } - } - - // non_network_walk as access/egress to modes other than walk on the network was replaced by walk. - - // kn/gl-nov'19 - private void replaceOutdatedNonNetworkWalk(Leg leg, String routingMode) { - if (leg.getMode().equals(TransportMode.non_network_walk)) { - leg.setMode(TransportMode.walk); - TripStructureUtils.setRoutingMode(leg, routingMode); - } - } - - /** - * Method to replace outdated TransportModes such as drt_fallback or transit_walk. - * Some of those were also used as access/egress to pt/drt helper modes. - * - * @param routingMode new routingMode which will be set at the leg - * @return null if no fallback mode was found or the main mode of the fallback mode found - */ - private String replaceOutdatedFallbackModesAndReturnOldMainMode(Leg leg, String routingMode) { - // transit_walk was replaced by walk (formerly fallback and access/egress/transfer to pt mode) - if (leg.getMode().equals(TransportMode.transit_walk)) { - leg.setMode(TransportMode.walk); - TripStructureUtils.setRoutingMode(leg, routingMode); - return TransportMode.pt; - } - - // replace drt_walk etc. (formerly fallback and access/egress to drt modes) - if (leg.getMode().endsWith("_walk") && !leg.getMode().equals(TransportMode.non_network_walk)) { - String oldMainMode = leg.getMode().substring(0, leg.getMode().length() - 5); - leg.setMode(TransportMode.walk); - TripStructureUtils.setRoutingMode(leg, routingMode); - return oldMainMode; - } - - // replace drt_fallback etc. (formerly fallback for drt modes) - if (leg.getMode().endsWith("_fallback")) { - String oldMainMode = leg.getMode().substring(0, leg.getMode().length() - 9); - leg.setMode(TransportMode.walk); - TripStructureUtils.setRoutingMode(leg, routingMode); - return oldMainMode; - } - - return null; - } -} diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index cb93edff..921126c4 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -10,14 +10,12 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.matsim.analysis.*; -import org.matsim.analysis.emissions.RunOfflineAirPollutionAnalysisByVehicleCategory; import org.matsim.analysis.personMoney.PersonMoneyEventsAnalysisModule; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.TransportMode; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Plan; -import org.matsim.application.MATSimAppCommand; import org.matsim.application.MATSimApplication; import org.matsim.application.analysis.CheckPopulation; import org.matsim.application.analysis.noise.NoiseAnalysis; @@ -48,7 +46,6 @@ import org.matsim.core.config.groups.*; import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; -import org.matsim.core.controler.PrepareForSim; import org.matsim.core.replanning.choosers.ForceInnovationStrategyChooser; import org.matsim.core.replanning.choosers.StrategyChooser; import org.matsim.core.replanning.strategies.DefaultPlanStrategiesModule; @@ -70,7 +67,6 @@ import playground.vsp.simpleParkingCostHandler.ParkingCostConfigGroup; import javax.annotation.Nullable; -import java.nio.file.Path; import java.util.*; /** @@ -95,9 +91,9 @@ public class RunLeipzigScenario extends MATSimApplication { * Coordinate system used in the scenario. */ public static final String CRS = "EPSG:25832"; - + static final String VERSION = "1.1"; - + private static final Logger log = LogManager.getLogger(RunLeipzigScenario.class); @CommandLine.Mixin @@ -109,6 +105,7 @@ public class RunLeipzigScenario extends MATSimApplication { Double relativeSpeedChange; @CommandLine.Option(names = "--bikes", defaultValue = "onNetworkWithStandardMatsim", description = "Define how bicycles are handled") private BicycleHandling bike; + //TODO: define adequate values for the following doubles @CommandLine.Option(names = "--parking-cost-time-period-start", defaultValue = "0", description = "Start of time period for which parking cost will be charged.") private Double parkingCostTimePeriodStart; @@ -287,8 +284,10 @@ public void install() { addEventHandlerBinding().toInstance(new TimeRestrictedParkingCostHandler(parkingCostTimePeriodStart, parkingCostTimePeriodEnd)); install(new PersonMoneyEventsAnalysisModule()); - bind(PrepareForSim.class).to(PreparePersonForParking.class); + + this.addPersonPrepareForSimAlgorithm().addBinding().to(LeipzigRouterPlanAlgorithm.class); this.addPlanStrategyBinding(LeipzigRoutingStrategyProvider.STRATEGY_NAME).toProvider(LeipzigRoutingStrategyProvider.class); + } // TODO FIXME yyyyyy replace by config option @@ -408,15 +407,6 @@ public void install() { modeChoiceConfigGroup.setModes(modes.toArray(new String[0])); } - @Override - protected List preparePostProcessing(Path outputFolder, String runId) { - - String hbefaFileWarm = "https://svn.vsp.tu-berlin.de/repos/public-svn/3507bb3997e5657ab9da76dbedbb13c9b5991d3e/0e73947443d68f95202b71a156b337f7f71604ae/7eff8f308633df1b8ac4d06d05180dd0c5fdf577.enc"; - String hbefaFileCold = "https://svn.vsp.tu-berlin.de/repos/public-svn/3507bb3997e5657ab9da76dbedbb13c9b5991d3e/0e73947443d68f95202b71a156b337f7f71604ae/ColdStart_Vehcat_2020_Average_withHGVetc.csv.enc"; - - return List.of(new RunOfflineAirPollutionAnalysisByVehicleCategory(outputFolder.toString(), runId, hbefaFileWarm, hbefaFileCold, outputFolder.toString())); - } - /** * Defines how bicycles are scored. */ diff --git a/src/main/java/org/matsim/run/TimeRestrictedParkingCostHandler.java b/src/main/java/org/matsim/run/TimeRestrictedParkingCostHandler.java index 1edeb333..71dd37bf 100644 --- a/src/main/java/org/matsim/run/TimeRestrictedParkingCostHandler.java +++ b/src/main/java/org/matsim/run/TimeRestrictedParkingCostHandler.java @@ -58,8 +58,8 @@ final class TimeRestrictedParkingCostHandler implements TransitDriverStartsEvent private final Set> ptDrivers = new HashSet<>(); private final Set> hasAlreadyPaidDailyResidentialParkingCosts = new HashSet<>(); private boolean isInRestrictedParkingPeriod; - private double parkingCostTimePeriodStart; - private double parkingCostTimePeriodEnd; + private final double parkingCostTimePeriodStart; + private final double parkingCostTimePeriodEnd; @Inject private EventsManager events; From ef276d02f03200fec9bb5f137e6396907ec70650 Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 20 Jun 2023 11:16:18 +0200 Subject: [PATCH 051/106] ensure xy2link in the parking preparation --- pom.xml | 2 +- .../org/matsim/run/LeipzigRouterPlanAlgorithm.java | 9 +++++++-- .../matsim/run/LeipzigRoutingStrategyProvider.java | 2 +- src/main/java/org/matsim/run/RunLeipzigScenario.java | 11 +++++------ 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index 15afa266..320c70bd 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.matsim matsim-all - 16.0-PR2632 + 16.0-PR2653 4.0.0 diff --git a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index cfb803cc..85125520 100644 --- a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -12,6 +12,7 @@ import org.matsim.core.controler.PersonPrepareForSimAlgorithm; import org.matsim.core.network.NetworkUtils; import org.matsim.core.population.algorithms.PlanAlgorithm; +import org.matsim.core.population.algorithms.XY2Links; import org.matsim.core.router.MultimodalLinkChooser; import org.matsim.core.router.SingleModeNetworksCache; import org.matsim.core.router.TripRouter; @@ -39,6 +40,8 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm, PersonPrepareFo private final MultimodalLinkChooser linkChooser; private final Scenario scenario; + private final XY2Links xy2Links; + @Inject LeipzigRouterPlanAlgorithm(final TripRouter tripRouter, final ActivityFacilities facilities, final TimeInterpretation timeInterpretation, SingleModeNetworksCache singleModeNetworksCache, Scenario scenario, MultimodalLinkChooser linkChooser) { @@ -59,6 +62,8 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm, PersonPrepareFo reducedNetwork.addLink(link); } } + + xy2Links = new XY2Links(fullModalNetwork, scenario.getActivityFacilities()); } private static LeipzigUtils.PersonParkingBehaviour getParkingBehaviour(Network fullModalNetwork, Activity originActivity, Facility originFacility, String routingMode) { @@ -101,8 +106,6 @@ public void run(final Plan plan) { final Facility toFacility = FacilitiesUtils.toFacility(oldTrip.getDestinationActivity(), facilities); -// System.exit(-1); - // At this point, I only want to deal with residential parking. Shopping comes later (and is simpler). LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtOrigin = getParkingBehaviour(fullModalNetwork, oldTrip.getOriginActivity(), fromFacility, routingMode); @@ -262,7 +265,9 @@ private List createTripForParkingAtDestination(TripStructureUtils.T @Override public void run(Person person) { + for (Plan plan : person.getPlans()) { + xy2Links.run(plan); run(plan); } } diff --git a/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java b/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java index f22605b0..613fed2a 100644 --- a/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java +++ b/src/main/java/org/matsim/run/LeipzigRoutingStrategyProvider.java @@ -35,7 +35,7 @@ public class LeipzigRoutingStrategyProvider implements Provider { @Inject private TimeInterpretation timeInterpretation; @Inject - MultimodalLinkChooser linkChooser; + private MultimodalLinkChooser linkChooser; @Override public PlanStrategy get() { diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index 921126c4..49755742 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -201,13 +201,12 @@ protected Config prepareConfig(Config config) { if (networkOpt.hasParkingCostArea()) { ConfigUtils.addOrGetModule(config, ParkingCostConfigGroup.class); config.planCalcScore().addActivityParams(new PlanCalcScoreConfigGroup.ActivityParams(TripStructureUtils.createStageActivityType("parking")).setScoringThisActivityAtAll(false)); + //TODO put into a static method Collection modifiableCollectionOfOldStrategySettings = new ArrayList<>( config.strategy().getStrategySettings()); config.strategy().clearStrategySettings(); - Iterator iterator = modifiableCollectionOfOldStrategySettings.iterator(); - while (iterator.hasNext()) { - StrategyConfigGroup.StrategySettings strategySetting = iterator.next(); + for (StrategyConfigGroup.StrategySettings strategySetting : modifiableCollectionOfOldStrategySettings) { if (strategySetting.getStrategyName().equals("ReRoute")) { StrategyConfigGroup.StrategySettings newReRouteStrategy = new StrategyConfigGroup.StrategySettings(); newReRouteStrategy.setStrategyName(LeipzigRoutingStrategyProvider.STRATEGY_NAME); @@ -220,8 +219,7 @@ protected Config prepareConfig(Config config) { } } // this is how it is supposed to be - //config.facilities().setFacilitiesSource(FacilitiesConfigGroup.FacilitiesSource.onePerActivityLinkInPlansFile); - config.facilities().setFacilitiesSource(FacilitiesConfigGroup.FacilitiesSource.none); + config.facilities().setFacilitiesSource(FacilitiesConfigGroup.FacilitiesSource.onePerActivityLinkInPlansFile); } // TODO: try to remove ParkingCostConfigGroup.class // TODO: FIXME: yyyyyy no longer supported on main branch. "complicatedParking" will resolve this with custom code. @@ -285,9 +283,10 @@ public void install() { install(new PersonMoneyEventsAnalysisModule()); - this.addPersonPrepareForSimAlgorithm().addBinding().to(LeipzigRouterPlanAlgorithm.class); + this.addPersonPrepareForSimAlgorithm().to(LeipzigRouterPlanAlgorithm.class); this.addPlanStrategyBinding(LeipzigRoutingStrategyProvider.STRATEGY_NAME).toProvider(LeipzigRoutingStrategyProvider.class); + } // TODO FIXME yyyyyy replace by config option From 2a06933ebce21b572c6d654ca810937c584a19bc Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 20 Jun 2023 11:28:03 +0200 Subject: [PATCH 052/106] finished some todos --- .../java/org/matsim/run/LeipzigUtils.java | 3 + .../org/matsim/run/RunLeipzigScenario.java | 64 ++++++++++--------- 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/matsim/run/LeipzigUtils.java b/src/main/java/org/matsim/run/LeipzigUtils.java index db52311a..8f63ec33 100644 --- a/src/main/java/org/matsim/run/LeipzigUtils.java +++ b/src/main/java/org/matsim/run/LeipzigUtils.java @@ -106,4 +106,7 @@ public static void setResidentialParkingCost(Link link, double parkingCost) { public static void setParkingCapacity(Link link, double parkingCapacity) { link.getAttributes().putAttribute("parkingCapacity", parkingCapacity); } + + + } diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index 49755742..10b4f813 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -92,7 +92,7 @@ public class RunLeipzigScenario extends MATSimApplication { */ public static final String CRS = "EPSG:25832"; - static final String VERSION = "1.1"; + static final String VERSION = "1.1"; private static final Logger log = LogManager.getLogger(RunLeipzigScenario.class); @@ -127,6 +127,27 @@ public static void main(String[] args) { // This implicitly calls "call()", which then calls prepareConfig, prepareScenario, prepareControler from the class here, and then calls controler.run(). } + /** + * Replaces reroute strategy with leipzig specific one. + */ + private static void adjustStrategiesForParking(Config config) { + Collection modifiableCollectionOfOldStrategySettings = new ArrayList<>(config.strategy().getStrategySettings()); + config.strategy().clearStrategySettings(); + + for (StrategyConfigGroup.StrategySettings strategySetting : modifiableCollectionOfOldStrategySettings) { + if (strategySetting.getStrategyName().equals("ReRoute")) { + StrategyConfigGroup.StrategySettings newReRouteStrategy = new StrategyConfigGroup.StrategySettings(); + newReRouteStrategy.setStrategyName(LeipzigRoutingStrategyProvider.STRATEGY_NAME); + newReRouteStrategy.setSubpopulation(strategySetting.getSubpopulation()); + newReRouteStrategy.setWeight(strategySetting.getWeight()); + newReRouteStrategy.setDisableAfter(strategySetting.getDisableAfter()); + config.strategy().addStrategySettings(newReRouteStrategy); + } else { + config.strategy().addStrategySettings(strategySetting); + } + } + } + @Nullable @Override protected Config prepareConfig(Config config) { @@ -165,6 +186,9 @@ protected Config prepareConfig(Config config) { config.qsim().setUsePersonIdForMissingVehicleId(false); + // this is how it is supposed to be + config.facilities().setFacilitiesSource(FacilitiesConfigGroup.FacilitiesSource.onePerActivityLinkInPlansFile); + switch ((bike)) { case onNetworkWithStandardMatsim -> { // bike is routed on the network per the xml config. @@ -202,29 +226,9 @@ protected Config prepareConfig(Config config) { ConfigUtils.addOrGetModule(config, ParkingCostConfigGroup.class); config.planCalcScore().addActivityParams(new PlanCalcScoreConfigGroup.ActivityParams(TripStructureUtils.createStageActivityType("parking")).setScoringThisActivityAtAll(false)); - //TODO put into a static method - Collection modifiableCollectionOfOldStrategySettings = new ArrayList<>( config.strategy().getStrategySettings()); - config.strategy().clearStrategySettings(); - - for (StrategyConfigGroup.StrategySettings strategySetting : modifiableCollectionOfOldStrategySettings) { - if (strategySetting.getStrategyName().equals("ReRoute")) { - StrategyConfigGroup.StrategySettings newReRouteStrategy = new StrategyConfigGroup.StrategySettings(); - newReRouteStrategy.setStrategyName(LeipzigRoutingStrategyProvider.STRATEGY_NAME); - newReRouteStrategy.setSubpopulation(strategySetting.getSubpopulation()); - newReRouteStrategy.setWeight(strategySetting.getWeight()); - newReRouteStrategy.setDisableAfter(strategySetting.getDisableAfter()); - config.strategy().addStrategySettings(newReRouteStrategy); - } else { - config.strategy().addStrategySettings(strategySetting); - } - } - // this is how it is supposed to be - config.facilities().setFacilitiesSource(FacilitiesConfigGroup.FacilitiesSource.onePerActivityLinkInPlansFile); + adjustStrategiesForParking(config); + } - // TODO: try to remove ParkingCostConfigGroup.class - // TODO: FIXME: yyyyyy no longer supported on main branch. "complicatedParking" will resolve this with custom code. - // right now TimeRestrictedParkingCostHandler depends on parkingCostConfigGroup, so we still need the cfg group. - //after merging complicatedParking branch: fix this! return config; } @@ -249,6 +253,7 @@ protected void prepareScenario(Scenario scenario) { scenario.getPopulation().getFactory().getRouteFactories().setRouteFactory(DrtRoute.class, new DrtRouteFactory()); // (matsim core does not know about DRT routes. This makes it possible to read them before the controler is there.) } + networkOpt.prepare(scenario.getNetwork()); // (passt das Netz an aus den mitgegebenen shape files, z.B. parking area, car-free area, ...) } @@ -279,13 +284,12 @@ public void install() { } if (networkOpt.hasParkingCostArea()) { - addEventHandlerBinding().toInstance(new TimeRestrictedParkingCostHandler(parkingCostTimePeriodStart, parkingCostTimePeriodEnd)); - - install(new PersonMoneyEventsAnalysisModule()); + this.addEventHandlerBinding().toInstance(new TimeRestrictedParkingCostHandler(parkingCostTimePeriodStart, parkingCostTimePeriodEnd)); this.addPersonPrepareForSimAlgorithm().to(LeipzigRouterPlanAlgorithm.class); this.addPlanStrategyBinding(LeipzigRoutingStrategyProvider.STRATEGY_NAME).toProvider(LeipzigRoutingStrategyProvider.class); + install(new PersonMoneyEventsAnalysisModule()); } @@ -299,10 +303,10 @@ public void install() { // Given a fixed mode, the "less disruptive" choice dimensions will be weighted higher during the end schedules.addBinding().toInstance(new StrategyWeightFadeout.Schedule(DefaultPlanStrategiesModule.DefaultStrategy.SubtourModeChoice, "person", 0.65, 0.80)); - // Fades out until 0.9 (innovation switch off) - //TODO switch no new ReRoute!!!! - schedules.addBinding().toInstance(new StrategyWeightFadeout.Schedule(LeipzigRoutingStrategyProvider.STRATEGY_NAME, "person", 0.75)); - schedules.addBinding().toInstance(new StrategyWeightFadeout.Schedule(DefaultPlanStrategiesModule.DefaultStrategy.TimeAllocationMutator, "person", 0.75)); + // Fades out until 0.9 (innovation switch off) + //TODO switch no new ReRoute!!!! + schedules.addBinding().toInstance(new StrategyWeightFadeout.Schedule(LeipzigRoutingStrategyProvider.STRATEGY_NAME, "person", 0.75)); + schedules.addBinding().toInstance(new StrategyWeightFadeout.Schedule(DefaultPlanStrategiesModule.DefaultStrategy.TimeAllocationMutator, "person", 0.75)); } bind(new TypeLiteral>() { From 0535d223a1b2c384b7a478c718c72b934033a7c2 Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 20 Jun 2023 11:36:24 +0200 Subject: [PATCH 053/106] fix checkstyle --- .../matsim/run/LeipzigRouterPlanAlgorithm.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index 85125520..dd8385cb 100644 --- a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -196,6 +196,15 @@ public void run(final Plan plan) { } + @Override + public void run(Person person) { + + for (Plan plan : person.getPlans()) { + xy2Links.run(plan); + run(plan); + } + } + private List createTripForParkingAtOrigin(TripStructureUtils.Trip oldTrip, String routingMode, Facility fromFacility, Facility toFacility, Plan plan, TimeTracker timeTracker) { // restricted parking at origin: @@ -263,12 +272,4 @@ private List createTripForParkingAtDestination(TripStructureUtils.T return newTripElements; } - @Override - public void run(Person person) { - - for (Plan plan : person.getPlans()) { - xy2Links.run(plan); - run(plan); - } - } } From 9d5a2ea853a6f378524f5a1c75c5eac95069c318 Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 20 Jun 2023 11:56:25 +0200 Subject: [PATCH 054/106] cleanup some style issues --- .../run/LeipzigRouterPlanAlgorithm.java | 28 ++++-------- .../java/org/matsim/run/LeipzigUtils.java | 42 +++++++++--------- .../run/prepare/AssignParkingAttributes.java | 44 ------------------- 3 files changed, 29 insertions(+), 85 deletions(-) delete mode 100644 src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java diff --git a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index dd8385cb..c8bc600b 100644 --- a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -1,8 +1,6 @@ package org.matsim.run; import com.google.inject.Inject; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.TransportMode; import org.matsim.api.core.v01.network.Link; @@ -31,7 +29,6 @@ import static org.matsim.run.LeipzigUtils.isLinkParkingTypeInsideResidentialArea; final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm, PersonPrepareForSimAlgorithm { - private static final Logger log = LogManager.getLogger(LeipzigRouterPlanAlgorithm.class); private final TripRouter tripRouter; private final ActivityFacilities facilities; private final TimeInterpretation timeInterpretation; @@ -66,12 +63,10 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm, PersonPrepareFo xy2Links = new XY2Links(fullModalNetwork, scenario.getActivityFacilities()); } - private static LeipzigUtils.PersonParkingBehaviour getParkingBehaviour(Network fullModalNetwork, Activity originActivity, Facility originFacility, String routingMode) { + private static LeipzigUtils.PersonParkingBehaviour getParkingBehaviour(Network fullModalNetwork, Activity originActivity, String routingMode) { LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.defaultLogic; // if we find out that there are time restrictions on all the links - //originActivity.getEndTime(); - if (routingMode.equals(TransportMode.car)) { // an dieser stelle waere es besser abzufragen, ob die person in der naehe wohnt anstatt nur die home act -> residential parking zuzuordnen @@ -81,11 +76,6 @@ private static LeipzigUtils.PersonParkingBehaviour getParkingBehaviour(Network f Link link = fullModalNetwork.getLinks().get(originActivity.getLinkId()); if (isLinkParkingTypeInsideResidentialArea(link)) { parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.parkingSearchLogicLeipzig; - // if (originActivity.getType().equals(ActivityTypes.SHOPPING)) { - // // change this to parking type normal/ unrestricted - // // on activity link, unrestricted - // parkingBehaviourAtOrigin = LeipzigUtils.PersonParkingBehaviour.shopping; - // } } } } @@ -108,8 +98,8 @@ public void run(final Plan plan) { // At this point, I only want to deal with residential parking. Shopping comes later (and is simpler). - LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtOrigin = getParkingBehaviour(fullModalNetwork, oldTrip.getOriginActivity(), fromFacility, routingMode); - LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtDestination = getParkingBehaviour(fullModalNetwork, oldTrip.getDestinationActivity(), toFacility, routingMode); + LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtOrigin = getParkingBehaviour(fullModalNetwork, oldTrip.getOriginActivity(), routingMode); + LeipzigUtils.PersonParkingBehaviour parkingBehaviourAtDestination = getParkingBehaviour(fullModalNetwork, oldTrip.getDestinationActivity(), routingMode); if (parkingBehaviourAtOrigin == LeipzigUtils.PersonParkingBehaviour.defaultLogic && parkingBehaviourAtDestination == LeipzigUtils.PersonParkingBehaviour.defaultLogic) { @@ -188,8 +178,7 @@ public void run(final Plan plan) { TripRouter.insertTrip(plan, oldTrip.getOriginActivity(), newTripElements, oldTrip.getDestinationActivity()); } else { - throw new RuntimeException(); - // to be implemented! + throw new IllegalStateException("Unhandled parking situation"); } } @@ -209,7 +198,6 @@ private List createTripForParkingAtOrigin(TripStructureUtils.Trip o // restricted parking at origin: // first find parking: -// final Link parkingLink = NetworkUtils.getNearestLink( reducedNetwork, oldTrip.getOriginActivity().getCoord() ); final Link parkingLink = linkChooser.decideOnLink(fromFacility, reducedNetwork); final Activity parkingActivity = scenario.getPopulation().getFactory().createInteractionActivityFromLinkId( @@ -220,8 +208,8 @@ private List createTripForParkingAtOrigin(TripStructureUtils.Trip o final List walkTripElements = tripRouter.calcRoute(TransportMode.walk, fromFacility, parkingFacility, timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes()); for (PlanElement tripElement : walkTripElements) { - if (tripElement instanceof Leg) { - TripStructureUtils.setRoutingMode((Leg) tripElement, TransportMode.car); + if (tripElement instanceof Leg leg) { + TripStructureUtils.setRoutingMode(leg, TransportMode.car); } } @@ -259,8 +247,8 @@ private List createTripForParkingAtDestination(TripStructureUtils.T final List walkTripElements = tripRouter.calcRoute(TransportMode.walk, parkingFacility, toFacility, timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes()); for (PlanElement tripElement : walkTripElements) { - if (tripElement instanceof Leg) { - TripStructureUtils.setRoutingMode((Leg) tripElement, TransportMode.car); + if (tripElement instanceof Leg leg) { + TripStructureUtils.setRoutingMode(leg, TransportMode.car); } } newTripElements.addAll(walkTripElements); diff --git a/src/main/java/org/matsim/run/LeipzigUtils.java b/src/main/java/org/matsim/run/LeipzigUtils.java index 8f63ec33..06f535cc 100644 --- a/src/main/java/org/matsim/run/LeipzigUtils.java +++ b/src/main/java/org/matsim/run/LeipzigUtils.java @@ -10,16 +10,16 @@ */ public final class LeipzigUtils{ - private static final String mode = "car"; - private static final String dailyParkingCostLinkAttributeName = "dailyPCost"; - private static final String firstHourParkingCostLinkAttributeName = "oneHourPCost"; - private static final String extraHourParkingCostLinkAttributeName = "extraHourPCost"; - private static final String maxDailyParkingCostLinkAttributeName = "maxDailyPCost"; - private static final String maxParkingDurationAttributeName = "maxPDuration"; - private static final String parkingPenaltyAttributeName = "penalty"; - private static final String residentialParkingFeePerDay = "residentialPFee"; - private static final String activityPrefixForDailyParkingCosts = "home"; - private static final Set activityPrefixToBeExcludedFromParkingCost = new HashSet<>(); + private static final String MODE = "car"; + private static final String DAILY_PARKING_COST_LINK_ATTRIBUTE_NAME = "dailyPCost"; + private static final String FIRST_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME = "oneHourPCost"; + private static final String EXTRA_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME = "extraHourPCost"; + private static final String MAX_DAILY_PARKING_COST_LINK_ATTRIBUTE_NAME = "maxDailyPCost"; + private static final String MAX_PARKING_DURATION_ATTRIBUTE_NAME = "maxPDuration"; + private static final String PARKING_PENALTY_ATTRIBUTE_NAME = "penalty"; + private static final String RESIDENTIAL_PARKING_FEE_PER_DAY = "residentialPFee"; + private static final String ACTIVITY_PREFIX_FOR_DAILY_PARKING_COSTS = "home"; + private static final Set ACTIVITY_PREFIX_TO_BE_EXCLUDED_FROM_PARKING_COST = new HashSet<>(); // do not instantiate private LeipzigUtils(){} @@ -27,7 +27,7 @@ private LeipzigUtils(){} /** * Defines the parkingBehaviour of a person. */ - enum PersonParkingBehaviour {defaultLogic, parkingSearchLogicLeipzig, @Deprecated shopping} + enum PersonParkingBehaviour {defaultLogic, parkingSearchLogicLeipzig} /** * Defines the parkingType of a network link. @@ -48,43 +48,43 @@ public static boolean isLinkParkingTypeInsideResidentialArea(Link link ) { } static String getMode() { - return mode; + return MODE; } static String getDailyParkingCostLinkAttributeName() { - return dailyParkingCostLinkAttributeName; + return DAILY_PARKING_COST_LINK_ATTRIBUTE_NAME; } static String getFirstHourParkingCostLinkAttributeName() { - return firstHourParkingCostLinkAttributeName; + return FIRST_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME; } static String getExtraHourParkingCostLinkAttributeName() { - return extraHourParkingCostLinkAttributeName; + return EXTRA_HOUR_PARKING_COST_LINK_ATTRIBUTE_NAME; } static String getMaxDailyParkingCostLinkAttributeName() { - return maxDailyParkingCostLinkAttributeName; + return MAX_DAILY_PARKING_COST_LINK_ATTRIBUTE_NAME; } static String getMaxParkingDurationAttributeName() { - return maxParkingDurationAttributeName; + return MAX_PARKING_DURATION_ATTRIBUTE_NAME; } static String getParkingPenaltyAttributeName() { - return parkingPenaltyAttributeName; + return PARKING_PENALTY_ATTRIBUTE_NAME; } static String getResidentialParkingFeeAttributeName() { - return residentialParkingFeePerDay; + return RESIDENTIAL_PARKING_FEE_PER_DAY; } static String getActivityPrefixForDailyParkingCosts() { - return activityPrefixForDailyParkingCosts; + return ACTIVITY_PREFIX_FOR_DAILY_PARKING_COSTS; } static Set getActivityPrefixesToBeExcludedFromParkingCost() { - return activityPrefixToBeExcludedFromParkingCost; + return ACTIVITY_PREFIX_TO_BE_EXCLUDED_FROM_PARKING_COST; } public static void setLinkParkingTypeToInsideResidentialArea(Link link ){ diff --git a/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java b/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java deleted file mode 100644 index 43f67b0d..00000000 --- a/src/main/java/org/matsim/run/prepare/AssignParkingAttributes.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.matsim.run.prepare; - -import org.locationtech.jts.geom.Geometry; -import org.matsim.api.core.v01.population.Activity; -import org.matsim.api.core.v01.population.Person; -import org.matsim.api.core.v01.population.PlanElement; -import org.matsim.api.core.v01.population.Population; -import org.matsim.application.options.ShpOptions; -//import org.matsim.core.utils.geometry.geotools.MGC; -import playground.vsp.openberlinscenario.cemdap.output.ActivityTypes; - -final class AssignParkingAttributes { - - private AssignParkingAttributes() {} - - static void addParkingAttributesToPopulation(Population population, ShpOptions shp) { - - Geometry restrictedParkingArea = shp.getGeometry(); - boolean isInsideRestrictedParkingArea; - - for (Person person : population.getPersons().values()) { - for (PlanElement element : person.getSelectedPlan().getPlanElements()) { - if (element instanceof Activity) { - - Activity activity = (Activity) element; - - if (!activity.getType().contains(ActivityTypes.HOME)) { - continue; - } - -// isInsideRestrictedParkingArea = MGC.coord2Point(activity.getCoord()).within(restrictedParkingArea); -// -// if (isInsideRestrictedParkingArea) { -//// LeipzigUtils.setParkingToRestricted(person); -// } else { -//// LeipzigUtils.setParkingToNonRestricted(person); -// } - - break; - } - } - } - } -} From c5dfcf6492a510dc7114c725dfd4aa6f51cdfa23 Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 20 Jun 2023 12:11:43 +0200 Subject: [PATCH 055/106] few more style fixes --- .../java/org/matsim/run/LeipzigRouterPlanAlgorithm.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index c8bc600b..6644dc1e 100644 --- a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -128,7 +128,6 @@ public void run(final Plan plan) { // restricted parking at origin: // first find parking: -// final Link parkingLink = NetworkUtils.getNearestLink( reducedNetwork, oldTrip.getOriginActivity().getCoord() ); final Link originParkingLink = linkChooser.decideOnLink(fromFacility, reducedNetwork); final Activity originParkingActivity = scenario.getPopulation().getFactory().createInteractionActivityFromLinkId( @@ -146,8 +145,8 @@ public void run(final Plan plan) { final List originWalkTripElements = tripRouter.calcRoute(TransportMode.walk, fromFacility, originParkingFacility, timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes()); for (PlanElement tripElement : originWalkTripElements) { - if (tripElement instanceof Leg) { - TripStructureUtils.setRoutingMode((Leg) tripElement, TransportMode.car); + if (tripElement instanceof Leg leg) { + TripStructureUtils.setRoutingMode(leg, TransportMode.car); } } @@ -165,8 +164,8 @@ public void run(final Plan plan) { final List destinationWalkTripElements = tripRouter.calcRoute(TransportMode.walk, destinationParkingFacility, toFacility, timeTracker.getTime().seconds(), plan.getPerson(), oldTrip.getTripAttributes()); for (PlanElement tripElement : destinationWalkTripElements) { - if (tripElement instanceof Leg) { - TripStructureUtils.setRoutingMode((Leg) tripElement, TransportMode.car); + if (tripElement instanceof Leg leg) { + TripStructureUtils.setRoutingMode(leg, TransportMode.car); } } newTripElements.addAll(destinationWalkTripElements); From dee681ee89a170d1455e6b87e1049552e2eede96 Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Wed, 21 Jun 2023 21:03:27 +0200 Subject: [PATCH 056/106] started with parking locations analysis --- .../org/matsim/analysis/ParkingLocation.java | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 src/main/java/org/matsim/analysis/ParkingLocation.java diff --git a/src/main/java/org/matsim/analysis/ParkingLocation.java b/src/main/java/org/matsim/analysis/ParkingLocation.java new file mode 100644 index 00000000..1a50d292 --- /dev/null +++ b/src/main/java/org/matsim/analysis/ParkingLocation.java @@ -0,0 +1,116 @@ +package org.matsim.analysis; + +import org.matsim.api.core.v01.Coord; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.ActivityStartEvent; +import org.matsim.api.core.v01.events.handler.ActivityStartEventHandler; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.application.MATSimAppCommand; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.events.EventsUtils; +import org.matsim.core.events.MatsimEventsReader; +import org.matsim.core.network.NetworkUtils; +import org.matsim.core.utils.io.IOUtils; +import org.matsim.vehicles.Vehicle; +import picocli.CommandLine; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static org.matsim.application.ApplicationUtils.globFile; + +public class ParkingLocation implements MATSimAppCommand { + + @CommandLine.Option(names = "--directory", description = "path to matsim output directory", required = true) + private Path directory; + + public static void main (String args []) { + new ParkingLocation().execute(args); + } + + @Override + public Integer call() throws Exception { + Path eventsPath = globFile(directory, "*output_events.*"); + Path networkPath = globFile(directory, "*output_network.*"); + EventsManager manager = EventsUtils.createEventsManager(); + Network network = NetworkUtils.readNetwork(String.valueOf(networkPath)); + List listOfParkingActivities = new ArrayList<>(); + + manager.addHandler(new ParkingActivites(listOfParkingActivities, network)); + manager.initProcessing(); + MatsimEventsReader matsimEventsReader = new MatsimEventsReader(manager); + matsimEventsReader.readFile(eventsPath.toString()); + manager.finishProcessing(); + writeResults(directory, listOfParkingActivities); + + return null; + } + + + private static class ParkingActivites implements ActivityStartEventHandler { + + private final List listOfParkingData; + private final Network network; + + ParkingActivites(List listOfParkingData, Network network) { + this.listOfParkingData = listOfParkingData; + this.network = network; + } + + @Override + public void handleEvent(ActivityStartEvent activityStartEvent) { + if (activityStartEvent.getActType().equals("parking interaction")) { + Link l = network.getLinks().get(activityStartEvent.getLinkId()); + ParkingData pd = new ParkingData(l.getCoord(), activityStartEvent.getLinkId()); + listOfParkingData.add(pd); + } + } + } + + + + private static void writeResults(Path outputFolder, List listOfParkingData) throws IOException { + BufferedWriter writer = IOUtils.getBufferedWriter(outputFolder.toString() + "/parkingActivities.tsv"); + BufferedWriter writerWithCounts = IOUtils.getBufferedWriter(outputFolder.toString() + "/parkingActivitiesWithCount.tsv"); + writer.write("x" + "\t" + "y" + "\t" + "linkId" ); + writer.newLine(); + for (int i = 0; i < listOfParkingData.size(); i++) { + + ParkingData pd = listOfParkingData.get(i); + writer.write(pd.coord.getX() + "\t" + pd.coord.getY() + "\t" + pd.linkId); + // writer.write(listOfIds.get(i).toString()); + writer.newLine(); + } + writer.close(); + + Map duplicateCountMap = listOfParkingData + .stream() + .collect( + Collectors.toMap(Function.identity(), company -> 1, Math::addExact) + ); + + + writerWithCounts.write(("x" + "\t" + "y" + "\t" + "linkId" + "count")); + writerWithCounts.newLine(); + duplicateCountMap.forEach( + (key, value) -> { + try { + writerWithCounts.write( key.coord.getX() + "\t" + key.coord.getY() +"\t" + + key.linkId + "\t" + value); + writerWithCounts.newLine(); + } catch (IOException e) { + e.printStackTrace(); + } + } + ); + } + + record ParkingData (Coord coord, Id linkId) {} +} From 5f40aaae161c80845fdf42456aee4949f6337a54 Mon Sep 17 00:00:00 2001 From: rakow Date: Wed, 21 Jun 2023 21:06:07 +0200 Subject: [PATCH 057/106] setup trip dashboards --- Makefile | 7 +- input/v1.2/leipzig-v1.2-25pct.config.xml | 8 +-- .../parkingCostArea/Bewohnerparken_2020.cpg | 1 + .../parkingCostArea/Bewohnerparken_2020.dbf | Bin 0 -> 5098 bytes .../parkingCostArea/Bewohnerparken_2020.prj | 1 + .../parkingCostArea/Bewohnerparken_2020.qmd | 26 ++++++++ .../parkingCostArea/Bewohnerparken_2020.shp | Bin 0 -> 8228 bytes .../parkingCostArea/Bewohnerparken_2020.shx | Bin 0 -> 292 bytes pom.xml | 3 +- .../dashboard/LeipzigDashboardProvider.java | 25 +++++++ .../org/matsim/run/RunLeipzigScenario.java | 23 +++++-- .../matsim/run/prepare/NetworkOptions.java | 16 +++-- .../matsim/run/prepare/PreparePopulation.java | 61 ++++++++++++++---- .../org.matsim.simwrapper.DashboardProvider | 1 + .../resources/mode_share_per_dist_ref.csv | 31 +++++++++ src/main/resources/mode_share_ref.csv | 31 +++++++++ src/main/resources/mode_users_ref.csv | 6 ++ 17 files changed, 208 insertions(+), 32 deletions(-) create mode 100644 input/v1.2/parkingCostArea/Bewohnerparken_2020.cpg create mode 100644 input/v1.2/parkingCostArea/Bewohnerparken_2020.dbf create mode 100644 input/v1.2/parkingCostArea/Bewohnerparken_2020.prj create mode 100644 input/v1.2/parkingCostArea/Bewohnerparken_2020.qmd create mode 100644 input/v1.2/parkingCostArea/Bewohnerparken_2020.shp create mode 100644 input/v1.2/parkingCostArea/Bewohnerparken_2020.shx create mode 100644 src/main/java/org/matsim/dashboard/LeipzigDashboardProvider.java create mode 100644 src/main/resources/META-INF/services/org.matsim.simwrapper.DashboardProvider create mode 100644 src/main/resources/mode_share_per_dist_ref.csv create mode 100644 src/main/resources/mode_share_ref.csv create mode 100644 src/main/resources/mode_users_ref.csv diff --git a/Makefile b/Makefile index 9127b894..a341dd9a 100644 --- a/Makefile +++ b/Makefile @@ -90,6 +90,7 @@ input/plans-longHaulFreight.xml.gz: input/$V/leipzig-$V-network.xml.gz --input-crs $(CRS)\ --target-crs $(CRS)\ --shp ../shared-svn/projects/NaMAV/data/shapefiles/freight-area/freight-area.shp\ + --cut-on-boundary\ --output $@ input/plans-commercialTraffic.xml.gz: @@ -118,7 +119,7 @@ input/$V/leipzig-$V-25pct.plans-initial.xml.gz: input/plans-longHaulFreight.xml. --population $(shared)/matsim-input-files/senozon/20210520_leipzig/population.xml.gz\ --attributes $(shared)/matsim-input-files/senozon/20210520_leipzig/personAttributes.xml.gz - $(sc) prepare population input/prepare-25pct.plans.xml.gz\ + $(sc) prepare population input/prepare-25pct.plans.xml.gz --phase pre\ --shp $(shared)/matsim-input-files/senozon/20210520_leipzig/dilutionArea.shp --shp-crs $(CRS)\ --output input/prepare-25pct.plans.xml.gz @@ -149,6 +150,10 @@ input/$V/leipzig-$V-25pct.plans-initial.xml.gz: input/plans-longHaulFreight.xml. $(sc) prepare merge-populations $@ $^ --output $@ + $(sc) prepare population $@ --phase post\ + --shp $(shared)/matsim-input-files/senozon/20210520_leipzig/dilutionArea.shp --shp-crs $(CRS)\ + --output $@ + $(sc) prepare extract-home-coordinates $@ --csv input/$V/leipzig-$V-homes.csv $(sc) prepare downsample-population $@\ diff --git a/input/v1.2/leipzig-v1.2-25pct.config.xml b/input/v1.2/leipzig-v1.2-25pct.config.xml index f99dceb9..2d1cf0fc 100644 --- a/input/v1.2/leipzig-v1.2-25pct.config.xml +++ b/input/v1.2/leipzig-v1.2-25pct.config.xml @@ -57,11 +57,11 @@ - + + - - - + + diff --git a/input/v1.2/parkingCostArea/Bewohnerparken_2020.cpg b/input/v1.2/parkingCostArea/Bewohnerparken_2020.cpg new file mode 100644 index 00000000..3ad133c0 --- /dev/null +++ b/input/v1.2/parkingCostArea/Bewohnerparken_2020.cpg @@ -0,0 +1 @@ +UTF-8 \ No newline at end of file diff --git a/input/v1.2/parkingCostArea/Bewohnerparken_2020.dbf b/input/v1.2/parkingCostArea/Bewohnerparken_2020.dbf new file mode 100644 index 0000000000000000000000000000000000000000..69dff29acab587bb20b74f3e804de44c6aa6246e GIT binary patch literal 5098 zcmds*Pfx-y6u_4dFB;?3cKZ2lT;-_1yYn#HDG@sQb*b<&_@1%pD)d z!Xz^rCN5CaFJpC>*x)8`1GigAuGzpC^9t0GfO4G;x4BjvMVElm-5en6Q5Z$XHY_~T zr-1HQLW0n8Lf_6T?T}W_m283?*GKnrG64cE-BpNWy169v4CV4a4_LfFmV*Y_cpGcI zXpxzXRMxw_Ap@nC&SQ&jag_FJNLEXW-J1ag&!bxmCacR|2kb(|uH2NMExs#PGEjyx zP;}vOg~fr@(jnk4rXF)w>e}ZheF2~;bKw$LQP;I@uM2m3btuB$VCqnWzroa@2*1J9 wp$L!m>QIDV#_CYoqT`A>lul((3|t+Q>O^~EpQFA0?Kg!{ooH|DeY8LO1Smyh!2kdN literal 0 HcmV?d00001 diff --git a/input/v1.2/parkingCostArea/Bewohnerparken_2020.prj b/input/v1.2/parkingCostArea/Bewohnerparken_2020.prj new file mode 100644 index 00000000..bd846aeb --- /dev/null +++ b/input/v1.2/parkingCostArea/Bewohnerparken_2020.prj @@ -0,0 +1 @@ +PROJCS["ETRS_1989_UTM_Zone_32N",GEOGCS["GCS_ETRS_1989",DATUM["D_ETRS_1989",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",500000.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",9.0],PARAMETER["Scale_Factor",0.9996],PARAMETER["Latitude_Of_Origin",0.0],UNIT["Meter",1.0]] \ No newline at end of file diff --git a/input/v1.2/parkingCostArea/Bewohnerparken_2020.qmd b/input/v1.2/parkingCostArea/Bewohnerparken_2020.qmd new file mode 100644 index 00000000..a18ad236 --- /dev/null +++ b/input/v1.2/parkingCostArea/Bewohnerparken_2020.qmd @@ -0,0 +1,26 @@ + + + + + + dataset + + + + + + + + + + 0 + 0 + + + + + false + + + + diff --git a/input/v1.2/parkingCostArea/Bewohnerparken_2020.shp b/input/v1.2/parkingCostArea/Bewohnerparken_2020.shp new file mode 100644 index 0000000000000000000000000000000000000000..24c433fd687da8d65df796738bbab594e0126813 GIT binary patch literal 8228 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 literal 0 HcmV?d00001 diff --git a/pom.xml b/pom.xml index 320c70bd..12df47f0 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,8 @@ org.matsim matsim-all - 16.0-PR2653 + 16.0-PR2657 + 4.0.0 diff --git a/src/main/java/org/matsim/dashboard/LeipzigDashboardProvider.java b/src/main/java/org/matsim/dashboard/LeipzigDashboardProvider.java new file mode 100644 index 00000000..f2fbb833 --- /dev/null +++ b/src/main/java/org/matsim/dashboard/LeipzigDashboardProvider.java @@ -0,0 +1,25 @@ +package org.matsim.dashboard; + +import org.matsim.core.config.Config; +import org.matsim.simwrapper.Dashboard; +import org.matsim.simwrapper.DashboardProvider; +import org.matsim.simwrapper.SimWrapper; +import org.matsim.simwrapper.dashboard.TripDashboard; + +import java.util.List; + +/** + * Provider for default dashboards in the scenario. + * Declared in META-INF/services + */ +public class LeipzigDashboardProvider implements DashboardProvider { + + @Override + public List getDashboards(Config config, SimWrapper simWrapper) { + + TripDashboard trips = new TripDashboard("mode_share_ref.csv", "mode_share_per_dist_ref.csv", "mode_users_ref.csv"); + + return List.of(trips); + } + +} diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index a6bb4e4e..79562739 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -58,6 +58,7 @@ import org.matsim.extensions.pt.routing.ptRoutingModes.PtIntermodalRoutingModesConfigGroup; import org.matsim.extensions.pt.routing.ptRoutingModes.PtIntermodalRoutingModesModule; import org.matsim.run.prepare.*; +import org.matsim.simwrapper.SimWrapperConfigGroup; import org.matsim.simwrapper.SimWrapperModule; import org.matsim.smallScaleCommercialTrafficGeneration.CreateSmallScaleCommercialTrafficDemand; import picocli.CommandLine; @@ -90,7 +91,7 @@ public class RunLeipzigScenario extends MATSimApplication { */ public static final String CRS = "EPSG:25832"; - static final String VERSION = "1.2"; + public static final String VERSION = "1.2"; private static final Logger log = LogManager.getLogger(RunLeipzigScenario.class); @@ -150,17 +151,24 @@ private static void adjustStrategiesForParking(Config config) { @Override protected Config prepareConfig(Config config) { - SnzActivities.addScoringParams(config); // senozon activity types that are always the same. Differentiated by typical duration. + SnzActivities.addScoringParams(config); // Prepare commercial config config.planCalcScore().addActivityParams(new PlanCalcScoreConfigGroup.ActivityParams("service").setTypicalDuration(3600)); - config.planCalcScore().addActivityParams(new PlanCalcScoreConfigGroup.ActivityParams("start").setTypicalDuration(3600)); - config.planCalcScore().addActivityParams(new PlanCalcScoreConfigGroup.ActivityParams("end").setTypicalDuration(3600)); + config.planCalcScore().addActivityParams(new PlanCalcScoreConfigGroup.ActivityParams("commercial_start").setTypicalDuration(3600)); + config.planCalcScore().addActivityParams(new PlanCalcScoreConfigGroup.ActivityParams("commercial_end").setTypicalDuration(3600)); + + SimWrapperConfigGroup simWrapper = ConfigUtils.addOrGetModule(config, SimWrapperConfigGroup.class); + + // Path is relative to config + simWrapper.defaultParams().shp = "../leipzig-utm32n/leipzig-utm32n.shp"; + simWrapper.defaultParams().mapCenter = "12.38,51.34"; + simWrapper.defaultParams().mapZoomLevel = 10.3; // freight is long-haul freight // freightTraffic is rather short distance - for (String subpopulation : List.of("freight", "freightTraffic", "businessTraffic", "businessTraffic_service")) { + for (String subpopulation : List.of("outside_person", "freight", "freightTraffic", "businessTraffic", "businessTraffic_service")) { config.strategy().addStrategySettings( new StrategyConfigGroup.StrategySettings() .setStrategyName(DefaultPlanStrategiesModule.DefaultSelector.ChangeExpBeta) @@ -184,6 +192,8 @@ protected Config prepareConfig(Config config) { config.qsim().setFlowCapFactor(sample.getSize() / 100.0); config.qsim().setStorageCapFactor(sample.getSize() / 100.0); + + simWrapper.defaultParams().sampleSize = String.valueOf(sample.getSample()); } @@ -318,7 +328,8 @@ public void install() { } - bind(new TypeLiteral>() {}).toInstance(new ForceInnovationStrategyChooser<>(10, ForceInnovationStrategyChooser.Permute.yes)); + bind(new TypeLiteral>() { + }).toInstance(new ForceInnovationStrategyChooser<>(10, ForceInnovationStrategyChooser.Permute.yes)); } }); diff --git a/src/main/java/org/matsim/run/prepare/NetworkOptions.java b/src/main/java/org/matsim/run/prepare/NetworkOptions.java index e5df8e7a..b87deefb 100644 --- a/src/main/java/org/matsim/run/prepare/NetworkOptions.java +++ b/src/main/java/org/matsim/run/prepare/NetworkOptions.java @@ -4,6 +4,7 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.application.options.ShpOptions; import org.matsim.core.utils.io.IOUtils; +import org.matsim.run.RunLeipzigScenario; import org.matsim.utils.gis.shp2matsim.ShpGeometryUtils; import picocli.CommandLine; @@ -27,7 +28,7 @@ public class NetworkOptions { private Path parkingCapacitiesArea; @CommandLine.Option(names = "--parking-capacities-input", description = "Path to csv file containing parking capacity data per link") private Path inputParkingCapacities; - @CommandLine.Option(names = "--parking-cost-area", description = "Path to SHP file specifying parking cost area") + @CommandLine.Option(names = "--parking-cost-area", description = "Path to SHP file specifying parking cost area", required = true, defaultValue = "input/v" + RunLeipzigScenario.VERSION + "/parkingCostArea/Bewohnerparken_2020.shp") private Path parkingCostArea; @CommandLine.Option(names = "--slow-speed-area", description = "Path to SHP file specifying area of adapted speed") private Path slowSpeedArea; @@ -45,14 +46,15 @@ public boolean hasCarFreeArea() { * Return whether a drt area is defined. */ public boolean hasDrtArea() { - return isDefined(drtArea); } + return isDefined(drtArea); + } /** * Return whether a parkingCost area is defined. */ public boolean hasParkingCostArea() { - return isDefined(parkingCostArea); } - + return isDefined(parkingCostArea); + } /** * Prepare network with given options. @@ -78,12 +80,12 @@ public void prepare(Network network) { if (isDefined(slowSpeedArea)) { if (!Files.exists(slowSpeedArea)) { throw new IllegalArgumentException("Path to slow speed area not found: " + slowSpeedArea); - } else if (slowSpeedRelativeChange==null) { + } else if (slowSpeedRelativeChange == null) { throw new IllegalArgumentException("No relative change value for freeSpeed defined: " + slowSpeedArea); } else { PrepareNetwork.prepareSlowSpeed(network, - ShpGeometryUtils.loadPreparedGeometries(IOUtils.resolveFileOrResource(new ShpOptions(slowSpeedArea, null, null).getShapeFile().toString())), - slowSpeedRelativeChange); + ShpGeometryUtils.loadPreparedGeometries(IOUtils.resolveFileOrResource(new ShpOptions(slowSpeedArea, null, null).getShapeFile().toString())), + slowSpeedRelativeChange); } } diff --git a/src/main/java/org/matsim/run/prepare/PreparePopulation.java b/src/main/java/org/matsim/run/prepare/PreparePopulation.java index cf274ab3..49455df6 100644 --- a/src/main/java/org/matsim/run/prepare/PreparePopulation.java +++ b/src/main/java/org/matsim/run/prepare/PreparePopulation.java @@ -42,6 +42,9 @@ public class PreparePopulation implements MATSimAppCommand { @CommandLine.Mixin private ShpOptions shp; + @CommandLine.Option(names = "--phase", description = "Run pre or post phase", required = true) + private Phase phase; + public static void main(String[] args) { new PreparePopulation().execute(args); } @@ -68,6 +71,23 @@ public Integer call() throws Exception { ShpOptions.Index index = shp.createIndex(RunLeipzigScenario.CRS, "_"); + switch (phase) { + case pre -> prePhase(population); + case post -> postPhase(population, index); + default -> throw new IllegalArgumentException("Illegal phase"); + } + + + PopulationUtils.writePopulation(population, output.toString()); + + return 0; + } + + /** + * This steps runs before any other steps. + */ + private void prePhase(Population population) { + Set> toRemove = new HashSet<>(); for (Person person : population.getPersons().values()) { @@ -75,19 +95,8 @@ public Integer call() throws Exception { List activities = TripStructureUtils.getActivities(person.getSelectedPlan(), TripStructureUtils.StageActivityHandling.ExcludeStageActivities); for (Activity act : activities) { // Remove persons having activities after 27hours - if (act.getStartTime().isDefined() && act.getStartTime().seconds() > 27 * 3600) toRemove.add(person.getId()); - - // Reduce unnecessary precision - if (act.getCoord() != null) { - act.setCoord(new Coord(round(act.getCoord().getX()), round(act.getCoord().getY()))); - } - } - - Coord home = setHomeCoordinate(person); - if (home == null || !index.contains(home)) { - PopulationUtils.putSubpopulation(person, "outside_person"); } // Set car availability to "never" for agents below 18 years old @@ -154,9 +163,32 @@ public Integer call() throws Exception { log.info("Removing {} out of {} persons with activities outside valid range", toRemove.size(), population.getPersons().size()); toRemove.forEach(population::removePerson); - PopulationUtils.writePopulation(population, output.toString()); + } - return 0; + /** + * This step runs at the very end. + */ + private void postPhase(Population population, ShpOptions.Index index) { + + for (Person person : population.getPersons().values()) { + + List activities = TripStructureUtils.getActivities(person.getSelectedPlan(), TripStructureUtils.StageActivityHandling.ExcludeStageActivities); + for (Activity act : activities) { + // Reduce unnecessary precision + if (act.getCoord() != null) { + act.setCoord(new Coord(round(act.getCoord().getX()), round(act.getCoord().getY()))); + } + } + + // Only handles persons + if (!"person".equals(PopulationUtils.getSubpopulation(person))) + continue; + + Coord home = setHomeCoordinate(person); + if (home == null || !index.contains(home)) { + PopulationUtils.putSubpopulation(person, "outside_person"); + } + } } /** @@ -183,4 +215,7 @@ private Coord setHomeCoordinate(Person person) { return null; } + + enum Phase {pre, post} + } diff --git a/src/main/resources/META-INF/services/org.matsim.simwrapper.DashboardProvider b/src/main/resources/META-INF/services/org.matsim.simwrapper.DashboardProvider new file mode 100644 index 00000000..8c8552bb --- /dev/null +++ b/src/main/resources/META-INF/services/org.matsim.simwrapper.DashboardProvider @@ -0,0 +1 @@ +org.matsim.dashboard.LeipzigDashboardProvider \ No newline at end of file diff --git a/src/main/resources/mode_share_per_dist_ref.csv b/src/main/resources/mode_share_per_dist_ref.csv new file mode 100644 index 00000000..50d37348 --- /dev/null +++ b/src/main/resources/mode_share_per_dist_ref.csv @@ -0,0 +1,31 @@ +dist_group,main_mode,mean_dist,share +0 - 1000,bike,647.1320211664759,0.18648889960816117 +0 - 1000,car,715.5733490038705,0.05234787770685268 +0 - 1000,pt,802.3537970116731,0.011283809577320864 +0 - 1000,ride,729.0617251936227,0.030663712677327124 +0 - 1000,walk,499.1497285817478,0.7192157004303381 +1000 - 2000,bike,1410.8376133243098,0.32720226728480256 +1000 - 2000,car,1500.735907403122,0.16968403169776486 +1000 - 2000,pt,1533.2653903592868,0.11751538922105827 +1000 - 2000,ride,1487.1708306254736,0.09512585828066737 +1000 - 2000,walk,1302.3249718162533,0.2904724535157069 +2000 - 5000,bike,3202.914928652807,0.32502884429891704 +2000 - 5000,car,3405.7134444946573,0.2673536802459596 +2000 - 5000,pt,3498.796011328994,0.25034686219019325 +2000 - 5000,ride,3493.776275422576,0.11424498215353364 +2000 - 5000,walk,2618.6861353776153,0.043025631111396456 +5000 - 10000,bike,6488.004797551106,0.13390529274426183 +5000 - 10000,car,7286.224192866411,0.46657794968496363 +5000 - 10000,pt,6911.388240153839,0.2673883884644953 +5000 - 10000,ride,7091.697700844432,0.12398400299689334 +5000 - 10000,walk,8512.500292899185,0.008144366109385872 +10000 - 20000,bike,11870.106118880658,0.05597065369100367 +10000 - 20000,car,13314.789717119513,0.6443810864079137 +10000 - 20000,pt,13268.066606129496,0.21199175918221977 +10000 - 20000,ride,12510.881840420803,0.08765650071886284 +10000 - 20000,walk,,0.0 +20000+,bike,39557.16129260366,0.008270651122581562 +20000+,car,45145.502240349495,0.7529385110988895 +20000+,pt,39740.679248392946,0.1367767540286853 +20000+,ride,38947.640630839625,0.10201408374984364 +20000+,walk,,0.0 diff --git a/src/main/resources/mode_share_ref.csv b/src/main/resources/mode_share_ref.csv new file mode 100644 index 00000000..7f9bdd64 --- /dev/null +++ b/src/main/resources/mode_share_ref.csv @@ -0,0 +1,31 @@ +dist_group,main_mode,mean_dist,share +0 - 1000,bike,647.1320211664759,0.04360576137790145 +0 - 1000,car,715.5733490038705,0.012240240940456974 +0 - 1000,pt,802.3537970116731,0.002638436437215161 +0 - 1000,ride,729.0617251936227,0.00716994170043107 +0 - 1000,walk,499.1497285817478,0.16817058966030335 +1000 - 2000,bike,1410.8376133243098,0.05161256124243341 +1000 - 2000,car,1500.735907403122,0.026765790929684893 +1000 - 2000,pt,1533.2653903592868,0.018536760987114285 +1000 - 2000,ride,1487.1708306254736,0.015005058574293151 +1000 - 2000,walk,1302.3249718162533,0.04581883683363964 +2000 - 5000,bike,3202.914928652807,0.09292657641457054 +2000 - 5000,car,3405.7134444946573,0.07643709976165831 +2000 - 5000,pt,3498.796011328994,0.07157480705949293 +2000 - 5000,ride,3493.776275422576,0.032662932075985494 +2000 - 5000,walk,2618.6861353776153,0.012301137783270974 +5000 - 10000,bike,6488.004797551106,0.025021931407638667 +5000 - 10000,car,7286.224192866411,0.08718610903327519 +5000 - 10000,pt,6911.388240153839,0.04996496985474348 +5000 - 10000,ride,7091.697700844432,0.023168010427770593 +5000 - 10000,walk,8512.500292899185,0.0015218798747332046 +10000 - 20000,bike,11870.106118880658,0.0042008516746133645 +10000 - 20000,car,13314.789717119513,0.04836372612101472 +10000 - 20000,pt,13268.066606129496,0.015910944000784487 +10000 - 20000,ride,12510.881840420803,0.0065790183525187155 +10000 - 20000,walk,,0.0 +20000+,bike,39557.16129260366,0.000501334015678044 +20000+,car,45145.502240349495,0.04564014147534626 +20000+,pt,39740.679248392946,0.008290863480069695 +20000+,ride,38947.640630839625,0.006183688503361974 +20000+,walk,,0.0 diff --git a/src/main/resources/mode_users_ref.csv b/src/main/resources/mode_users_ref.csv new file mode 100644 index 00000000..5362f73a --- /dev/null +++ b/src/main/resources/mode_users_ref.csv @@ -0,0 +1,6 @@ +main_mode,user +walk,0.34795408095941294 +car,0.3507092997070994 +ride,0.16356884852805617 +bike,0.26412581692795317 +pt,0.2773855049775048 From e85f5c7906b048eea9d8691a8ea7b8f3a7707918 Mon Sep 17 00:00:00 2001 From: rakow Date: Wed, 21 Jun 2023 21:20:20 +0200 Subject: [PATCH 058/106] update calibrate script --- src/main/python/calibrate.py | 68 ++++++++++++------------------------ 1 file changed, 22 insertions(+), 46 deletions(-) diff --git a/src/main/python/calibrate.py b/src/main/python/calibrate.py index 0db66fe6..467a968c 100644 --- a/src/main/python/calibrate.py +++ b/src/main/python/calibrate.py @@ -3,60 +3,36 @@ import os -import geopandas as gpd import pandas as pd -from matsim import calibration - -# %% - -if os.path.exists("srv.csv"): - srv = pd.read_csv("srv.csv") - sim = pd.read_csv("sim.csv") - - _, adj = calibration.calc_adjusted_mode_share(sim, srv) - - print(srv.groupby("mode").sum()) - - print("Adjusted") - print(adj.groupby("mode").sum()) +import geopandas as gpd +import numpy as np - adj.to_csv("srv_adj.csv", index=False) +import calibration -# %% +#%% modes = ["walk", "car", "ride", "pt", "bike"] fixed_mode = "walk" initial = { - "bike": -1.9, - "pt": -0.7, - "car": -1.4, - "ride": -4 + "bike": -0.56, + "pt": 0.01, + "car": -0.799, + "ride": -5.30 } -# Original target +# Mode share target target = { - "walk": 0.243, - "bike": 0.206, - "pt": 0.162, - "car": 0.301, - "ride": 0.086 + "walk": 0.227812, + "bike": 0.217869, + "pt": 0.166917, + "car": 0.296633, + "ride": 0.090769 } -# Adjusted for distance distribution -# target = { -# "bike": 0.205680, -# "car": 0.321617, -# "pt": 0.186261, -# "ride": 0.093713, -# "walk": 0.192729 -# } city = gpd.read_file("../scenarios/input/leipzig-utm32n/leipzig-utm32n.shp") -homes = pd.read_csv("../input/v1.2/leipzig-v1.2-homes.csv", dtype={"person": "str"}) - def f(persons): - persons = pd.merge(persons, homes, how="inner", left_on="person", right_on="person") persons = gpd.GeoDataFrame(persons, geometry=gpd.points_from_xy(persons.home_x, persons.home_y)) df = gpd.sjoin(persons.set_crs("EPSG:25832"), city, how="inner", op="intersects") @@ -65,20 +41,20 @@ def f(persons): return df - def filter_modes(df): return df[df.main_mode.isin(modes)] - -study, obj = calibration.create_mode_share_study("calib", "matsim-leipzig-1.2-SNAPSHOT-25ee44a.jar", +study, obj = calibration.create_mode_share_study("calib", "matsim-leipzig-1.2-SNAPSHOT-8ad10fc.jar", "../input/v1.2/leipzig-v1.2-25pct.config.xml", modes, target, initial_asc=initial, - args="--25pct --config:TimeAllocationMutator.mutationRange=900", - jvm_args="-Xmx46G -Xms46G -XX:+AlwaysPreTouch -XX:+UseParallelGC", - person_filter=f, map_trips=filter_modes, + args="--10pct", + jvm_args="-Xmx46G -Xms46G -XX:+AlwaysPreTouch", + transform_persons=f, transform_trips=filter_modes, + lr=calibration.linear_lr_scheduler(start=0.3, interval=6), chain_runs=calibration.default_chain_scheduler) -# %% -study.optimize(obj, 10) +#%% + +study.optimize(obj, 4) \ No newline at end of file From e8615eed9bcdbd07f626197c61e50a2bff22ae1c Mon Sep 17 00:00:00 2001 From: rakow Date: Wed, 21 Jun 2023 21:35:45 +0200 Subject: [PATCH 059/106] config update --- input/v1.2/leipzig-v1.2-25pct.config.xml | 2 +- src/main/python/calibrate.py | 26 ++++++++++-------------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/input/v1.2/leipzig-v1.2-25pct.config.xml b/input/v1.2/leipzig-v1.2-25pct.config.xml index 2d1cf0fc..e4160732 100644 --- a/input/v1.2/leipzig-v1.2-25pct.config.xml +++ b/input/v1.2/leipzig-v1.2-25pct.config.xml @@ -19,7 +19,7 @@ - + diff --git a/src/main/python/calibrate.py b/src/main/python/calibrate.py index 467a968c..334f533a 100644 --- a/src/main/python/calibrate.py +++ b/src/main/python/calibrate.py @@ -1,15 +1,10 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -import os - -import pandas as pd -import geopandas as gpd -import numpy as np - import calibration +import geopandas as gpd -#%% +# %% modes = ["walk", "car", "ride", "pt", "bike"] fixed_mode = "walk" @@ -20,7 +15,7 @@ "ride": -5.30 } -# Mode share target +# Original target target = { "walk": 0.227812, "bike": 0.217869, @@ -29,9 +24,9 @@ "ride": 0.090769 } - city = gpd.read_file("../scenarios/input/leipzig-utm32n/leipzig-utm32n.shp") + def f(persons): persons = gpd.GeoDataFrame(persons, geometry=gpd.points_from_xy(persons.home_x, persons.home_y)) @@ -41,20 +36,21 @@ def f(persons): return df + def filter_modes(df): return df[df.main_mode.isin(modes)] -study, obj = calibration.create_mode_share_study("calib", "matsim-leipzig-1.2-SNAPSHOT-8ad10fc.jar", + +study, obj = calibration.create_mode_share_study("calib", "matsim-leipzig-1.2-SNAPSHOT-e85f5c7.jar", "../input/v1.2/leipzig-v1.2-25pct.config.xml", modes, target, initial_asc=initial, - args="--10pct", - jvm_args="-Xmx46G -Xms46G -XX:+AlwaysPreTouch", + args="--10pct --parking-cost-area ../input/v1.2/parkingCostArea/Bewohnerparken_2020.shp --config:TimeAllocationMutator.mutationRange=900", + jvm_args="-Xmx46G -Xms46G -XX:+AlwaysPreTouch -XX:+UseParallelGC", transform_persons=f, transform_trips=filter_modes, lr=calibration.linear_lr_scheduler(start=0.3, interval=6), chain_runs=calibration.default_chain_scheduler) +# %% -#%% - -study.optimize(obj, 4) \ No newline at end of file +study.optimize(obj, 4) From 0b79dae69c0c8181a9d76ef3fcf60b0b38a00f89 Mon Sep 17 00:00:00 2001 From: rakow Date: Thu, 22 Jun 2023 08:21:16 +0200 Subject: [PATCH 060/106] add subtour replanning that uses the leipzig re routing --- Makefile | 4 +- input/v1.2/leipzig-v1.2-25pct.config.xml | 2 +- .../matsim/run/LeipzigSubtourModeChoice.java | 62 +++++++++++++++++++ .../org/matsim/run/RunLeipzigScenario.java | 57 +++++++---------- 4 files changed, 86 insertions(+), 39 deletions(-) create mode 100644 src/main/java/org/matsim/run/LeipzigSubtourModeChoice.java diff --git a/Makefile b/Makefile index a341dd9a..a682b3a2 100644 --- a/Makefile +++ b/Makefile @@ -146,14 +146,14 @@ input/$V/leipzig-$V-25pct.plans-initial.xml.gz: input/plans-longHaulFreight.xml. --input input/prepare-25pct.plans-with-trips.xml.gz\ --output $@ - $(sc) prepare fix-subtour-modes --input $@ --coord-dist 100 --output $@ - $(sc) prepare merge-populations $@ $^ --output $@ $(sc) prepare population $@ --phase post\ --shp $(shared)/matsim-input-files/senozon/20210520_leipzig/dilutionArea.shp --shp-crs $(CRS)\ --output $@ + $(sc) prepare fix-subtour-modes --input $@ --coord-dist 100 --output $@ + $(sc) prepare extract-home-coordinates $@ --csv input/$V/leipzig-$V-homes.csv $(sc) prepare downsample-population $@\ diff --git a/input/v1.2/leipzig-v1.2-25pct.config.xml b/input/v1.2/leipzig-v1.2-25pct.config.xml index e4160732..97c36fdc 100644 --- a/input/v1.2/leipzig-v1.2-25pct.config.xml +++ b/input/v1.2/leipzig-v1.2-25pct.config.xml @@ -84,7 +84,7 @@ - + diff --git a/src/main/java/org/matsim/run/LeipzigSubtourModeChoice.java b/src/main/java/org/matsim/run/LeipzigSubtourModeChoice.java new file mode 100644 index 00000000..bbec7421 --- /dev/null +++ b/src/main/java/org/matsim/run/LeipzigSubtourModeChoice.java @@ -0,0 +1,62 @@ +package org.matsim.run; + +import jakarta.inject.Inject; +import jakarta.inject.Provider; +import org.matsim.api.core.v01.Scenario; +import org.matsim.core.config.groups.GlobalConfigGroup; +import org.matsim.core.config.groups.SubtourModeChoiceConfigGroup; +import org.matsim.core.population.algorithms.PermissibleModesCalculator; +import org.matsim.core.population.algorithms.PlanAlgorithm; +import org.matsim.core.replanning.PlanStrategy; +import org.matsim.core.replanning.PlanStrategyImpl; +import org.matsim.core.replanning.modules.AbstractMultithreadedModule; +import org.matsim.core.replanning.selectors.RandomPlanSelector; +import org.matsim.core.router.MultimodalLinkChooser; +import org.matsim.core.router.SingleModeNetworksCache; +import org.matsim.core.router.TripRouter; +import org.matsim.core.utils.timing.TimeInterpretation; +import org.matsim.facilities.ActivityFacilities; + +/** + * Standard subtour mode choice, but replaced the re-routing. + */ +public class LeipzigSubtourModeChoice implements Provider { + + public static final String STRATEGY_NAME = "SubTourModeChoiceLeipzig"; + + @Inject + private Provider tripRouterProvider; + @Inject + private GlobalConfigGroup globalConfigGroup; + @Inject + private SubtourModeChoiceConfigGroup subtourModeChoiceConfigGroup; + @Inject + private ActivityFacilities facilities; + @Inject + private PermissibleModesCalculator permissibleModesCalculator; + @Inject + private TimeInterpretation timeInterpretation; + @Inject + private SingleModeNetworksCache singleModeNetworksCache; + @Inject + private Scenario scenario; + @Inject + private MultimodalLinkChooser linkChooser; + + @Override + public PlanStrategy get() { + PlanStrategyImpl.Builder builder = new PlanStrategyImpl.Builder(new RandomPlanSelector<>()); + builder.addStrategyModule(new org.matsim.core.replanning.modules.SubtourModeChoice(globalConfigGroup, subtourModeChoiceConfigGroup, permissibleModesCalculator)); + + // Re-routing + builder.addStrategyModule(new AbstractMultithreadedModule(globalConfigGroup) { + @Override + public PlanAlgorithm getPlanAlgoInstance() { + return new LeipzigRouterPlanAlgorithm(tripRouterProvider.get(), facilities, timeInterpretation, singleModeNetworksCache, scenario, linkChooser); + } + }); + + return builder.build(); + } + +} diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index 79562739..5d86826c 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -11,7 +11,6 @@ import org.matsim.analysis.personMoney.PersonMoneyEventsAnalysisModule; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.TransportMode; -import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Plan; import org.matsim.application.MATSimApplication; @@ -20,7 +19,6 @@ import org.matsim.application.analysis.population.SubTourAnalysis; import org.matsim.application.analysis.traffic.LinkStats; import org.matsim.application.options.SampleOptions; -import org.matsim.application.options.ShpOptions; import org.matsim.application.prepare.CreateLandUseShp; import org.matsim.application.prepare.freight.tripExtraction.ExtractRelevantFreightTrips; import org.matsim.application.prepare.network.CleanNetwork; @@ -44,6 +42,8 @@ import org.matsim.core.config.groups.*; import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; +import org.matsim.core.population.algorithms.PermissibleModesCalculator; +import org.matsim.core.population.algorithms.PermissibleModesCalculatorImpl; import org.matsim.core.replanning.choosers.ForceInnovationStrategyChooser; import org.matsim.core.replanning.choosers.StrategyChooser; import org.matsim.core.replanning.strategies.DefaultPlanStrategiesModule; @@ -90,18 +90,16 @@ public class RunLeipzigScenario extends MATSimApplication { * Coordinate system used in the scenario. */ public static final String CRS = "EPSG:25832"; - + /** + * Current version number. + */ public static final String VERSION = "1.2"; - private static final Logger log = LogManager.getLogger(RunLeipzigScenario.class); - @CommandLine.Mixin private final SampleOptions sample = new SampleOptions(1, 10, 25); @CommandLine.ArgGroup(heading = "%nNetwork options%n", exclusive = false, multiplicity = "0..1") private final NetworkOptions networkOpt = new NetworkOptions(); - @CommandLine.Option(names = "--relativeSpeedChange", defaultValue = "1", description = "provide a value that is bigger then 0.0 and smaller then 1.0, else the speed will be reduced to 20 km/h") - Double relativeSpeedChange; @CommandLine.Option(names = "--bikes", defaultValue = "onNetworkWithStandardMatsim", description = "Define how bicycles are handled") private BicycleHandling bike; @@ -110,8 +108,6 @@ public class RunLeipzigScenario extends MATSimApplication { private Double parkingCostTimePeriodStart; @CommandLine.Option(names = "--parking-cost-time-period-end", defaultValue = "0", description = "End of time period for which parking cost will be charged.") private Double parkingCostTimePeriodEnd; - @CommandLine.Mixin - private ShpOptions shp; public RunLeipzigScenario(@Nullable Config config) { super(config); @@ -134,16 +130,15 @@ private static void adjustStrategiesForParking(Config config) { config.strategy().clearStrategySettings(); for (StrategyConfigGroup.StrategySettings strategySetting : modifiableCollectionOfOldStrategySettings) { + if (strategySetting.getStrategyName().equals("ReRoute")) { - StrategyConfigGroup.StrategySettings newReRouteStrategy = new StrategyConfigGroup.StrategySettings(); - newReRouteStrategy.setStrategyName(LeipzigRoutingStrategyProvider.STRATEGY_NAME); - newReRouteStrategy.setSubpopulation(strategySetting.getSubpopulation()); - newReRouteStrategy.setWeight(strategySetting.getWeight()); - newReRouteStrategy.setDisableAfter(strategySetting.getDisableAfter()); - config.strategy().addStrategySettings(newReRouteStrategy); - } else { - config.strategy().addStrategySettings(strategySetting); + strategySetting.setStrategyName(LeipzigRoutingStrategyProvider.STRATEGY_NAME); + } else if (strategySetting.getStrategyName().equals("SubtourModeChoice")) { + strategySetting.setStrategyName(LeipzigSubtourModeChoice.STRATEGY_NAME); } + + config.strategy().addStrategySettings(strategySetting); + } } @@ -216,8 +211,8 @@ protected Config prepareConfig(Config config) { config.qsim().setUsePersonIdForMissingVehicleId(false); - // this is how it is supposed to be - config.facilities().setFacilitiesSource(FacilitiesConfigGroup.FacilitiesSource.onePerActivityLinkInPlansFile); + // We need to use coordinates only, otherwise subtour constraints will be violated by the parking re-routing, because it may change link/facility ids + config.facilities().setFacilitiesSource(FacilitiesConfigGroup.FacilitiesSource.none); switch ((bike)) { case onNetworkWithStandardMatsim -> { @@ -270,19 +265,6 @@ protected Config prepareConfig(Config config) { @Override protected void prepareScenario(Scenario scenario) { - // TODO: can be removed once v1.2 is done, because this is done in the preparation phase - for (Link link : scenario.getNetwork().getLinks().values()) { - Set modes = link.getAllowedModes(); - - // allow freight traffic together with cars - if (modes.contains("car")) { - Set newModes = Sets.newHashSet(modes); - newModes.add("freight"); - - link.setAllowedModes(newModes); - } - } - if (networkOpt.hasDrtArea()) { scenario.getPopulation().getFactory().getRouteFactories().setRouteFactory(DrtRoute.class, new DrtRouteFactory()); // (matsim core does not know about DRT routes. This makes it possible to read them before the controler is there.) @@ -313,6 +295,13 @@ public void install() { // Plots how many different modes agents tried out addControlerListenerBinding().to(ModeChoiceCoverageControlerListener.class); + // Leipzig specific planning strategies + this.addPersonPrepareForSimAlgorithm().to(LeipzigRouterPlanAlgorithm.class); + this.addPlanStrategyBinding(LeipzigRoutingStrategyProvider.STRATEGY_NAME).toProvider(LeipzigRoutingStrategyProvider.class); + this.addPlanStrategyBinding(LeipzigSubtourModeChoice.STRATEGY_NAME).toProvider(LeipzigSubtourModeChoice.class); + + bind(PermissibleModesCalculator.class).to(PermissibleModesCalculatorImpl.class); + if (networkOpt.hasCarFreeArea()) { bind(MultimodalLinkChooser.class).to(CarfreeMultimodalLinkChooser.class); } @@ -320,14 +309,10 @@ public void install() { if (networkOpt.hasParkingCostArea()) { this.addEventHandlerBinding().toInstance(new TimeRestrictedParkingCostHandler(parkingCostTimePeriodStart, parkingCostTimePeriodEnd)); - this.addPersonPrepareForSimAlgorithm().to(LeipzigRouterPlanAlgorithm.class); - this.addPlanStrategyBinding(LeipzigRoutingStrategyProvider.STRATEGY_NAME).toProvider(LeipzigRoutingStrategyProvider.class); install(new PersonMoneyEventsAnalysisModule()); - } - bind(new TypeLiteral>() { }).toInstance(new ForceInnovationStrategyChooser<>(10, ForceInnovationStrategyChooser.Permute.yes)); } From 43ac678053362ffdcb228eaa32adf6dc061cb014 Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Thu, 22 Jun 2023 14:51:07 +0200 Subject: [PATCH 061/106] adding counter to parking locations analysis --- .../org/matsim/analysis/ParkingLocation.java | 27 ++++++------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/matsim/analysis/ParkingLocation.java b/src/main/java/org/matsim/analysis/ParkingLocation.java index 1a50d292..a56ddf2b 100644 --- a/src/main/java/org/matsim/analysis/ParkingLocation.java +++ b/src/main/java/org/matsim/analysis/ParkingLocation.java @@ -42,7 +42,6 @@ public Integer call() throws Exception { EventsManager manager = EventsUtils.createEventsManager(); Network network = NetworkUtils.readNetwork(String.valueOf(networkPath)); List listOfParkingActivities = new ArrayList<>(); - manager.addHandler(new ParkingActivites(listOfParkingActivities, network)); manager.initProcessing(); MatsimEventsReader matsimEventsReader = new MatsimEventsReader(manager); @@ -55,7 +54,6 @@ public Integer call() throws Exception { private static class ParkingActivites implements ActivityStartEventHandler { - private final List listOfParkingData; private final Network network; @@ -74,18 +72,13 @@ public void handleEvent(ActivityStartEvent activityStartEvent) { } } - - private static void writeResults(Path outputFolder, List listOfParkingData) throws IOException { BufferedWriter writer = IOUtils.getBufferedWriter(outputFolder.toString() + "/parkingActivities.tsv"); - BufferedWriter writerWithCounts = IOUtils.getBufferedWriter(outputFolder.toString() + "/parkingActivitiesWithCount.tsv"); writer.write("x" + "\t" + "y" + "\t" + "linkId" ); writer.newLine(); for (int i = 0; i < listOfParkingData.size(); i++) { - ParkingData pd = listOfParkingData.get(i); writer.write(pd.coord.getX() + "\t" + pd.coord.getY() + "\t" + pd.linkId); - // writer.write(listOfIds.get(i).toString()); writer.newLine(); } writer.close(); @@ -96,20 +89,16 @@ private static void writeResults(Path outputFolder, List listOfPark Collectors.toMap(Function.identity(), company -> 1, Math::addExact) ); + BufferedWriter writerWithCounts = IOUtils.getBufferedWriter(outputFolder.toString() + "/parkingActivitiesWithCount.tsv"); - writerWithCounts.write(("x" + "\t" + "y" + "\t" + "linkId" + "count")); + writerWithCounts.write(("x" + "\t" + "y" + "\t" + "linkId" + "\t" + "count")); writerWithCounts.newLine(); - duplicateCountMap.forEach( - (key, value) -> { - try { - writerWithCounts.write( key.coord.getX() + "\t" + key.coord.getY() +"\t" - + key.linkId + "\t" + value); - writerWithCounts.newLine(); - } catch (IOException e) { - e.printStackTrace(); - } - } - ); + for(ParkingData parkingData: duplicateCountMap.keySet()) { + writerWithCounts.write( parkingData.coord.getX() + "\t" + parkingData.coord.getY() +"\t" + + parkingData.linkId + "\t" + duplicateCountMap.get(parkingData)); + writerWithCounts.newLine(); + } + writerWithCounts.close(); } record ParkingData (Coord coord, Id linkId) {} From 61762ff75fc8f6fc68be1de6a58f44ca2ff510b1 Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Tue, 27 Jun 2023 20:20:22 +0200 Subject: [PATCH 062/106] only adding the necessary nodes to the reduced network, to fix wrong parking locations --- .../java/org/matsim/analysis/ParkingLocation.java | 5 +++-- .../org/matsim/run/LeipzigRouterPlanAlgorithm.java | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/matsim/analysis/ParkingLocation.java b/src/main/java/org/matsim/analysis/ParkingLocation.java index a56ddf2b..78295703 100644 --- a/src/main/java/org/matsim/analysis/ParkingLocation.java +++ b/src/main/java/org/matsim/analysis/ParkingLocation.java @@ -6,6 +6,7 @@ import org.matsim.api.core.v01.events.handler.ActivityStartEventHandler; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.population.Person; import org.matsim.application.MATSimAppCommand; import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.core.events.EventsUtils; @@ -66,7 +67,7 @@ private static class ParkingActivites implements ActivityStartEventHandler { public void handleEvent(ActivityStartEvent activityStartEvent) { if (activityStartEvent.getActType().equals("parking interaction")) { Link l = network.getLinks().get(activityStartEvent.getLinkId()); - ParkingData pd = new ParkingData(l.getCoord(), activityStartEvent.getLinkId()); + ParkingData pd = new ParkingData(activityStartEvent.getPersonId(), l.getCoord(), activityStartEvent.getLinkId()); listOfParkingData.add(pd); } } @@ -101,5 +102,5 @@ private static void writeResults(Path outputFolder, List listOfPark writerWithCounts.close(); } - record ParkingData (Coord coord, Id linkId) {} + record ParkingData (Id personId, Coord coord, Id linkId) {} } diff --git a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index 6644dc1e..0fa6e29e 100644 --- a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -51,11 +51,21 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm, PersonPrepareFo // yyyy one should look at the networks cache and see how the following is done. And maybe even register it there. this.reducedNetwork = NetworkUtils.createNetwork(scenario.getConfig().network()); this.linkChooser = linkChooser; - for (Node node : this.fullModalNetwork.getNodes().values()) { + + /*for (Node node : this.fullModalNetwork.getNodes().values()) { reducedNetwork.addNode(node); - } + }*/ + for (Link link : this.fullModalNetwork.getLinks().values()) { if (!LeipzigUtils.isLinkParkingTypeInsideResidentialArea(link)) { + Node fromNode = link.getFromNode(); + Node tNode = link.getToNode(); + if (!reducedNetwork.getNodes().containsKey(fromNode.getId())){ + reducedNetwork.addNode(fromNode); + } + if (!reducedNetwork.getNodes().containsKey(tNode.getId())){ + reducedNetwork.addNode(tNode); + } reducedNetwork.addLink(link); } } From 924fef18bf92713a6de3c02dae44a375ed136d4a Mon Sep 17 00:00:00 2001 From: simei94 Date: Thu, 6 Jul 2023 20:56:04 +0200 Subject: [PATCH 063/106] change method for checking if link is inside of geometry. it is more precise now --- src/main/java/org/matsim/run/prepare/PrepareNetwork.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/matsim/run/prepare/PrepareNetwork.java b/src/main/java/org/matsim/run/prepare/PrepareNetwork.java index d48b29bd..ba4a1799 100644 --- a/src/main/java/org/matsim/run/prepare/PrepareNetwork.java +++ b/src/main/java/org/matsim/run/prepare/PrepareNetwork.java @@ -166,17 +166,16 @@ static void prepareParkingCost(Network network, ShpOptions parkingCostShape) { if (!link.getAllowedModes().contains("pt")) { - GeometryFactory gf = new GeometryFactory(); - - LineString line = gf.createLineString(new Coordinate[]{MGC.coord2Coordinate(link.getFromNode().getCoord()), - MGC.coord2Coordinate(link.getToNode().getCoord())}); double oneHourPCost = 0.; double extraHourPCost = 0.; double resPFee = 0.; for (SimpleFeature feature : features) { Geometry geometry = (Geometry) feature.getDefaultGeometry(); - boolean linkInShp = line.intersects(geometry); + // Here we have to use a different logic for checking whether a link is inside the given geometry. + // The standard procedure (as used in the other methods of this class) did include some link out of the geometry. + //for this case we have to be more precise than that. -sme0723 + boolean linkInShp = MGC.coord2Point(link.getCoord()).within(geometry); if (linkInShp && feature.getAttribute(hourlyParkingCostAttrName) != null) { From fefa2025bd4ba07ef16f4b1076765cfb1261b92e Mon Sep 17 00:00:00 2001 From: rakow Date: Fri, 12 May 2023 20:14:55 +0200 Subject: [PATCH 064/106] commercial traffic updates --- Makefile | 4 +- input/v1.2/leipzig-v1.2-25pct.config.xml | 55 +++--- .../org/matsim/run/StrategyWeightFadeout.java | 167 ------------------ 3 files changed, 27 insertions(+), 199 deletions(-) delete mode 100644 src/main/java/org/matsim/run/StrategyWeightFadeout.java diff --git a/Makefile b/Makefile index 12a29f6f..9127b894 100644 --- a/Makefile +++ b/Makefile @@ -76,7 +76,7 @@ input/$V/leipzig-$V-network.xml.gz: input/sumo.net.xml input/$V/leipzig-$V-network-with-pt.xml.gz: input/$V/leipzig-$V-network.xml.gz input/gtfs-lvb.zip $(sc) prepare transit-from-gtfs --network $< $(filter-out $<,$^)\ - --name leipzig-$V --date "2019-06-05" --target-crs $(CRS)\ + --name leipzig-$V --date "2023-04-19" --target-crs $(CRS)\ --output input/$V $(sc) prepare prepare-transit-schedule\ @@ -147,7 +147,7 @@ input/$V/leipzig-$V-25pct.plans-initial.xml.gz: input/plans-longHaulFreight.xml. $(sc) prepare fix-subtour-modes --input $@ --coord-dist 100 --output $@ - $(sc) prepare merge-populations $@ $< --output $@ + $(sc) prepare merge-populations $@ $^ --output $@ $(sc) prepare extract-home-coordinates $@ --csv input/$V/leipzig-$V-homes.csv diff --git a/input/v1.2/leipzig-v1.2-25pct.config.xml b/input/v1.2/leipzig-v1.2-25pct.config.xml index d8de8daf..f99dceb9 100644 --- a/input/v1.2/leipzig-v1.2-25pct.config.xml +++ b/input/v1.2/leipzig-v1.2-25pct.config.xml @@ -23,13 +23,13 @@ - + + value="./leipzig-v1.2-25pct.plans-initial.xml.gz"/> @@ -39,9 +39,9 @@ + value="./leipzig-v1.2-transitSchedule.xml.gz"/> + value="./leipzig-v1.2-transitVehicles.xml.gz"/> @@ -57,8 +57,8 @@ - - + + @@ -70,11 +70,24 @@ - - + + + + + + + + + + + + + + + @@ -87,43 +100,25 @@ - - - - - - - - - - - - - - + - + - - - - - - + @@ -308,4 +303,4 @@ - \ No newline at end of file + diff --git a/src/main/java/org/matsim/run/StrategyWeightFadeout.java b/src/main/java/org/matsim/run/StrategyWeightFadeout.java deleted file mode 100644 index 45563553..00000000 --- a/src/main/java/org/matsim/run/StrategyWeightFadeout.java +++ /dev/null @@ -1,167 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* - * Controler.java - * * - * *********************************************************************** * - * * - * copyright : (C) 2007 by the members listed in the COPYING, * - * LICENSE and WARRANTY file. * - * email : info at matsim dot org * - * * - * *********************************************************************** * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * See also COPYING, LICENSE and WARRANTY file * - * * - * *********************************************************************** */ - -package org.matsim.run; - -import com.google.inject.Binder; -import com.google.inject.Inject; -import com.google.inject.multibindings.Multibinder; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.matsim.api.core.v01.population.Person; -import org.matsim.api.core.v01.population.Plan; -import org.matsim.core.config.Config; -import org.matsim.core.config.groups.StrategyConfigGroup; -import org.matsim.core.controler.events.IterationStartsEvent; -import org.matsim.core.controler.listener.IterationStartsListener; -import org.matsim.core.replanning.GenericPlanStrategy; -import org.matsim.core.replanning.PlanStrategy; -import org.matsim.core.replanning.StrategyManager; - -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -/** - * Fade-out the strategy weight during the simulation. - * This class as well as the {@link Schedule}s has be bound with guice in the controller. - */ -public final class StrategyWeightFadeout implements IterationStartsListener { - - private final Logger log = LogManager.getLogger(StrategyWeightFadeout.class); - - @Inject - private Map planStrategies; - - @Inject - private Config config; - - @Inject - private StrategyManager strategyManager; - - @Inject - private Set schedules; - - @Override - public void notifyIterationStarts(IterationStartsEvent iterationStartsEvent) { - - for (Schedule s : schedules) { - - StrategyConfigGroup.StrategySettings settings = null; - - for (StrategyConfigGroup.StrategySettings strategySettings : planStrategies.keySet()) { - if (strategySettings.getStrategyName().equals(s.name) && strategySettings.getSubpopulation().equals(s.subpopulation)) { - settings = strategySettings; - break; - } - } - - if (settings == null) { - log.info("Strategy settings for {} not found", s.name); - continue; - } - - String strategyName = settings.getStrategyName(); - - if (Double.isNaN(s.initialWeight)) { - s.initialWeight = settings.getWeight(); - s.startIteration = (int) (config.controler().getLastIteration() * s.startAt); - double disable = config.strategy().getFractionOfIterationsToDisableInnovation(); - - // use disable after if it is set - if (!Double.isNaN(s.endAt)) - s.endIteration = (int) (config.controler().getLastIteration() * s.endAt); - else if (settings.getDisableAfter() > 0 && settings.getDisableAfter() < Integer.MAX_VALUE && settings.getDisableAfter() <= disable) - s.endIteration = settings.getDisableAfter(); - else if (Double.isFinite(disable) && disable < Integer.MAX_VALUE) - s.endIteration = (int) (config.controler().getLastIteration() * disable); - else - s.endIteration = settings.getDisableAfter(); - - log.info("{} fadeout from iteration {} to {} with start weight {}", strategyName, s.startIteration, s.endIteration, s.initialWeight); - } - - // Find the implementation to update the strategy weight - List> strategies = strategyManager.getStrategies(s.subpopulation); - Optional> strategy = strategies.stream().filter(st -> st.toString().contains(strategyName)).findFirst(); - - if (strategy.isEmpty()) { - log.warn("Could not find loaded strategy for {}", strategy); - return; - } - - if (iterationStartsEvent.getIteration() > s.startIteration && iterationStartsEvent.getIteration() <= s.endIteration) { - double step = s.initialWeight / (s.endIteration - s.startIteration); - double weight = s.initialWeight + step * (s.startIteration - iterationStartsEvent.getIteration()); - - log.info("Setting {} weight at iteration {} to {}", strategyName, iterationStartsEvent.getIteration(), weight); - - strategyManager.changeWeightOfStrategy(strategy.get(), s.subpopulation, weight); - } - } - } - - /** - * Get the binder which is needed to add {@link Schedule}. - */ - public static Multibinder getBinder(Binder binder) { - return Multibinder.newSetBinder(binder, StrategyWeightFadeout.Schedule.class); - } - - /** - * Defines the fade-out schedule for certain strategies. - */ - public static class Schedule { - - /** - * Start weight for fade-out. - */ - private double initialWeight = Double.NaN; - - /** - * Start and end iteration for fade-out. - */ - private int startIteration; - private int endIteration; - - private final String name; - private final String subpopulation; - private final double startAt; - private final double endAt; - - /** - * Constructor where the end is taken from the config and not given explicitly. - */ - public Schedule(String name, String subpopulation, double startAt) { - this.name = name; - this.subpopulation = subpopulation; - this.startAt = startAt; - this.endAt = Double.NaN; - } - - public Schedule(String name, String subpopulation, double startAt, double endAt) { - this.name = name; - this.subpopulation = subpopulation; - this.startAt = startAt; - this.endAt = endAt; - } - } -} From 7b6b2877bce7e727877f05dc5bcddd140042f337 Mon Sep 17 00:00:00 2001 From: rakow Date: Fri, 12 May 2023 20:16:47 +0200 Subject: [PATCH 065/106] remove unused imports --- src/main/java/org/matsim/run/RunLeipzigScenario.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index 10b4f813..c812523f 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -4,9 +4,7 @@ import ch.sbb.matsim.routing.pt.raptor.RaptorIntermodalAccessEgress; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; -import com.google.inject.Singleton; import com.google.inject.TypeLiteral; -import com.google.inject.multibindings.Multibinder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.matsim.analysis.*; From d89b46d355e38fd3676990b0df94cf62ede2ed3e Mon Sep 17 00:00:00 2001 From: rakow Date: Wed, 21 Jun 2023 21:06:07 +0200 Subject: [PATCH 066/106] setup trip dashboards --- Makefile | 7 +- input/v1.2/leipzig-v1.2-25pct.config.xml | 8 +-- .../parkingCostArea/Bewohnerparken_2020.cpg | 1 + .../parkingCostArea/Bewohnerparken_2020.dbf | Bin 0 -> 5098 bytes .../parkingCostArea/Bewohnerparken_2020.prj | 1 + .../parkingCostArea/Bewohnerparken_2020.qmd | 26 ++++++++ .../parkingCostArea/Bewohnerparken_2020.shp | Bin 0 -> 8228 bytes .../parkingCostArea/Bewohnerparken_2020.shx | Bin 0 -> 292 bytes pom.xml | 3 +- .../dashboard/LeipzigDashboardProvider.java | 25 +++++++ .../matsim/run/prepare/NetworkOptions.java | 16 +++-- .../matsim/run/prepare/PreparePopulation.java | 61 ++++++++++++++---- .../org.matsim.simwrapper.DashboardProvider | 1 + .../resources/mode_share_per_dist_ref.csv | 31 +++++++++ src/main/resources/mode_share_ref.csv | 31 +++++++++ src/main/resources/mode_users_ref.csv | 6 ++ 16 files changed, 191 insertions(+), 26 deletions(-) create mode 100644 input/v1.2/parkingCostArea/Bewohnerparken_2020.cpg create mode 100644 input/v1.2/parkingCostArea/Bewohnerparken_2020.dbf create mode 100644 input/v1.2/parkingCostArea/Bewohnerparken_2020.prj create mode 100644 input/v1.2/parkingCostArea/Bewohnerparken_2020.qmd create mode 100644 input/v1.2/parkingCostArea/Bewohnerparken_2020.shp create mode 100644 input/v1.2/parkingCostArea/Bewohnerparken_2020.shx create mode 100644 src/main/java/org/matsim/dashboard/LeipzigDashboardProvider.java create mode 100644 src/main/resources/META-INF/services/org.matsim.simwrapper.DashboardProvider create mode 100644 src/main/resources/mode_share_per_dist_ref.csv create mode 100644 src/main/resources/mode_share_ref.csv create mode 100644 src/main/resources/mode_users_ref.csv diff --git a/Makefile b/Makefile index 9127b894..a341dd9a 100644 --- a/Makefile +++ b/Makefile @@ -90,6 +90,7 @@ input/plans-longHaulFreight.xml.gz: input/$V/leipzig-$V-network.xml.gz --input-crs $(CRS)\ --target-crs $(CRS)\ --shp ../shared-svn/projects/NaMAV/data/shapefiles/freight-area/freight-area.shp\ + --cut-on-boundary\ --output $@ input/plans-commercialTraffic.xml.gz: @@ -118,7 +119,7 @@ input/$V/leipzig-$V-25pct.plans-initial.xml.gz: input/plans-longHaulFreight.xml. --population $(shared)/matsim-input-files/senozon/20210520_leipzig/population.xml.gz\ --attributes $(shared)/matsim-input-files/senozon/20210520_leipzig/personAttributes.xml.gz - $(sc) prepare population input/prepare-25pct.plans.xml.gz\ + $(sc) prepare population input/prepare-25pct.plans.xml.gz --phase pre\ --shp $(shared)/matsim-input-files/senozon/20210520_leipzig/dilutionArea.shp --shp-crs $(CRS)\ --output input/prepare-25pct.plans.xml.gz @@ -149,6 +150,10 @@ input/$V/leipzig-$V-25pct.plans-initial.xml.gz: input/plans-longHaulFreight.xml. $(sc) prepare merge-populations $@ $^ --output $@ + $(sc) prepare population $@ --phase post\ + --shp $(shared)/matsim-input-files/senozon/20210520_leipzig/dilutionArea.shp --shp-crs $(CRS)\ + --output $@ + $(sc) prepare extract-home-coordinates $@ --csv input/$V/leipzig-$V-homes.csv $(sc) prepare downsample-population $@\ diff --git a/input/v1.2/leipzig-v1.2-25pct.config.xml b/input/v1.2/leipzig-v1.2-25pct.config.xml index f99dceb9..2d1cf0fc 100644 --- a/input/v1.2/leipzig-v1.2-25pct.config.xml +++ b/input/v1.2/leipzig-v1.2-25pct.config.xml @@ -57,11 +57,11 @@ - + + - - - + + diff --git a/input/v1.2/parkingCostArea/Bewohnerparken_2020.cpg b/input/v1.2/parkingCostArea/Bewohnerparken_2020.cpg new file mode 100644 index 00000000..3ad133c0 --- /dev/null +++ b/input/v1.2/parkingCostArea/Bewohnerparken_2020.cpg @@ -0,0 +1 @@ +UTF-8 \ No newline at end of file diff --git a/input/v1.2/parkingCostArea/Bewohnerparken_2020.dbf b/input/v1.2/parkingCostArea/Bewohnerparken_2020.dbf new file mode 100644 index 0000000000000000000000000000000000000000..69dff29acab587bb20b74f3e804de44c6aa6246e GIT binary patch literal 5098 zcmds*Pfx-y6u_4dFB;?3cKZ2lT;-_1yYn#HDG@sQb*b<&_@1%pD)d z!Xz^rCN5CaFJpC>*x)8`1GigAuGzpC^9t0GfO4G;x4BjvMVElm-5en6Q5Z$XHY_~T zr-1HQLW0n8Lf_6T?T}W_m283?*GKnrG64cE-BpNWy169v4CV4a4_LfFmV*Y_cpGcI zXpxzXRMxw_Ap@nC&SQ&jag_FJNLEXW-J1ag&!bxmCacR|2kb(|uH2NMExs#PGEjyx zP;}vOg~fr@(jnk4rXF)w>e}ZheF2~;bKw$LQP;I@uM2m3btuB$VCqnWzroa@2*1J9 wp$L!m>QIDV#_CYoqT`A>lul((3|t+Q>O^~EpQFA0?Kg!{ooH|DeY8LO1Smyh!2kdN literal 0 HcmV?d00001 diff --git a/input/v1.2/parkingCostArea/Bewohnerparken_2020.prj b/input/v1.2/parkingCostArea/Bewohnerparken_2020.prj new file mode 100644 index 00000000..bd846aeb --- /dev/null +++ b/input/v1.2/parkingCostArea/Bewohnerparken_2020.prj @@ -0,0 +1 @@ +PROJCS["ETRS_1989_UTM_Zone_32N",GEOGCS["GCS_ETRS_1989",DATUM["D_ETRS_1989",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",500000.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",9.0],PARAMETER["Scale_Factor",0.9996],PARAMETER["Latitude_Of_Origin",0.0],UNIT["Meter",1.0]] \ No newline at end of file diff --git a/input/v1.2/parkingCostArea/Bewohnerparken_2020.qmd b/input/v1.2/parkingCostArea/Bewohnerparken_2020.qmd new file mode 100644 index 00000000..a18ad236 --- /dev/null +++ b/input/v1.2/parkingCostArea/Bewohnerparken_2020.qmd @@ -0,0 +1,26 @@ + + + + + + dataset + + + + + + + + + + 0 + 0 + + + + + false + + + + diff --git a/input/v1.2/parkingCostArea/Bewohnerparken_2020.shp b/input/v1.2/parkingCostArea/Bewohnerparken_2020.shp new file mode 100644 index 0000000000000000000000000000000000000000..24c433fd687da8d65df796738bbab594e0126813 GIT binary patch literal 8228 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 literal 0 HcmV?d00001 diff --git a/pom.xml b/pom.xml index 320c70bd..12df47f0 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,8 @@ org.matsim matsim-all - 16.0-PR2653 + 16.0-PR2657 + 4.0.0 diff --git a/src/main/java/org/matsim/dashboard/LeipzigDashboardProvider.java b/src/main/java/org/matsim/dashboard/LeipzigDashboardProvider.java new file mode 100644 index 00000000..f2fbb833 --- /dev/null +++ b/src/main/java/org/matsim/dashboard/LeipzigDashboardProvider.java @@ -0,0 +1,25 @@ +package org.matsim.dashboard; + +import org.matsim.core.config.Config; +import org.matsim.simwrapper.Dashboard; +import org.matsim.simwrapper.DashboardProvider; +import org.matsim.simwrapper.SimWrapper; +import org.matsim.simwrapper.dashboard.TripDashboard; + +import java.util.List; + +/** + * Provider for default dashboards in the scenario. + * Declared in META-INF/services + */ +public class LeipzigDashboardProvider implements DashboardProvider { + + @Override + public List getDashboards(Config config, SimWrapper simWrapper) { + + TripDashboard trips = new TripDashboard("mode_share_ref.csv", "mode_share_per_dist_ref.csv", "mode_users_ref.csv"); + + return List.of(trips); + } + +} diff --git a/src/main/java/org/matsim/run/prepare/NetworkOptions.java b/src/main/java/org/matsim/run/prepare/NetworkOptions.java index e5df8e7a..b87deefb 100644 --- a/src/main/java/org/matsim/run/prepare/NetworkOptions.java +++ b/src/main/java/org/matsim/run/prepare/NetworkOptions.java @@ -4,6 +4,7 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.application.options.ShpOptions; import org.matsim.core.utils.io.IOUtils; +import org.matsim.run.RunLeipzigScenario; import org.matsim.utils.gis.shp2matsim.ShpGeometryUtils; import picocli.CommandLine; @@ -27,7 +28,7 @@ public class NetworkOptions { private Path parkingCapacitiesArea; @CommandLine.Option(names = "--parking-capacities-input", description = "Path to csv file containing parking capacity data per link") private Path inputParkingCapacities; - @CommandLine.Option(names = "--parking-cost-area", description = "Path to SHP file specifying parking cost area") + @CommandLine.Option(names = "--parking-cost-area", description = "Path to SHP file specifying parking cost area", required = true, defaultValue = "input/v" + RunLeipzigScenario.VERSION + "/parkingCostArea/Bewohnerparken_2020.shp") private Path parkingCostArea; @CommandLine.Option(names = "--slow-speed-area", description = "Path to SHP file specifying area of adapted speed") private Path slowSpeedArea; @@ -45,14 +46,15 @@ public boolean hasCarFreeArea() { * Return whether a drt area is defined. */ public boolean hasDrtArea() { - return isDefined(drtArea); } + return isDefined(drtArea); + } /** * Return whether a parkingCost area is defined. */ public boolean hasParkingCostArea() { - return isDefined(parkingCostArea); } - + return isDefined(parkingCostArea); + } /** * Prepare network with given options. @@ -78,12 +80,12 @@ public void prepare(Network network) { if (isDefined(slowSpeedArea)) { if (!Files.exists(slowSpeedArea)) { throw new IllegalArgumentException("Path to slow speed area not found: " + slowSpeedArea); - } else if (slowSpeedRelativeChange==null) { + } else if (slowSpeedRelativeChange == null) { throw new IllegalArgumentException("No relative change value for freeSpeed defined: " + slowSpeedArea); } else { PrepareNetwork.prepareSlowSpeed(network, - ShpGeometryUtils.loadPreparedGeometries(IOUtils.resolveFileOrResource(new ShpOptions(slowSpeedArea, null, null).getShapeFile().toString())), - slowSpeedRelativeChange); + ShpGeometryUtils.loadPreparedGeometries(IOUtils.resolveFileOrResource(new ShpOptions(slowSpeedArea, null, null).getShapeFile().toString())), + slowSpeedRelativeChange); } } diff --git a/src/main/java/org/matsim/run/prepare/PreparePopulation.java b/src/main/java/org/matsim/run/prepare/PreparePopulation.java index cf274ab3..49455df6 100644 --- a/src/main/java/org/matsim/run/prepare/PreparePopulation.java +++ b/src/main/java/org/matsim/run/prepare/PreparePopulation.java @@ -42,6 +42,9 @@ public class PreparePopulation implements MATSimAppCommand { @CommandLine.Mixin private ShpOptions shp; + @CommandLine.Option(names = "--phase", description = "Run pre or post phase", required = true) + private Phase phase; + public static void main(String[] args) { new PreparePopulation().execute(args); } @@ -68,6 +71,23 @@ public Integer call() throws Exception { ShpOptions.Index index = shp.createIndex(RunLeipzigScenario.CRS, "_"); + switch (phase) { + case pre -> prePhase(population); + case post -> postPhase(population, index); + default -> throw new IllegalArgumentException("Illegal phase"); + } + + + PopulationUtils.writePopulation(population, output.toString()); + + return 0; + } + + /** + * This steps runs before any other steps. + */ + private void prePhase(Population population) { + Set> toRemove = new HashSet<>(); for (Person person : population.getPersons().values()) { @@ -75,19 +95,8 @@ public Integer call() throws Exception { List activities = TripStructureUtils.getActivities(person.getSelectedPlan(), TripStructureUtils.StageActivityHandling.ExcludeStageActivities); for (Activity act : activities) { // Remove persons having activities after 27hours - if (act.getStartTime().isDefined() && act.getStartTime().seconds() > 27 * 3600) toRemove.add(person.getId()); - - // Reduce unnecessary precision - if (act.getCoord() != null) { - act.setCoord(new Coord(round(act.getCoord().getX()), round(act.getCoord().getY()))); - } - } - - Coord home = setHomeCoordinate(person); - if (home == null || !index.contains(home)) { - PopulationUtils.putSubpopulation(person, "outside_person"); } // Set car availability to "never" for agents below 18 years old @@ -154,9 +163,32 @@ public Integer call() throws Exception { log.info("Removing {} out of {} persons with activities outside valid range", toRemove.size(), population.getPersons().size()); toRemove.forEach(population::removePerson); - PopulationUtils.writePopulation(population, output.toString()); + } - return 0; + /** + * This step runs at the very end. + */ + private void postPhase(Population population, ShpOptions.Index index) { + + for (Person person : population.getPersons().values()) { + + List activities = TripStructureUtils.getActivities(person.getSelectedPlan(), TripStructureUtils.StageActivityHandling.ExcludeStageActivities); + for (Activity act : activities) { + // Reduce unnecessary precision + if (act.getCoord() != null) { + act.setCoord(new Coord(round(act.getCoord().getX()), round(act.getCoord().getY()))); + } + } + + // Only handles persons + if (!"person".equals(PopulationUtils.getSubpopulation(person))) + continue; + + Coord home = setHomeCoordinate(person); + if (home == null || !index.contains(home)) { + PopulationUtils.putSubpopulation(person, "outside_person"); + } + } } /** @@ -183,4 +215,7 @@ private Coord setHomeCoordinate(Person person) { return null; } + + enum Phase {pre, post} + } diff --git a/src/main/resources/META-INF/services/org.matsim.simwrapper.DashboardProvider b/src/main/resources/META-INF/services/org.matsim.simwrapper.DashboardProvider new file mode 100644 index 00000000..8c8552bb --- /dev/null +++ b/src/main/resources/META-INF/services/org.matsim.simwrapper.DashboardProvider @@ -0,0 +1 @@ +org.matsim.dashboard.LeipzigDashboardProvider \ No newline at end of file diff --git a/src/main/resources/mode_share_per_dist_ref.csv b/src/main/resources/mode_share_per_dist_ref.csv new file mode 100644 index 00000000..50d37348 --- /dev/null +++ b/src/main/resources/mode_share_per_dist_ref.csv @@ -0,0 +1,31 @@ +dist_group,main_mode,mean_dist,share +0 - 1000,bike,647.1320211664759,0.18648889960816117 +0 - 1000,car,715.5733490038705,0.05234787770685268 +0 - 1000,pt,802.3537970116731,0.011283809577320864 +0 - 1000,ride,729.0617251936227,0.030663712677327124 +0 - 1000,walk,499.1497285817478,0.7192157004303381 +1000 - 2000,bike,1410.8376133243098,0.32720226728480256 +1000 - 2000,car,1500.735907403122,0.16968403169776486 +1000 - 2000,pt,1533.2653903592868,0.11751538922105827 +1000 - 2000,ride,1487.1708306254736,0.09512585828066737 +1000 - 2000,walk,1302.3249718162533,0.2904724535157069 +2000 - 5000,bike,3202.914928652807,0.32502884429891704 +2000 - 5000,car,3405.7134444946573,0.2673536802459596 +2000 - 5000,pt,3498.796011328994,0.25034686219019325 +2000 - 5000,ride,3493.776275422576,0.11424498215353364 +2000 - 5000,walk,2618.6861353776153,0.043025631111396456 +5000 - 10000,bike,6488.004797551106,0.13390529274426183 +5000 - 10000,car,7286.224192866411,0.46657794968496363 +5000 - 10000,pt,6911.388240153839,0.2673883884644953 +5000 - 10000,ride,7091.697700844432,0.12398400299689334 +5000 - 10000,walk,8512.500292899185,0.008144366109385872 +10000 - 20000,bike,11870.106118880658,0.05597065369100367 +10000 - 20000,car,13314.789717119513,0.6443810864079137 +10000 - 20000,pt,13268.066606129496,0.21199175918221977 +10000 - 20000,ride,12510.881840420803,0.08765650071886284 +10000 - 20000,walk,,0.0 +20000+,bike,39557.16129260366,0.008270651122581562 +20000+,car,45145.502240349495,0.7529385110988895 +20000+,pt,39740.679248392946,0.1367767540286853 +20000+,ride,38947.640630839625,0.10201408374984364 +20000+,walk,,0.0 diff --git a/src/main/resources/mode_share_ref.csv b/src/main/resources/mode_share_ref.csv new file mode 100644 index 00000000..7f9bdd64 --- /dev/null +++ b/src/main/resources/mode_share_ref.csv @@ -0,0 +1,31 @@ +dist_group,main_mode,mean_dist,share +0 - 1000,bike,647.1320211664759,0.04360576137790145 +0 - 1000,car,715.5733490038705,0.012240240940456974 +0 - 1000,pt,802.3537970116731,0.002638436437215161 +0 - 1000,ride,729.0617251936227,0.00716994170043107 +0 - 1000,walk,499.1497285817478,0.16817058966030335 +1000 - 2000,bike,1410.8376133243098,0.05161256124243341 +1000 - 2000,car,1500.735907403122,0.026765790929684893 +1000 - 2000,pt,1533.2653903592868,0.018536760987114285 +1000 - 2000,ride,1487.1708306254736,0.015005058574293151 +1000 - 2000,walk,1302.3249718162533,0.04581883683363964 +2000 - 5000,bike,3202.914928652807,0.09292657641457054 +2000 - 5000,car,3405.7134444946573,0.07643709976165831 +2000 - 5000,pt,3498.796011328994,0.07157480705949293 +2000 - 5000,ride,3493.776275422576,0.032662932075985494 +2000 - 5000,walk,2618.6861353776153,0.012301137783270974 +5000 - 10000,bike,6488.004797551106,0.025021931407638667 +5000 - 10000,car,7286.224192866411,0.08718610903327519 +5000 - 10000,pt,6911.388240153839,0.04996496985474348 +5000 - 10000,ride,7091.697700844432,0.023168010427770593 +5000 - 10000,walk,8512.500292899185,0.0015218798747332046 +10000 - 20000,bike,11870.106118880658,0.0042008516746133645 +10000 - 20000,car,13314.789717119513,0.04836372612101472 +10000 - 20000,pt,13268.066606129496,0.015910944000784487 +10000 - 20000,ride,12510.881840420803,0.0065790183525187155 +10000 - 20000,walk,,0.0 +20000+,bike,39557.16129260366,0.000501334015678044 +20000+,car,45145.502240349495,0.04564014147534626 +20000+,pt,39740.679248392946,0.008290863480069695 +20000+,ride,38947.640630839625,0.006183688503361974 +20000+,walk,,0.0 diff --git a/src/main/resources/mode_users_ref.csv b/src/main/resources/mode_users_ref.csv new file mode 100644 index 00000000..5362f73a --- /dev/null +++ b/src/main/resources/mode_users_ref.csv @@ -0,0 +1,6 @@ +main_mode,user +walk,0.34795408095941294 +car,0.3507092997070994 +ride,0.16356884852805617 +bike,0.26412581692795317 +pt,0.2773855049775048 From faf8498475a4a6e25cf63d6da569828c01f03e35 Mon Sep 17 00:00:00 2001 From: rakow Date: Wed, 21 Jun 2023 21:20:20 +0200 Subject: [PATCH 067/106] update calibrate script --- src/main/python/calibrate.py | 68 ++++++++++++------------------------ 1 file changed, 22 insertions(+), 46 deletions(-) diff --git a/src/main/python/calibrate.py b/src/main/python/calibrate.py index 0db66fe6..467a968c 100644 --- a/src/main/python/calibrate.py +++ b/src/main/python/calibrate.py @@ -3,60 +3,36 @@ import os -import geopandas as gpd import pandas as pd -from matsim import calibration - -# %% - -if os.path.exists("srv.csv"): - srv = pd.read_csv("srv.csv") - sim = pd.read_csv("sim.csv") - - _, adj = calibration.calc_adjusted_mode_share(sim, srv) - - print(srv.groupby("mode").sum()) - - print("Adjusted") - print(adj.groupby("mode").sum()) +import geopandas as gpd +import numpy as np - adj.to_csv("srv_adj.csv", index=False) +import calibration -# %% +#%% modes = ["walk", "car", "ride", "pt", "bike"] fixed_mode = "walk" initial = { - "bike": -1.9, - "pt": -0.7, - "car": -1.4, - "ride": -4 + "bike": -0.56, + "pt": 0.01, + "car": -0.799, + "ride": -5.30 } -# Original target +# Mode share target target = { - "walk": 0.243, - "bike": 0.206, - "pt": 0.162, - "car": 0.301, - "ride": 0.086 + "walk": 0.227812, + "bike": 0.217869, + "pt": 0.166917, + "car": 0.296633, + "ride": 0.090769 } -# Adjusted for distance distribution -# target = { -# "bike": 0.205680, -# "car": 0.321617, -# "pt": 0.186261, -# "ride": 0.093713, -# "walk": 0.192729 -# } city = gpd.read_file("../scenarios/input/leipzig-utm32n/leipzig-utm32n.shp") -homes = pd.read_csv("../input/v1.2/leipzig-v1.2-homes.csv", dtype={"person": "str"}) - def f(persons): - persons = pd.merge(persons, homes, how="inner", left_on="person", right_on="person") persons = gpd.GeoDataFrame(persons, geometry=gpd.points_from_xy(persons.home_x, persons.home_y)) df = gpd.sjoin(persons.set_crs("EPSG:25832"), city, how="inner", op="intersects") @@ -65,20 +41,20 @@ def f(persons): return df - def filter_modes(df): return df[df.main_mode.isin(modes)] - -study, obj = calibration.create_mode_share_study("calib", "matsim-leipzig-1.2-SNAPSHOT-25ee44a.jar", +study, obj = calibration.create_mode_share_study("calib", "matsim-leipzig-1.2-SNAPSHOT-8ad10fc.jar", "../input/v1.2/leipzig-v1.2-25pct.config.xml", modes, target, initial_asc=initial, - args="--25pct --config:TimeAllocationMutator.mutationRange=900", - jvm_args="-Xmx46G -Xms46G -XX:+AlwaysPreTouch -XX:+UseParallelGC", - person_filter=f, map_trips=filter_modes, + args="--10pct", + jvm_args="-Xmx46G -Xms46G -XX:+AlwaysPreTouch", + transform_persons=f, transform_trips=filter_modes, + lr=calibration.linear_lr_scheduler(start=0.3, interval=6), chain_runs=calibration.default_chain_scheduler) -# %% -study.optimize(obj, 10) +#%% + +study.optimize(obj, 4) \ No newline at end of file From 5f82184918c7582ad0c0b2b4bc1dec91c9d4a3dc Mon Sep 17 00:00:00 2001 From: rakow Date: Wed, 21 Jun 2023 21:35:45 +0200 Subject: [PATCH 068/106] config update --- input/v1.2/leipzig-v1.2-25pct.config.xml | 2 +- src/main/python/calibrate.py | 26 ++++++++++-------------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/input/v1.2/leipzig-v1.2-25pct.config.xml b/input/v1.2/leipzig-v1.2-25pct.config.xml index 2d1cf0fc..e4160732 100644 --- a/input/v1.2/leipzig-v1.2-25pct.config.xml +++ b/input/v1.2/leipzig-v1.2-25pct.config.xml @@ -19,7 +19,7 @@ - + diff --git a/src/main/python/calibrate.py b/src/main/python/calibrate.py index 467a968c..334f533a 100644 --- a/src/main/python/calibrate.py +++ b/src/main/python/calibrate.py @@ -1,15 +1,10 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -import os - -import pandas as pd -import geopandas as gpd -import numpy as np - import calibration +import geopandas as gpd -#%% +# %% modes = ["walk", "car", "ride", "pt", "bike"] fixed_mode = "walk" @@ -20,7 +15,7 @@ "ride": -5.30 } -# Mode share target +# Original target target = { "walk": 0.227812, "bike": 0.217869, @@ -29,9 +24,9 @@ "ride": 0.090769 } - city = gpd.read_file("../scenarios/input/leipzig-utm32n/leipzig-utm32n.shp") + def f(persons): persons = gpd.GeoDataFrame(persons, geometry=gpd.points_from_xy(persons.home_x, persons.home_y)) @@ -41,20 +36,21 @@ def f(persons): return df + def filter_modes(df): return df[df.main_mode.isin(modes)] -study, obj = calibration.create_mode_share_study("calib", "matsim-leipzig-1.2-SNAPSHOT-8ad10fc.jar", + +study, obj = calibration.create_mode_share_study("calib", "matsim-leipzig-1.2-SNAPSHOT-e85f5c7.jar", "../input/v1.2/leipzig-v1.2-25pct.config.xml", modes, target, initial_asc=initial, - args="--10pct", - jvm_args="-Xmx46G -Xms46G -XX:+AlwaysPreTouch", + args="--10pct --parking-cost-area ../input/v1.2/parkingCostArea/Bewohnerparken_2020.shp --config:TimeAllocationMutator.mutationRange=900", + jvm_args="-Xmx46G -Xms46G -XX:+AlwaysPreTouch -XX:+UseParallelGC", transform_persons=f, transform_trips=filter_modes, lr=calibration.linear_lr_scheduler(start=0.3, interval=6), chain_runs=calibration.default_chain_scheduler) +# %% -#%% - -study.optimize(obj, 4) \ No newline at end of file +study.optimize(obj, 4) From 1af6a8fa20eb7a4f4a85570cbead1b2ba446eac4 Mon Sep 17 00:00:00 2001 From: rakow Date: Thu, 22 Jun 2023 08:21:16 +0200 Subject: [PATCH 069/106] add subtour replanning that uses the leipzig re routing --- Makefile | 4 +- input/v1.2/leipzig-v1.2-25pct.config.xml | 2 +- .../matsim/run/LeipzigSubtourModeChoice.java | 62 +++++++++++++++++++ 3 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/matsim/run/LeipzigSubtourModeChoice.java diff --git a/Makefile b/Makefile index a341dd9a..a682b3a2 100644 --- a/Makefile +++ b/Makefile @@ -146,14 +146,14 @@ input/$V/leipzig-$V-25pct.plans-initial.xml.gz: input/plans-longHaulFreight.xml. --input input/prepare-25pct.plans-with-trips.xml.gz\ --output $@ - $(sc) prepare fix-subtour-modes --input $@ --coord-dist 100 --output $@ - $(sc) prepare merge-populations $@ $^ --output $@ $(sc) prepare population $@ --phase post\ --shp $(shared)/matsim-input-files/senozon/20210520_leipzig/dilutionArea.shp --shp-crs $(CRS)\ --output $@ + $(sc) prepare fix-subtour-modes --input $@ --coord-dist 100 --output $@ + $(sc) prepare extract-home-coordinates $@ --csv input/$V/leipzig-$V-homes.csv $(sc) prepare downsample-population $@\ diff --git a/input/v1.2/leipzig-v1.2-25pct.config.xml b/input/v1.2/leipzig-v1.2-25pct.config.xml index e4160732..97c36fdc 100644 --- a/input/v1.2/leipzig-v1.2-25pct.config.xml +++ b/input/v1.2/leipzig-v1.2-25pct.config.xml @@ -84,7 +84,7 @@ - + diff --git a/src/main/java/org/matsim/run/LeipzigSubtourModeChoice.java b/src/main/java/org/matsim/run/LeipzigSubtourModeChoice.java new file mode 100644 index 00000000..bbec7421 --- /dev/null +++ b/src/main/java/org/matsim/run/LeipzigSubtourModeChoice.java @@ -0,0 +1,62 @@ +package org.matsim.run; + +import jakarta.inject.Inject; +import jakarta.inject.Provider; +import org.matsim.api.core.v01.Scenario; +import org.matsim.core.config.groups.GlobalConfigGroup; +import org.matsim.core.config.groups.SubtourModeChoiceConfigGroup; +import org.matsim.core.population.algorithms.PermissibleModesCalculator; +import org.matsim.core.population.algorithms.PlanAlgorithm; +import org.matsim.core.replanning.PlanStrategy; +import org.matsim.core.replanning.PlanStrategyImpl; +import org.matsim.core.replanning.modules.AbstractMultithreadedModule; +import org.matsim.core.replanning.selectors.RandomPlanSelector; +import org.matsim.core.router.MultimodalLinkChooser; +import org.matsim.core.router.SingleModeNetworksCache; +import org.matsim.core.router.TripRouter; +import org.matsim.core.utils.timing.TimeInterpretation; +import org.matsim.facilities.ActivityFacilities; + +/** + * Standard subtour mode choice, but replaced the re-routing. + */ +public class LeipzigSubtourModeChoice implements Provider { + + public static final String STRATEGY_NAME = "SubTourModeChoiceLeipzig"; + + @Inject + private Provider tripRouterProvider; + @Inject + private GlobalConfigGroup globalConfigGroup; + @Inject + private SubtourModeChoiceConfigGroup subtourModeChoiceConfigGroup; + @Inject + private ActivityFacilities facilities; + @Inject + private PermissibleModesCalculator permissibleModesCalculator; + @Inject + private TimeInterpretation timeInterpretation; + @Inject + private SingleModeNetworksCache singleModeNetworksCache; + @Inject + private Scenario scenario; + @Inject + private MultimodalLinkChooser linkChooser; + + @Override + public PlanStrategy get() { + PlanStrategyImpl.Builder builder = new PlanStrategyImpl.Builder(new RandomPlanSelector<>()); + builder.addStrategyModule(new org.matsim.core.replanning.modules.SubtourModeChoice(globalConfigGroup, subtourModeChoiceConfigGroup, permissibleModesCalculator)); + + // Re-routing + builder.addStrategyModule(new AbstractMultithreadedModule(globalConfigGroup) { + @Override + public PlanAlgorithm getPlanAlgoInstance() { + return new LeipzigRouterPlanAlgorithm(tripRouterProvider.get(), facilities, timeInterpretation, singleModeNetworksCache, scenario, linkChooser); + } + }); + + return builder.build(); + } + +} From 7b8daa0e274232e1ba7d926cf4231b4069a582df Mon Sep 17 00:00:00 2001 From: rakow Date: Mon, 10 Jul 2023 13:49:42 +0200 Subject: [PATCH 070/106] fix shp path --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index a682b3a2..abee0556 100644 --- a/Makefile +++ b/Makefile @@ -171,8 +171,8 @@ check: input/$V/leipzig-$V-25pct.plans-initial.xml.gz $(sc) analysis check-population $<\ --input-crs $(CRS)\ --attribute carAvail\ - --shp ../shared-svn/projects/NaMAV/data/leipzig-utm32n/leipzig-utm32n.shp\ + --shp ../shared-svn/projects/NaMAV/data/shapefiles/leipzig-utm32n/leipzig-utm32n.shp\ # Aggregated target prepare: input/$V/leipzig-$V-25pct.plans-initial.xml.gz input/$V/leipzig-$V-network-with-pt.xml.gz - echo "Done" \ No newline at end of file + echo "Done" From 0e06e4651f7525a679a8b90885d7e54e1a0cc886 Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Mon, 10 Jul 2023 15:35:04 +0200 Subject: [PATCH 071/106] add shopping so agents who perform shopping activities can park in the residential zone too --- src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index 0fa6e29e..4930f17a 100644 --- a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -81,7 +81,7 @@ private static LeipzigUtils.PersonParkingBehaviour getParkingBehaviour(Network f // an dieser stelle waere es besser abzufragen, ob die person in der naehe wohnt anstatt nur die home act -> residential parking zuzuordnen // check if non-home activity (since otherwise we assume that there is no parking restriction): - if (!originActivity.getType().equals(ActivityTypes.HOME)) { + if (!originActivity.getType().contains(ActivityTypes.HOME) && !originActivity.getType().contains(ActivityTypes.SHOPPING) ) { Link link = fullModalNetwork.getLinks().get(originActivity.getLinkId()); if (isLinkParkingTypeInsideResidentialArea(link)) { From e7c75e218d610b8e995fc29d2514f3c63c1e7769 Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Fri, 14 Jul 2023 10:50:31 +0200 Subject: [PATCH 072/106] write out agents who perform parking activities --- .../org/matsim/analysis/ParkingLocation.java | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/matsim/analysis/ParkingLocation.java b/src/main/java/org/matsim/analysis/ParkingLocation.java index 78295703..00255d07 100644 --- a/src/main/java/org/matsim/analysis/ParkingLocation.java +++ b/src/main/java/org/matsim/analysis/ParkingLocation.java @@ -7,11 +7,14 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Population; import org.matsim.application.MATSimAppCommand; import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.config.ConfigUtils; import org.matsim.core.events.EventsUtils; import org.matsim.core.events.MatsimEventsReader; import org.matsim.core.network.NetworkUtils; +import org.matsim.core.population.PopulationUtils; import org.matsim.core.utils.io.IOUtils; import org.matsim.vehicles.Vehicle; import picocli.CommandLine; @@ -40,16 +43,29 @@ public static void main (String args []) { public Integer call() throws Exception { Path eventsPath = globFile(directory, "*output_events.*"); Path networkPath = globFile(directory, "*output_network.*"); + Path popPath = globFile(directory, "*output_plans.*"); EventsManager manager = EventsUtils.createEventsManager(); Network network = NetworkUtils.readNetwork(String.valueOf(networkPath)); List listOfParkingActivities = new ArrayList<>(); - manager.addHandler(new ParkingActivites(listOfParkingActivities, network)); + List> listOfRelevantPersons = new ArrayList<>(); + manager.addHandler(new ParkingActivites(listOfParkingActivities, network, listOfRelevantPersons)); manager.initProcessing(); MatsimEventsReader matsimEventsReader = new MatsimEventsReader(manager); matsimEventsReader.readFile(eventsPath.toString()); manager.finishProcessing(); writeResults(directory, listOfParkingActivities); + if (listOfRelevantPersons.size()>0) { + Population population = PopulationUtils.readPopulation(String.valueOf(popPath)); + Population reducedPop = PopulationUtils.createPopulation(ConfigUtils.createConfig()); + for (Person p: population.getPersons().values()) { + if (listOfRelevantPersons.contains(p.getId())) { + reducedPop.addPerson(p); + } + } + PopulationUtils.writePopulation(reducedPop,directory+"/reducedPlans.xml"); + } + return null; } @@ -57,10 +73,12 @@ public Integer call() throws Exception { private static class ParkingActivites implements ActivityStartEventHandler { private final List listOfParkingData; private final Network network; + private final List> listOfRelevantPersons; - ParkingActivites(List listOfParkingData, Network network) { + ParkingActivites(List listOfParkingData, Network network, List> listOfRelevantPersons ) { this.listOfParkingData = listOfParkingData; this.network = network; + this.listOfRelevantPersons = listOfRelevantPersons; } @Override @@ -69,17 +87,21 @@ public void handleEvent(ActivityStartEvent activityStartEvent) { Link l = network.getLinks().get(activityStartEvent.getLinkId()); ParkingData pd = new ParkingData(activityStartEvent.getPersonId(), l.getCoord(), activityStartEvent.getLinkId()); listOfParkingData.add(pd); + + if(l.getAttributes().getAttribute("linkParkingType")!= null) { + listOfRelevantPersons.add(activityStartEvent.getPersonId()); + } } } } private static void writeResults(Path outputFolder, List listOfParkingData) throws IOException { BufferedWriter writer = IOUtils.getBufferedWriter(outputFolder.toString() + "/parkingActivities.tsv"); - writer.write("x" + "\t" + "y" + "\t" + "linkId" ); + writer.write("personId" + "\t" + "x" + "\t" + "y" + "\t" + "linkId" ); writer.newLine(); for (int i = 0; i < listOfParkingData.size(); i++) { ParkingData pd = listOfParkingData.get(i); - writer.write(pd.coord.getX() + "\t" + pd.coord.getY() + "\t" + pd.linkId); + writer.write(pd.personId + "\t" +pd.coord.getX() + "\t" + pd.coord.getY() + "\t" + pd.linkId); writer.newLine(); } writer.close(); From c1dea8ecf4a26e1dbd0e8c43240eaeb3d1b85db8 Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Mon, 17 Jul 2023 16:15:28 +0200 Subject: [PATCH 073/106] fix networking filtering so we have no more issues with nodes being inside the residential area --- .../run/LeipzigRouterPlanAlgorithm.java | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index 4930f17a..7d894602 100644 --- a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -9,6 +9,8 @@ import org.matsim.api.core.v01.population.*; import org.matsim.core.controler.PersonPrepareForSimAlgorithm; import org.matsim.core.network.NetworkUtils; +import org.matsim.core.network.filter.NetworkFilterManager; +import org.matsim.core.network.filter.NetworkLinkFilter; import org.matsim.core.population.algorithms.PlanAlgorithm; import org.matsim.core.population.algorithms.XY2Links; import org.matsim.core.router.MultimodalLinkChooser; @@ -47,30 +49,22 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm, PersonPrepareFo this.timeInterpretation = timeInterpretation; this.scenario = scenario; this.fullModalNetwork = singleModeNetworksCache.getSingleModeNetworksCache().get(TransportMode.car); + this.linkChooser = linkChooser; + // yyyy one should look at the networks cache and see how the following is done. And maybe even register it there. - this.reducedNetwork = NetworkUtils.createNetwork(scenario.getConfig().network()); - this.linkChooser = linkChooser; + NetworkFilterManager networkFilterManager = new NetworkFilterManager(fullModalNetwork, scenario.getConfig().network()); - /*for (Node node : this.fullModalNetwork.getNodes().values()) { - reducedNetwork.addNode(node); - }*/ + networkFilterManager.addLinkFilter(link -> !LeipzigUtils.isLinkParkingTypeInsideResidentialArea(link)); - for (Link link : this.fullModalNetwork.getLinks().values()) { - if (!LeipzigUtils.isLinkParkingTypeInsideResidentialArea(link)) { - Node fromNode = link.getFromNode(); - Node tNode = link.getToNode(); - if (!reducedNetwork.getNodes().containsKey(fromNode.getId())){ - reducedNetwork.addNode(fromNode); - } - if (!reducedNetwork.getNodes().containsKey(tNode.getId())){ - reducedNetwork.addNode(tNode); - } - reducedNetwork.addLink(link); - } - } + // keep all nodes that have no in and out links inside parking area + // otherwise nearest link might crash if it finds an empty node + networkFilterManager.addNodeFilter(n -> + n.getInLinks().values().stream().noneMatch(LeipzigUtils::isLinkParkingTypeInsideResidentialArea) && + n.getOutLinks().values().stream().noneMatch(LeipzigUtils::isLinkParkingTypeInsideResidentialArea)); - xy2Links = new XY2Links(fullModalNetwork, scenario.getActivityFacilities()); + this.reducedNetwork = networkFilterManager.applyFilters(); + this.xy2Links = new XY2Links(fullModalNetwork, scenario.getActivityFacilities()); } private static LeipzigUtils.PersonParkingBehaviour getParkingBehaviour(Network fullModalNetwork, Activity originActivity, String routingMode) { From 6cc33f87a6fe04819123afc800063b92171d1874 Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Mon, 17 Jul 2023 16:46:49 +0200 Subject: [PATCH 074/106] fix checkstyle issues --- .../org/matsim/analysis/ParkingLocation.java | 77 +++++++++---------- .../run/LeipzigRouterPlanAlgorithm.java | 5 +- 2 files changed, 36 insertions(+), 46 deletions(-) diff --git a/src/main/java/org/matsim/analysis/ParkingLocation.java b/src/main/java/org/matsim/analysis/ParkingLocation.java index 00255d07..2dbd797f 100644 --- a/src/main/java/org/matsim/analysis/ParkingLocation.java +++ b/src/main/java/org/matsim/analysis/ParkingLocation.java @@ -16,7 +16,6 @@ import org.matsim.core.network.NetworkUtils; import org.matsim.core.population.PopulationUtils; import org.matsim.core.utils.io.IOUtils; -import org.matsim.vehicles.Vehicle; import picocli.CommandLine; import java.io.BufferedWriter; @@ -30,12 +29,16 @@ import static org.matsim.application.ApplicationUtils.globFile; +/** + * Creates a tsv file donating the location of parking activities. + * Can be used for visualizations. + */ public class ParkingLocation implements MATSimAppCommand { @CommandLine.Option(names = "--directory", description = "path to matsim output directory", required = true) private Path directory; - public static void main (String args []) { + public static void main(String[] args) { new ParkingLocation().execute(args); } @@ -48,60 +51,33 @@ public Integer call() throws Exception { Network network = NetworkUtils.readNetwork(String.valueOf(networkPath)); List listOfParkingActivities = new ArrayList<>(); List> listOfRelevantPersons = new ArrayList<>(); - manager.addHandler(new ParkingActivites(listOfParkingActivities, network, listOfRelevantPersons)); + manager.addHandler(new ParkingActivities(listOfParkingActivities, network, listOfRelevantPersons)); manager.initProcessing(); MatsimEventsReader matsimEventsReader = new MatsimEventsReader(manager); matsimEventsReader.readFile(eventsPath.toString()); manager.finishProcessing(); writeResults(directory, listOfParkingActivities); - if (listOfRelevantPersons.size()>0) { + if (listOfRelevantPersons.size() > 0) { Population population = PopulationUtils.readPopulation(String.valueOf(popPath)); Population reducedPop = PopulationUtils.createPopulation(ConfigUtils.createConfig()); - for (Person p: population.getPersons().values()) { + for (Person p : population.getPersons().values()) { if (listOfRelevantPersons.contains(p.getId())) { reducedPop.addPerson(p); } } - PopulationUtils.writePopulation(reducedPop,directory+"/reducedPlans.xml"); + PopulationUtils.writePopulation(reducedPop, directory.resolve("reducedPlans.xml").toString()); } return null; } - - private static class ParkingActivites implements ActivityStartEventHandler { - private final List listOfParkingData; - private final Network network; - private final List> listOfRelevantPersons; - - ParkingActivites(List listOfParkingData, Network network, List> listOfRelevantPersons ) { - this.listOfParkingData = listOfParkingData; - this.network = network; - this.listOfRelevantPersons = listOfRelevantPersons; - } - - @Override - public void handleEvent(ActivityStartEvent activityStartEvent) { - if (activityStartEvent.getActType().equals("parking interaction")) { - Link l = network.getLinks().get(activityStartEvent.getLinkId()); - ParkingData pd = new ParkingData(activityStartEvent.getPersonId(), l.getCoord(), activityStartEvent.getLinkId()); - listOfParkingData.add(pd); - - if(l.getAttributes().getAttribute("linkParkingType")!= null) { - listOfRelevantPersons.add(activityStartEvent.getPersonId()); - } - } - } - } - private static void writeResults(Path outputFolder, List listOfParkingData) throws IOException { - BufferedWriter writer = IOUtils.getBufferedWriter(outputFolder.toString() + "/parkingActivities.tsv"); - writer.write("personId" + "\t" + "x" + "\t" + "y" + "\t" + "linkId" ); + BufferedWriter writer = IOUtils.getBufferedWriter(outputFolder.resolve("parkingActivities.tsv").toString()); + writer.write("personId\tx\ty\tlinkId"); writer.newLine(); - for (int i = 0; i < listOfParkingData.size(); i++) { - ParkingData pd = listOfParkingData.get(i); - writer.write(pd.personId + "\t" +pd.coord.getX() + "\t" + pd.coord.getY() + "\t" + pd.linkId); + for (ParkingData pd : listOfParkingData) { + writer.write(pd.personId + "\t" + pd.coord.getX() + "\t" + pd.coord.getY() + "\t" + pd.linkId); writer.newLine(); } writer.close(); @@ -112,17 +88,34 @@ private static void writeResults(Path outputFolder, List listOfPark Collectors.toMap(Function.identity(), company -> 1, Math::addExact) ); - BufferedWriter writerWithCounts = IOUtils.getBufferedWriter(outputFolder.toString() + "/parkingActivitiesWithCount.tsv"); + BufferedWriter writerWithCounts = IOUtils.getBufferedWriter(outputFolder.resolve("parkingActivitiesWithCount.tsv").toString()); - writerWithCounts.write(("x" + "\t" + "y" + "\t" + "linkId" + "\t" + "count")); + writerWithCounts.write(("x\ty\tlinkId\tcount")); writerWithCounts.newLine(); - for(ParkingData parkingData: duplicateCountMap.keySet()) { - writerWithCounts.write( parkingData.coord.getX() + "\t" + parkingData.coord.getY() +"\t" + for (ParkingData parkingData : duplicateCountMap.keySet()) { + writerWithCounts.write(parkingData.coord.getX() + "\t" + parkingData.coord.getY() + "\t" + parkingData.linkId + "\t" + duplicateCountMap.get(parkingData)); writerWithCounts.newLine(); } writerWithCounts.close(); } - record ParkingData (Id personId, Coord coord, Id linkId) {} + private record ParkingActivities(List listOfParkingData, Network network, List> listOfRelevantPersons) implements ActivityStartEventHandler { + + @Override + public void handleEvent(ActivityStartEvent activityStartEvent) { + if (activityStartEvent.getActType().equals("parking interaction")) { + Link l = network.getLinks().get(activityStartEvent.getLinkId()); + ParkingData pd = new ParkingData(activityStartEvent.getPersonId(), l.getCoord(), activityStartEvent.getLinkId()); + listOfParkingData.add(pd); + + if (l.getAttributes().getAttribute("linkParkingType") != null) { + listOfRelevantPersons.add(activityStartEvent.getPersonId()); + } + } + } + } + + private record ParkingData(Id personId, Coord coord, Id linkId) { + } } diff --git a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index 7d894602..b6272430 100644 --- a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -5,12 +5,9 @@ import org.matsim.api.core.v01.TransportMode; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; -import org.matsim.api.core.v01.network.Node; import org.matsim.api.core.v01.population.*; import org.matsim.core.controler.PersonPrepareForSimAlgorithm; -import org.matsim.core.network.NetworkUtils; import org.matsim.core.network.filter.NetworkFilterManager; -import org.matsim.core.network.filter.NetworkLinkFilter; import org.matsim.core.population.algorithms.PlanAlgorithm; import org.matsim.core.population.algorithms.XY2Links; import org.matsim.core.router.MultimodalLinkChooser; @@ -75,7 +72,7 @@ private static LeipzigUtils.PersonParkingBehaviour getParkingBehaviour(Network f // an dieser stelle waere es besser abzufragen, ob die person in der naehe wohnt anstatt nur die home act -> residential parking zuzuordnen // check if non-home activity (since otherwise we assume that there is no parking restriction): - if (!originActivity.getType().contains(ActivityTypes.HOME) && !originActivity.getType().contains(ActivityTypes.SHOPPING) ) { + if (!originActivity.getType().contains(ActivityTypes.HOME) && !originActivity.getType().contains(ActivityTypes.SHOPPING)) { Link link = fullModalNetwork.getLinks().get(originActivity.getLinkId()); if (isLinkParkingTypeInsideResidentialArea(link)) { From d7241e511518d2824d305395d8ba3182104a8385 Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 18 Jul 2023 10:09:45 +0200 Subject: [PATCH 075/106] update config, added counts --- Makefile | 13 +++++++------ input/v1.2/leipzig-v1.2-25pct.config.xml | 4 ++++ input/v1.2/leipzig-v1.2-counts-car-bast.xml.gz | Bin 0 -> 2769 bytes input/v1.2/leipzig-v1.2-counts-hgv-bast.xml.gz | Bin 0 -> 2601 bytes pom.xml | 2 +- .../java/org/matsim/run/RunLeipzigScenario.java | 6 +++--- .../matsim/run/prepare/PreparePopulation.java | 2 +- 7 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 input/v1.2/leipzig-v1.2-counts-car-bast.xml.gz create mode 100644 input/v1.2/leipzig-v1.2-counts-hgv-bast.xml.gz diff --git a/Makefile b/Makefile index abee0556..07b722f2 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ osmosis := osmosis/bin/osmosis NETWORK := germany-220327.osm.pbf germany := ../shared-svn/projects/matsim-germany -shared := ../shared-svn/projects/NaMAV +shared ?= ../shared-svn/projects/NaMAV .PHONY: prepare @@ -70,7 +70,7 @@ input/sumo.net.xml: input/network.osm input/$V/leipzig-$V-network.xml.gz: input/sumo.net.xml - $(sc) prepare network-from-sumo $< --output $@ + $(sc) prepare network-from-sumo $< --output $@ --free-speed-factor 0.75 $(sc) prepare fix-network $@ --output $@ $(sc) prepare clean-network $@ --output $@ --modes bike @@ -93,25 +93,25 @@ input/plans-longHaulFreight.xml.gz: input/$V/leipzig-$V-network.xml.gz --cut-on-boundary\ --output $@ -input/plans-commercialTraffic.xml.gz: +input/plans-completeSmallScaleCommercialTraffic.xml.gz: $(sc) prepare generate-small-scale-commercial-traffic\ input/commercialTraffic\ --sample 0.25\ --jspritIterations 1\ --creationOption createNewCarrierFile\ --landuseConfiguration useOSMBuildingsAndLanduse\ - --trafficType commercialTraffic\ + --smallScaleCommercialTrafficType completeSmallScaleCommercialTraffic\ --zoneShapeFileName $(shared)/data/input-commercialTraffic/leipzig_zones_25832.shp\ --buildingsShapeFileName $(shared)/data/input-commercialTraffic/leipzig_buildings_25832.shp\ --landuseShapeFileName $(shared)/data/input-commercialTraffic/leipzig_landuse_25832.shp\ --shapeCRS "EPSG:25832"\ --resistanceFactor "0.005"\ --nameOutputPopulation $(notdir $@)\ - --PathOutput output/commercialTraffic + --pathOutput output/commercialTraffic mv output/commercialTraffic/$(notdir $@) $@ -input/$V/leipzig-$V-25pct.plans-initial.xml.gz: input/plans-longHaulFreight.xml.gz input/plans-commercialTraffic.xml.gz +input/$V/leipzig-$V-25pct.plans-initial.xml.gz: input/plans-longHaulFreight.xml.gz input/plans-completeSmallScaleCommercialTraffic.xml.gz $(sc) prepare trajectory-to-plans\ --name prepare --sample-size 0.25\ --max-typical-duration 0\ @@ -144,6 +144,7 @@ input/$V/leipzig-$V-25pct.plans-initial.xml.gz: input/plans-longHaulFreight.xml. $(sc) prepare split-activity-types-duration\ --input input/prepare-25pct.plans-with-trips.xml.gz\ + --exclude commercial_start,commercial_end,freight_start,freight_end\ --output $@ $(sc) prepare merge-populations $@ $^ --output $@ diff --git a/input/v1.2/leipzig-v1.2-25pct.config.xml b/input/v1.2/leipzig-v1.2-25pct.config.xml index 97c36fdc..280e0e2c 100644 --- a/input/v1.2/leipzig-v1.2-25pct.config.xml +++ b/input/v1.2/leipzig-v1.2-25pct.config.xml @@ -45,6 +45,10 @@ + + + + diff --git a/input/v1.2/leipzig-v1.2-counts-car-bast.xml.gz b/input/v1.2/leipzig-v1.2-counts-car-bast.xml.gz new file mode 100644 index 0000000000000000000000000000000000000000..b06a2be9fb6ccc7dbc0877a6f2bd71f9c49736fc GIT binary patch literal 2769 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 Lgh+nQJ~;pY)@A_& literal 0 HcmV?d00001 diff --git a/pom.xml b/pom.xml index 12df47f0..086a1aaf 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.matsim matsim-all - 16.0-PR2657 + 16.0-PR2693 diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index 5d86826c..7e83bf06 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -60,7 +60,7 @@ import org.matsim.run.prepare.*; import org.matsim.simwrapper.SimWrapperConfigGroup; import org.matsim.simwrapper.SimWrapperModule; -import org.matsim.smallScaleCommercialTrafficGeneration.CreateSmallScaleCommercialTrafficDemand; +import org.matsim.smallScaleCommercialTrafficGeneration.GenerateSmallScaleCommercialTrafficDemand; import picocli.CommandLine; import playground.vsp.scoring.IncomeDependentUtilityOfMoneyPersonScoringParameters; import playground.vsp.simpleParkingCostHandler.ParkingCostConfigGroup; @@ -78,7 +78,7 @@ MergePopulations.class, ExtractRelevantFreightTrips.class, DownSamplePopulation.class, PrepareNetwork.class, CleanNetwork.class, CreateLandUseShp.class, ResolveGridCoordinates.class, PreparePopulation.class, CleanPopulation.class, AdjustActivityToLinkDistances.class, SplitActivityTypesDuration.class, ExtractHomeCoordinates.class, FixSubtourModes.class, FixNetwork.class, PrepareTransitSchedule.class, - CreateSmallScaleCommercialTrafficDemand.class + GenerateSmallScaleCommercialTrafficDemand.class }) @MATSimApplication.Analysis({ CheckPopulation.class, LinkStats.class, SubTourAnalysis.class, DrtServiceQualityAnalysis.class, @@ -188,7 +188,7 @@ protected Config prepareConfig(Config config) { config.qsim().setFlowCapFactor(sample.getSize() / 100.0); config.qsim().setStorageCapFactor(sample.getSize() / 100.0); - simWrapper.defaultParams().sampleSize = String.valueOf(sample.getSample()); + simWrapper.defaultParams().sampleSize = sample.getSample(); } diff --git a/src/main/java/org/matsim/run/prepare/PreparePopulation.java b/src/main/java/org/matsim/run/prepare/PreparePopulation.java index 49455df6..c7336ce0 100644 --- a/src/main/java/org/matsim/run/prepare/PreparePopulation.java +++ b/src/main/java/org/matsim/run/prepare/PreparePopulation.java @@ -84,7 +84,7 @@ public Integer call() throws Exception { } /** - * This steps runs before any other steps. + * This step runs before any other steps. */ private void prePhase(Population population) { From aac016a83cdf4481d51c0986ecfb58f2e33831cf Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 18 Jul 2023 10:48:18 +0200 Subject: [PATCH 076/106] update calibrate script --- .../java/org/matsim/run/RunLeipzigScenario.java | 1 + src/main/python/calibrate.py | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index 7e83bf06..099c50f3 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -300,6 +300,7 @@ public void install() { this.addPlanStrategyBinding(LeipzigRoutingStrategyProvider.STRATEGY_NAME).toProvider(LeipzigRoutingStrategyProvider.class); this.addPlanStrategyBinding(LeipzigSubtourModeChoice.STRATEGY_NAME).toProvider(LeipzigSubtourModeChoice.class); + // Normally this is bound with the default subtour mode choice, because we use our own variant this is bound again here bind(PermissibleModesCalculator.class).to(PermissibleModesCalculatorImpl.class); if (networkOpt.hasCarFreeArea()) { diff --git a/src/main/python/calibrate.py b/src/main/python/calibrate.py index 334f533a..187c1214 100644 --- a/src/main/python/calibrate.py +++ b/src/main/python/calibrate.py @@ -12,16 +12,16 @@ "bike": -0.56, "pt": 0.01, "car": -0.799, - "ride": -5.30 + "ride": -1.30 } -# Original target +# Target from SrV target = { - "walk": 0.227812, - "bike": 0.217869, - "pt": 0.166917, - "car": 0.296633, - "ride": 0.090769 + "walk": 0.2278, + "bike": 0.2179, + "pt": 0.1669, + "car": 0.2966, + "ride": 0.0908 } city = gpd.read_file("../scenarios/input/leipzig-utm32n/leipzig-utm32n.shp") @@ -48,7 +48,7 @@ def filter_modes(df): args="--10pct --parking-cost-area ../input/v1.2/parkingCostArea/Bewohnerparken_2020.shp --config:TimeAllocationMutator.mutationRange=900", jvm_args="-Xmx46G -Xms46G -XX:+AlwaysPreTouch -XX:+UseParallelGC", transform_persons=f, transform_trips=filter_modes, - lr=calibration.linear_lr_scheduler(start=0.3, interval=6), + lr=calibration.linear_lr_scheduler(start=0.3, interval=8), chain_runs=calibration.default_chain_scheduler) # %% From aadc2f5f465ac078bd2c11117d1c3ae4bfbaf1d6 Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 18 Jul 2023 10:51:27 +0200 Subject: [PATCH 077/106] reduce todo severity --- src/main/java/org/matsim/run/RunLeipzigScenario.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index 099c50f3..8332902e 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -320,8 +320,8 @@ public void install() { }); if (networkOpt.hasDrtArea()) { - // FIXME yyyyyy move above into prepareConfig - // FIXME will be integrated into DrtCaseSetup class + // TODO yyyyyy move above into prepareConfig + // TODO will be integrated into DrtCaseSetup class MultiModeDrtConfigGroup multiModeDrtConfigGroup = ConfigUtils.addOrGetModule(config, MultiModeDrtConfigGroup.class); @@ -356,7 +356,7 @@ public void install() { } /** - * FIXME: will be moved into separate class. + * TODO: will be moved into separate class. */ private void prepareDrtFareCompensation(Config config, Controler controler, Set nonPtModes, Double ptBaseFare) { IntermodalTripFareCompensatorsConfigGroup intermodalTripFareCompensatorsConfigGroup = From ae2e2674bf6425a7b9f56fd666c4993388f70109 Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 18 Jul 2023 10:52:09 +0200 Subject: [PATCH 078/106] reduce todo severity --- src/main/java/org/matsim/run/RunLeipzigScenario.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index 8332902e..e941e145 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -320,7 +320,7 @@ public void install() { }); if (networkOpt.hasDrtArea()) { - // TODO yyyyyy move above into prepareConfig + // TODO yyyy move above into prepareConfig // TODO will be integrated into DrtCaseSetup class MultiModeDrtConfigGroup multiModeDrtConfigGroup = ConfigUtils.addOrGetModule(config, MultiModeDrtConfigGroup.class); From 8d03aed311e7246346e983a9cdd685aa6a665d65 Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 18 Jul 2023 14:01:58 +0200 Subject: [PATCH 079/106] update ride parameters --- input/v1.2/leipzig-v1.2-25pct.config.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/input/v1.2/leipzig-v1.2-25pct.config.xml b/input/v1.2/leipzig-v1.2-25pct.config.xml index 280e0e2c..e2cea736 100644 --- a/input/v1.2/leipzig-v1.2-25pct.config.xml +++ b/input/v1.2/leipzig-v1.2-25pct.config.xml @@ -158,9 +158,10 @@ - + + - + From 175911258ee70237cf9d0cc0f9974ada0840024f Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Wed, 26 Jul 2023 13:51:53 +0200 Subject: [PATCH 080/106] annotate parking capacities with deprecated as it is not used anymore --- .../java/org/matsim/run/prepare/ParkingCapacitiesAttacher.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/matsim/run/prepare/ParkingCapacitiesAttacher.java b/src/main/java/org/matsim/run/prepare/ParkingCapacitiesAttacher.java index f1ce0bf9..c836e11c 100644 --- a/src/main/java/org/matsim/run/prepare/ParkingCapacitiesAttacher.java +++ b/src/main/java/org/matsim/run/prepare/ParkingCapacitiesAttacher.java @@ -23,6 +23,7 @@ /** * Attach parking information to links. */ +@Deprecated public final class ParkingCapacitiesAttacher { private static final Logger log = LogManager.getLogger(ParkingCapacitiesAttacher.class); From 79abaf5af38b37fa675ae8239b4281247d310a5e Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 1 Aug 2023 12:59:25 +0200 Subject: [PATCH 081/106] update config and matsim version --- input/v1.2/leipzig-v1.2-25pct.config.xml | 9 ++++++--- pom.xml | 2 +- src/main/java/org/matsim/run/RunLeipzigScenario.java | 4 +--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/input/v1.2/leipzig-v1.2-25pct.config.xml b/input/v1.2/leipzig-v1.2-25pct.config.xml index e2cea736..a714ccf8 100644 --- a/input/v1.2/leipzig-v1.2-25pct.config.xml +++ b/input/v1.2/leipzig-v1.2-25pct.config.xml @@ -114,6 +114,7 @@ + @@ -148,20 +149,22 @@ + + - - + + - + diff --git a/pom.xml b/pom.xml index 086a1aaf..0c559fb2 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.matsim matsim-all - 16.0-PR2693 + 16.0-PR2714 diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index e941e145..9a704afe 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -161,9 +161,7 @@ protected Config prepareConfig(Config config) { simWrapper.defaultParams().mapCenter = "12.38,51.34"; simWrapper.defaultParams().mapZoomLevel = 10.3; - // freight is long-haul freight - // freightTraffic is rather short distance - for (String subpopulation : List.of("outside_person", "freight", "freightTraffic", "businessTraffic", "businessTraffic_service")) { + for (String subpopulation : List.of("outside_person", "freight", "goodsTraffic", "commercialPersonTraffic", "commercialPersonTraffic_service")) { config.strategy().addStrategySettings( new StrategyConfigGroup.StrategySettings() .setStrategyName(DefaultPlanStrategiesModule.DefaultSelector.ChangeExpBeta) From 08b3ce74ed5f96577e12dcfb97677bcd8a856b2d Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 1 Aug 2023 13:31:29 +0200 Subject: [PATCH 082/106] fix typo --- input/v1.2/leipzig-v1.2-25pct.config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input/v1.2/leipzig-v1.2-25pct.config.xml b/input/v1.2/leipzig-v1.2-25pct.config.xml index a714ccf8..8cfe39c7 100644 --- a/input/v1.2/leipzig-v1.2-25pct.config.xml +++ b/input/v1.2/leipzig-v1.2-25pct.config.xml @@ -150,7 +150,7 @@ - + From e934e6fa36903b721d06a9c383b5717c9d7d1975 Mon Sep 17 00:00:00 2001 From: simei94 <67737999+simei94@users.noreply.github.com> Date: Mon, 28 Aug 2023 19:58:20 +0200 Subject: [PATCH 083/106] make creation of DrtStops + intermodal TransitSchedule part of run class (#27) * make creation of DrtStops + intermodal TransitSchedule part of RunLeipzigScenario (WIP) * add TODO * prepare extraction of drt config to class * Code review masterscript (#30) * analysis runs without errors, added basic loop option * updated analysis scripts in order to do winner/loser analysis * WIP but working * Heatmaps implemented * WIP new master drt script * Add files via upload * modified .gitignore to exclude Rstudio dot files * updated shape file paths, updated winner-loser analysis to sourcemethods from matsim-r library instead of utils_jr.R file * WIP drt skript, output needs to be checked * WIP DRT complete, naming needs to be revisited * small fixes re paths + dir creation * tried to plot all demographic differences * WIP nearly done - DRT done, variable names changes, sankey and winner-loser still need work/merging * cleaned up equity / winner loser analysis * bug fixes * ready for final checks/revisions * bug fixes * fixed naming issues * changing relative paths in r masterscript * code review and added some todos - SME, GR, MK * code review and added some todos - SME, GR, MK * cleanup scripts + add missing carfree area analysis * further debugging + cleaning of scripts to make them ready to go * some comments * further clean up + make all scripts runnable * some comments + corrections * mode av is only analyzed when there are av trips * bug fixes + some comments * delete unnecessary traffic volumes analysis (this is done in java anyways) * smaller bug fixes + generalizations in analysis * make runId of emissions analysis modifiable * runId not needed anymore when using string search patterns for files * make outputDir modifiable if wished * add / when missing * checkstyle --------- Co-authored-by: j-bnsch <78746351+j-bnsch@users.noreply.github.com> Co-authored-by: Jakob Rehmann Co-authored-by: mkreuschner <64031262+mkreuschner@users.noreply.github.com> Co-authored-by: mkreuschnervsp * move drt configs into DrtCaseSetup class, not tested yet * drt automation almost ready * add config file with updated car+ride prices and new ride scoring parameters * further improvements of DRT automation WIP * bugfix * update input files to new drt automation * final steps of drt input automation done * fix NetworkOptionsTest * intermodal configs in code * bugfix * sonarcloud bug fixes * sonarcloud bug fixes * simplify calling of optionals * add missing imports * update test and config to v1.2 --------- Co-authored-by: j-bnsch <78746351+j-bnsch@users.noreply.github.com> Co-authored-by: Jakob Rehmann Co-authored-by: mkreuschner <64031262+mkreuschner@users.noreply.github.com> Co-authored-by: mkreuschnervsp Co-authored-by: vsp-gleich --- .gitignore | 12 +- ...a-leipzig-utm32n.cpg => Leipzig_stadt.cpg} | 0 input/v1.2/drtServiceArea/Leipzig_stadt.dbf | Bin 0 -> 119 bytes ...a-leipzig-utm32n.prj => Leipzig_stadt.prj} | 0 input/v1.2/drtServiceArea/Leipzig_stadt.shp | Bin 0 -> 4732 bytes input/v1.2/drtServiceArea/Leipzig_stadt.shx | Bin 0 -> 108 bytes .../leipzig_flexa_service_area_2021.cpg | 1 + .../leipzig_flexa_service_area_2021.dbf | Bin 0 -> 788 bytes .../leipzig_flexa_service_area_2021.prj | 1 + .../leipzig_flexa_service_area_2021.shp | Bin 0 -> 2996 bytes .../leipzig_flexa_service_area_2021.shx | Bin 0 -> 116 bytes ...preliminary-serviceArea-leipzig-utm32n.dbf | Bin 342 -> 0 bytes ...preliminary-serviceArea-leipzig-utm32n.shp | Bin 59316 -> 0 bytes ...preliminary-serviceArea-leipzig-utm32n.shx | Bin 116 -> 0 bytes input/v1.2/leipzig-v1.2-25pct.config.xml | 130 +-- ... leipzig-v1.2-25pct_prices2021.config.xml} | 238 ++-- .../ODAnalysis/Trippurpose-Analysis.R | 111 +- .../R/master_carfreeareas_extendedversion.R | 1002 ----------------- src/main/R/master_drt.R | 173 +++ src/main/R/masteranalyse.R | 960 ++++++++++------ src/main/R/masterscript.R | 286 ++--- ...AirPollutionAnalysisByVehicleCategory.java | 62 +- .../org/matsim/run/LeipzigPtFareModule.java | 33 +- .../org/matsim/run/RunLeipzigScenario.java | 161 +-- .../prepare/CreateDrtStopsFromNetwork.java | 30 +- .../org/matsim/run/prepare/DrtCaseSetup.java | 442 ++++++++ .../matsim/run/prepare/DrtStopsWriter.java | 94 +- .../run/prepare/LeipzigDrtVehicleCreator.java | 88 +- .../matsim/run/prepare/NetworkOptions.java | 6 +- .../matsim/run/prepare/PrepareDrtStops.java | 6 +- .../run/prepare/PrepareTransitSchedule.java | 33 +- .../matsim/run/RunLeipzigIntegrationTest.java | 20 +- .../run/prepare/NetworkOptionsTest.java | 2 +- 33 files changed, 1864 insertions(+), 2027 deletions(-) rename input/v1.2/drtServiceArea/{preliminary-serviceArea-leipzig-utm32n.cpg => Leipzig_stadt.cpg} (100%) create mode 100644 input/v1.2/drtServiceArea/Leipzig_stadt.dbf rename input/v1.2/drtServiceArea/{preliminary-serviceArea-leipzig-utm32n.prj => Leipzig_stadt.prj} (100%) create mode 100644 input/v1.2/drtServiceArea/Leipzig_stadt.shp create mode 100644 input/v1.2/drtServiceArea/Leipzig_stadt.shx create mode 100644 input/v1.2/drtServiceArea/leipzig_flexa_service_area_2021.cpg create mode 100644 input/v1.2/drtServiceArea/leipzig_flexa_service_area_2021.dbf create mode 100644 input/v1.2/drtServiceArea/leipzig_flexa_service_area_2021.prj create mode 100644 input/v1.2/drtServiceArea/leipzig_flexa_service_area_2021.shp create mode 100644 input/v1.2/drtServiceArea/leipzig_flexa_service_area_2021.shx delete mode 100644 input/v1.2/drtServiceArea/preliminary-serviceArea-leipzig-utm32n.dbf delete mode 100644 input/v1.2/drtServiceArea/preliminary-serviceArea-leipzig-utm32n.shp delete mode 100644 input/v1.2/drtServiceArea/preliminary-serviceArea-leipzig-utm32n.shx rename input/v1.2/{leipzig-test.with-drt.config.xml => leipzig-v1.2-25pct_prices2021.config.xml} (57%) delete mode 100644 src/main/R/master_carfreeareas_extendedversion.R create mode 100644 src/main/R/master_drt.R create mode 100644 src/main/java/org/matsim/run/prepare/DrtCaseSetup.java diff --git a/.gitignore b/.gitignore index a4308b74..93e8b24a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,11 @@ # Add own ignores on top, don't edit the template +input/*stops.xml +input/*stops.csv +*.Rhistory +*.RProj + + # Common files to ignore when working with matsim @@ -519,4 +525,8 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk -# End of https://www.toptal.com/developers/gitignore/api/r,osx,java,linux,maven,python,eclipse,windows,intellij+all,jupyternotebooks \ No newline at end of file +# End of https://www.toptal.com/developers/gitignore/api/r,osx,java,linux,maven,python,eclipse,windows,intellij+all,jupyternotebooks + + +.lintr +*.Rproj \ No newline at end of file diff --git a/input/v1.2/drtServiceArea/preliminary-serviceArea-leipzig-utm32n.cpg b/input/v1.2/drtServiceArea/Leipzig_stadt.cpg similarity index 100% rename from input/v1.2/drtServiceArea/preliminary-serviceArea-leipzig-utm32n.cpg rename to input/v1.2/drtServiceArea/Leipzig_stadt.cpg diff --git a/input/v1.2/drtServiceArea/Leipzig_stadt.dbf b/input/v1.2/drtServiceArea/Leipzig_stadt.dbf new file mode 100644 index 0000000000000000000000000000000000000000..c768f03d7d26252d0896f9702b545d1fa79c68e9 GIT binary patch literal 119 zcmZRs=3r)IU|>jO5CxK$ATtFn<_BVN!MPAdZhlHCRLB`bqs#LufPtZrv59F)Q3-^n GAO!$sVF)My literal 0 HcmV?d00001 diff --git a/input/v1.2/drtServiceArea/preliminary-serviceArea-leipzig-utm32n.prj b/input/v1.2/drtServiceArea/Leipzig_stadt.prj similarity index 100% rename from input/v1.2/drtServiceArea/preliminary-serviceArea-leipzig-utm32n.prj rename to input/v1.2/drtServiceArea/Leipzig_stadt.prj diff --git a/input/v1.2/drtServiceArea/Leipzig_stadt.shp b/input/v1.2/drtServiceArea/Leipzig_stadt.shp new file mode 100644 index 0000000000000000000000000000000000000000..81c592e1b5a90f199374b4b300bb133fe01b2ae7 GIT binary patch literal 4732 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 K=wo zCGiv1>|B~-RfWh<2EBofJ%gu;w{NSL%L zQ%w^US)xVRNh*|RG4I!P<(l`Mcg}yGb3e=P_x$erei#fTQO3XirabEvU@)d2mNUo0 z^z$Y{%eZf&QcBp=*w*2~f;>WHNl!^eC7b^1^#A>nkHO$io>(hc=8~>l#Q(3QwO2_k zzno1mkYV{_?jw`FEmLC%O>rB$)r)-}X{?N17)|KTV3&OEB{p5Xm}mVViqI@&O;Z=R zSaODB;t4|Yl~pvX;ozkfGWL;#zU$E5U=L63{mZJ#yHDU0^MN%dPZIjtJB6DvFT#h7^}6gX|JBtYWR9+ zAWs}VJtHh?6Ra*8Zq$bL{10VzH#a#ob;hxSuTlCC(8ksD0p6+qJt&O|A`7s>^F6Ob!^dDLYsON zvkt-^_i#)LVE*CXYh7Vs6DDhNyR_sBnhzt|lu4Ta}faLm&$5E^%Bd1fH&Yn!qB3M?#jCnXV1 zIw?J0DwEKNtk-Q}a8Bj=rXkpYW!Dk}M?|F$_GS@!>R9W*UYL9rIMEHu+FEK_!pl@7 za`*g<=NbNjZYd0L$=7%lvZ3p?`YRlVvUC8&I=)SZzJYOrjqM;LZ zPgaor6L$68&CP=6Bm_L2*nh2=NNNTwndqY}0rN|`_n(6AuW_3DHV0M?U3CI(ytH6e zQ7)l%&AZ&J;K@ExD0_aN8oXFheO5olJ6FFjl7WR3mTA|(w*;8Ky+M5%smlKqlY#f$ zarFiRah6zNlOVpgN@iouDB`K|YA4?Gw4gL}R`oA&Vxp{%Qx zqwa9rl$EXR$bS>n>Zum+m|@52Bgur0`5o2HK)LpBGpuUE zn=E0h4mq>-O2{j0L-6(LAP5C1i zu8@E;KYO>&hJ||zQljh#-8gjc)*^W2oB8@2%sxWDc7kS?%b@~Q0hkLvhLISo8g%cdk2Je z5h~Vn^+^Z3VuxDa7C1-D>sbZ7*(&qQ@NPoO-}3Lh1)CjTw_3*?b)!3jkp){T**IBx z5!#t#;r$Sf@u0VbU=f~k!fx10wdl}Ec&cNnKNEI(n8{qfALo6C`+E=^xbMibR3Abc z?Sj6Czz$Lx-O7H1*66#s#KPur$}*Y$g!Y^zvZvvSk1WSJIQez)z$sW!^jR>(%<;M^XNftV4t@(h(W^15R*d$zMA{s1=h} zz5@6A#s#r$?(nvnfB-`{p;gE(C=7mZ!}u&LG~?G-7kPi@qWIB3FE==ty|Pae@yT43 z>B`+cg7rN%m3CbZ!Mzltu*w`R*HCDWKa4(P?4R!rM;)wq@F|GUq9mU!x^T_S7t_`q zM4g4pKA#RBz7**%3!iwxJNpLtlP$ztYKHgCxhOT%4{N1oPt}2=E5A9{!>-GwaTNmy z9SYu9)&O@-XWFF0@%~X}ad3Hk2Ky(R|8h=$oh2+PTpwugRA3M4skTU{0akU44El`k*&fuhwG#I4ijBGeKfCjII0xRGIrQr_PxKYh zxV8{Dd)})lT^@uQaWb_%;5I>t#uVhw-4>ZLH+YT(L-QT-i>GS)XeZ3I*YXpB!$-a` zChYD@cYoc9_1w%nIq*BL5OpQ^t19Qv1~|K9{WM49bG5vl_Zpa^aPrtySJdYf`dSAb zNz%U8;X;7Y|aXN}>D3YQ&oP=6NY_t&%FvSi!!N{)o?@f>9>ffeXs@NXrp`q-}`t5BQ+`>6QVK1N(QFm9U)Y|^3EBmhtL z((!BkBWYNF$4up6LBvHTqElU&6O1 z@ZlS4!pd#%{m1J(3*f+(5Hm^iJN}r8j0u~(dN_Q{n$U%nxdp|rbaqveDV&!t)#3s3 e@zOg#!TO0>p*vyi5qr-ZKhzPs Tb`((~1_q`XKzxpYftdpU8vYR~ literal 0 HcmV?d00001 diff --git a/input/v1.2/drtServiceArea/preliminary-serviceArea-leipzig-utm32n.dbf b/input/v1.2/drtServiceArea/preliminary-serviceArea-leipzig-utm32n.dbf deleted file mode 100644 index fe21f6123f710b59a6fcd81dd18ca60641be4a2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 342 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

60Fq&8| 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 - + + value="https://svn.vsp.tu-berlin.de/repos/public-svn/matsim/scenarios/countries/de/leipzig/leipzig-v1.2/input/leipzig-v1.2-25pct.plans-initial.xml.gz"/> - + + value="https://svn.vsp.tu-berlin.de/repos/public-svn/matsim/scenarios/countries/de/leipzig/leipzig-v1.2/input/leipzig-v1.2-transitSchedule.xml.gz"/> + value="https://svn.vsp.tu-berlin.de/repos/public-svn/matsim/scenarios/countries/de/leipzig/leipzig-v1.2/input/leipzig-v1.2-transitVehicles.xml.gz"/> @@ -61,11 +61,12 @@ - - - - - + + + + + + @@ -202,113 +203,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/input/v1.2/leipzig-test.with-drt.config.xml b/input/v1.2/leipzig-v1.2-25pct_prices2021.config.xml similarity index 57% rename from input/v1.2/leipzig-test.with-drt.config.xml rename to input/v1.2/leipzig-v1.2-25pct_prices2021.config.xml index 0c72c39e..0bfe9fcc 100644 --- a/input/v1.2/leipzig-test.with-drt.config.xml +++ b/input/v1.2/leipzig-v1.2-25pct_prices2021.config.xml @@ -6,12 +6,12 @@ - - - - + + + + - + @@ -23,32 +23,31 @@ - - + - - + - - + - - - + + - - + + @@ -59,52 +58,43 @@ - - - + + - + - - + + + - + - - - - - - - - - + - + - @@ -116,7 +106,6 @@ - @@ -124,18 +113,17 @@ - - - + + + + - + - - @@ -145,8 +133,7 @@ - - + @@ -154,87 +141,53 @@ - + + + + - - - - - + + + + + + + + + + + + - - + + + + - - - + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + @@ -246,6 +199,19 @@ + + + + + + + + + + + + + @@ -262,19 +228,19 @@ - - - - + + + + - - + + - + @@ -293,7 +259,7 @@ --> - + @@ -308,19 +274,19 @@ - - - - + + + + - - + + - + @@ -342,18 +308,4 @@ - - - - - - - - - - - - - - diff --git a/src/main/R/Analysis/ODAnalysis/Trippurpose-Analysis.R b/src/main/R/Analysis/ODAnalysis/Trippurpose-Analysis.R index d1d795f8..1d2b3c19 100644 --- a/src/main/R/Analysis/ODAnalysis/Trippurpose-Analysis.R +++ b/src/main/R/Analysis/ODAnalysis/Trippurpose-Analysis.R @@ -12,9 +12,7 @@ require(readr) ################################################################################################################################## ##INPUTS - -# !!!INCLUDE TRAILING SLASH!!! -#the output_trips file should be there +print("###### Starting trip purpose analysis #####") option_list <- list( make_option(c("-d", "--runDir"), type="character", default=NULL, @@ -30,13 +28,17 @@ if (is.null(opt$runDir)){ runDirectory <- opt$runDir +if (endsWith(runDirectory, "/") == FALSE) { + runDirectory <- paste0(runDirectory,"/"); +} + interactiveMode <-FALSE #setwd(runDirectory) # !not necessary but try if something fails! ################################################################################################################################## ##output directory is automatically set and created -outputDir <- paste(runDirectory, "analysis-trip-purposes", sep = "") # the plots are going to be saved here +outputDir <- paste(runDirectory, "analysis/analysis-trip-purposes", sep = "") # the plots are going to be saved here if(!file.exists(outputDir)){ print("creating analysis sub-directory") dir.create(outputDir) @@ -113,7 +115,7 @@ drtAnalysis <- TripDataframe %>% plot <-ggplot(drtAnalysis,aes(x=end_activity_type,y=stat(count),group=factor(main_mode),fill=factor(main_mode)))+ geom_bar()+ xlab("Trip purpose")+ - ggtitle("Trip purposes of rides with conventional Flexa")+ + ggtitle("Trip purposes of rides with conventional DRT")+ ylab("Number of trips ")+ theme(axis.text.x = element_text(angle = 45,hjust=1))+ theme(plot.title = element_text(hjust = 0.5))+ @@ -137,7 +139,7 @@ plot1 <-ggplot(drtAnalysis,aes(x=main_mode,y=stat(count), group=factor(end_activ geom_bar()+ xlab("Trip purpose")+ ylab("Traveling mode")+ - ggtitle("Trip purposes of trips with conventional Flexa")+ + ggtitle("Trip purposes of trips with conventional DRT")+ scale_fill_manual(values = c("#A6CEE3", "#6EAACF", "#3687BC", "#4190AA" ,"#7EBA98" ,"#AADB84" ,"#76C15D", "#41A737", "#6D9E4C", "#C09B78", "#F88A8A", "#EE5656", "#E42123", "#EC5439", "#F6985B", "#FDB35B" ,"#FE992D", "#FF7F00")) + #theme_ipsum()+ theme(text=element_text(size=20))+ @@ -159,55 +161,60 @@ if(interactiveMode){ #analyze av mode avAnalysis <- TripDataframe %>% filter(main_mode=="av") -print(length(avAnalysis$main_mode)) - -#################### -#av Balkendiagramm -plot <-ggplot(avAnalysis,aes(x=end_activity_type))+ - geom_bar(fill= "steelblue")+ - xlab("Trip purpose")+ - ggtitle("Trip purposes of rides with Flexa AV")+ - ylab("Number of trips ")+ - theme(axis.text.x = element_text(angle = 45,hjust=1))+ - theme(plot.title = element_text(hjust = 0.5))+ - #theme_ipsum()+ - theme(text=element_text(size=20))+ - geom_text(aes(label=stat(count)),stat="count",position = position_stack(vjust = 0.5),text=element_text(size=20),color="white") - -plotFile <-paste(outputDir,"/av_trip_purposes.png",sep="") -paste("printing plot to ", plotFile) -png(plotFile, width = 1100, height = 800) -plot -dev.off() -if(interactiveMode){ - ggplotly(plot) +if (length(avAnalysis$main_mode) > 0 ) { + + print(length(avAnalysis$main_mode)) + + #################### + #av Balkendiagramm + plot <-ggplot(avAnalysis,aes(x=end_activity_type))+ + geom_bar(fill= "steelblue")+ + xlab("Trip purpose")+ + ggtitle("Trip purposes of rides with AV")+ + ylab("Number of trips ")+ + theme(axis.text.x = element_text(angle = 45,hjust=1))+ + theme(plot.title = element_text(hjust = 0.5))+ + #theme_ipsum()+ + theme(text=element_text(size=20))+ + geom_text(aes(label=stat(count)),stat="count",position = position_stack(vjust = 0.5),text=element_text(size=20),color="white") + + + plotFile <-paste(outputDir,"/av_trip_purposes.png",sep="") + paste("printing plot to ", plotFile) + png(plotFile, width = 1100, height = 800) + plot + dev.off() + if(interactiveMode){ + ggplotly(plot) + } + + ############## + #av Stackbalkendiagramm + plot1 <-ggplot(avAnalysis,aes(x=main_mode,y=stat(count), group=factor(end_activity_type), fill=factor(end_activity_type)))+ + geom_bar()+ + xlab("Trip purpose")+ + ylab("Traveling mode")+ + ggtitle("Trip purposes of trips with AV")+ + scale_fill_manual(values = c("#A6CEE3", "#6EAACF", "#3687BC", "#4190AA" ,"#7EBA98" ,"#AADB84" ,"#76C15D", "#41A737", "#6D9E4C", "#C09B78", "#F88A8A", "#EE5656", "#E42123", "#EC5439", "#F6985B", "#FDB35B" ,"#FE992D", "#FF7F00")) + + #theme_ipsum()+ + theme(text=element_text(size=20))+ + theme(plot.title = element_text(hjust = 0.5))+ + #theme(axis.text.x = element_text(angle = 45,hjust=1))+ + geom_text(aes(label=stat(count)),stat="count",position = position_stack(vjust = 0.5)) + + + plotFile <-paste(outputDir,"/av_trip_purposes_stacked.png",sep="") + paste("printing plot to ", plotFile) + #ggsave(plotFile) + png(plotFile, width = 1200, height = 800) + plot1 + dev.off() + if(interactiveMode){ + ggplotly(plot1) + } } -############## -#av Stackbalkendiagramm -plot1 <-ggplot(avAnalysis,aes(x=main_mode,y=stat(count), group=factor(end_activity_type), fill=factor(end_activity_type)))+ - geom_bar()+ - xlab("Trip purpose")+ - ylab("Traveling mode")+ - ggtitle("Trip purposes of trips with Flexa AV")+ - scale_fill_manual(values = c("#A6CEE3", "#6EAACF", "#3687BC", "#4190AA" ,"#7EBA98" ,"#AADB84" ,"#76C15D", "#41A737", "#6D9E4C", "#C09B78", "#F88A8A", "#EE5656", "#E42123", "#EC5439", "#F6985B", "#FDB35B" ,"#FE992D", "#FF7F00")) + - #theme_ipsum()+ - theme(text=element_text(size=20))+ - theme(plot.title = element_text(hjust = 0.5))+ - #theme(axis.text.x = element_text(angle = 45,hjust=1))+ - geom_text(aes(label=stat(count)),stat="count",position = position_stack(vjust = 0.5)) - - -plotFile <-paste(outputDir,"/av_trip_purposes_stacked.png",sep="") -paste("printing plot to ", plotFile) -#ggsave(plotFile) -png(plotFile, width = 1200, height = 800) -plot1 -dev.off() -if(interactiveMode){ - ggplotly(plot1) -} #analyze car mode carAnalysis <- TripDataframe %>% diff --git a/src/main/R/master_carfreeareas_extendedversion.R b/src/main/R/master_carfreeareas_extendedversion.R deleted file mode 100644 index 069bc9f6..00000000 --- a/src/main/R/master_carfreeareas_extendedversion.R +++ /dev/null @@ -1,1002 +0,0 @@ -#MASTERSKRIPT for car free areas (using output for dashboard) - -####Libraries#### -library(gridExtra) -library(tidyr) -library(tidyverse) -library(lubridate) -library(viridis) -library(ggsci) -library(sf) -library(dplyr) -library(ggplot2) -library(matsim) -library(purrr) -library(networkD3) -library(alluvial) - -####File Paths#### - -#BASE CASE - path where to find the MATSim ouput - -f <- "/Users/mkreuschnervsp/Desktop/VSP_projects/02_NaMaV/MATSim/25pct_base/20220811/" - -#POLICY CASE 99 - path where to find the MATSim ouput -g <- "/Users/mkreuschnervsp/Desktop/VSP_projects/02_NaMaV/MATSim/25pct_scenario99/20220811/" - -#POLICY CASE 95 - path where to find the MATSim ouput -h <- "/Users/mkreuschnervsp/Desktop/VSP_projects/02_NaMaV/MATSim/25pct_scenario95/20220811/" - -#POLICY CASE 90 - path where to find the MATSim ouput -i <- "/Users/mkreuschnervsp/Desktop/VSP_projects/02_NaMaV/MATSim/25pct_scenario90/20220811/" - -#Shape Files -region_shp_path <- "shapefiles/Leipzig_puffer.shp" -city_shp_path <- "shapefiles/Leipzig_stadt.shp" -area90_shp_path <- "shapefiles/Zonen90_update.shp" -area95_shp_path <-"shapefiles/Zonen95_update.shp" -area99_shp_path <- "shapefiles/Zonen99_update.shp" - -#Trips Files -Base_trips_path <- "matsim_output/base/leipzig-flexa-25pct-scaledFleet-base_noDepot.output_trips.csv" -Area90_trips_path <- "matsim_output/carfree90pct/leipzig-flexa-25pct-scaledFleet-carfree90pct_noDepot.output_trips.csv.gz" -Area95_trips_path <- "matsim_output/carfree95pct/leipzig-flexa-25pct-scaledFleet-carfree95pct_noDepot.output_trips.csv.gz" -Area99_trips_path <- "matsim_output/carfree99pct/leipzig-flexa-25pct-scaledFleet-carfree99pct_noDepot.output_trips.csv.gz" - -#Legs Files - -Base_legs_path <- "matsim_output/base/leipzig-flexa-25pct-scaledFleet-base_noDepot.output_legs.csv.gz" -Area90_legs_path <- "matsim_output/carfree90pct/leipzig-flexa-25pct-scaledFleet-carfree90pct_noDepot.output_legs.csv.gz" -Area95_legs_path <- "matsim_output/carfree95pct/leipzig-flexa-25pct-scaledFleet-carfree95pct_noDepot.output_legs.csv.gz" -Area99_legs_path <- "matsim_output/carfree99pct/leipzig-flexa-25pct-scaledFleet-carfree99pct_noDepot.output_legs.csv.gz" - - -####Reading Files#### -RegionShape <- st_read(region_shp_path, crs=25832) -CityShape <- st_read(city_shp_path, crs=25832) #city of Leipzig -AreaShape90 <- st_read(area90_shp_path, crs=25832) #area of 90% -AreaShape95 <- st_read(area95_shp_path, crs=25832) #area of 95% -AreaShape99 <- st_read(area99_shp_path, crs=25832) #area of 99% - -#loading trips tables -BASEtripsTable <- read.csv2(Base_trips_path, dec = ".") -AREA90tripsTable <- readTripsTable(pathToMATSimOutputDirectory = Area90_trips_path) -AREA95tripsTable <- readTripsTable(pathToMATSimOutputDirectory = Area95_trips_path) -AREA99tripsTable <- readTripsTable(pathToMATSimOutputDirectory = Area99_trips_path) - -#loading legs tables -BASELegsTable <- read_delim(Base_legs_path, delim= ";") -AREA90LegsTable <- read_delim(Area90_legs_path, delim= ";") -AREA95LegsTable <- read_delim(Area95_legs_path, delim= ";") -AREA99LegsTable <- read_delim(Area99_legs_path, delim= ";") - -####Parameters#### -#SIMULATION SCALE (25% -> 4) -sim_scale <- 4 - -#BREAKING DIFFERENT DISTANCES IN M -breaks = c(0, 1000, 2000, 5000, 10000, 20000, Inf) - -## INPUT DEFINITION - files needed TRIPS, LEGS, PERSONS, NETWORK, SHAPEFILES ## - -runID <- "Base_Region" -runID <- "Base_Leipzig" -runID <- "Base_Area90" -runID <- "Base_Area95" -runID <- "Base_Area99" - - -####TRIPS SPATIAL FILTER#### -BaseTripsRegion <- filterByRegion(BASEtripsTable,RegionShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -BaseTripsCity <- filterByRegion(BASEtripsTable,CityShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -BaseTrips90 <- filterByRegion(BASEtripsTable,AreaShape90,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -BaseTrips95 <- filterByRegion(BASEtripsTable,AreaShape95,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -BaseTrips99 <- filterByRegion(BASEtripsTable,AreaShape99,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA90TripsRegion <- filterByRegion(AREA90tripsTable,RegionShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA90TripsCity <- filterByRegion(AREA90tripsTable,CityShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA90Trips90 <- filterByRegion(AREA90tripsTable,AreaShape90,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA90Trips95 <- filterByRegion(AREA90tripsTable,AreaShape95,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA90Trips99 <- filterByRegion(AREA90tripsTable,AreaShape99,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA95TripsRegion <- filterByRegion(AREA95tripsTable,RegionShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA95TripsCity <- filterByRegion(AREA95tripsTable,CityShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA95Trips90 <- filterByRegion(AREA95tripsTable,AreaShape90,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA95Trips95 <- filterByRegion(AREA95tripsTable,AreaShape95,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA95Trips99 <- filterByRegion(AREA95tripsTable,AreaShape99,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA99TripsRegion <- filterByRegion(AREA99tripsTable,RegionShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA99TripsCity <- filterByRegion(AREA99tripsTable,CityShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA99Trips90 <- filterByRegion(AREA99tripsTable,AreaShape90,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA99Trips95 <- filterByRegion(AREA99tripsTable,AreaShape95,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA99Trips99 <- filterByRegion(AREA99tripsTable,AreaShape99,crs=25832,start.inshape = TRUE,end.inshape = TRUE) - -####TRIPS MORE FILTERS#### - -BaseTripsRegion <- BaseTripsRegion %>% - filter(main_mode!="freight") %>% - mutate(dist_group = cut(traveled_distance, breaks=breaks, labels= c("0-1000","1000-2000","2000-5000","5000-10000","10000-20000",">20000"))) %>% - filter(!is.na(dist_group)) - -BaseTripsCity <- BaseTripsCity %>% - filter(main_mode!="freight") %>% - mutate(dist_group = cut(traveled_distance, breaks=breaks, labels= c("0-1000","1000-2000","2000-5000","5000-10000","10000-20000",">20000"))) %>% - filter(!is.na(dist_group)) - -BaseTrips90 <- BaseTrips90 %>% - filter(main_mode!="freight") %>% - mutate(dist_group = cut(traveled_distance, breaks=breaks, labels= c("0-1000","1000-2000","2000-5000","5000-10000","10000-20000",">20000"))) %>% - filter(!is.na(dist_group)) - -BaseTrips95 <- BaseTrips95 %>% - filter(main_mode!="freight") %>% - mutate(dist_group = cut(traveled_distance, breaks=breaks, labels= c("0-1000","1000-2000","2000-5000","5000-10000","10000-20000",">20000"))) %>% - filter(!is.na(dist_group)) - -BaseTrips99 <- BaseTrips99 %>% - filter(main_mode!="freight") %>% - mutate(dist_group = cut(traveled_distance, breaks=breaks, labels= c("0-1000","1000-2000","2000-5000","5000-10000","10000-20000",">20000"))) %>% - filter(!is.na(dist_group)) - -AREA90TripsRegion <- AREA90TripsRegion %>% - filter(main_mode!="freight") %>% - mutate(dist_group = cut(traveled_distance, breaks=breaks, labels= c("0-1000","1000-2000","2000-5000","5000-10000","10000-20000",">20000"))) %>% - filter(!is.na(dist_group)) - -AREA90TripsCity <- AREA90TripsCity %>% - filter(main_mode!="freight") %>% - mutate(dist_group = cut(traveled_distance, breaks=breaks, labels= c("0-1000","1000-2000","2000-5000","5000-10000","10000-20000",">20000"))) %>% - filter(!is.na(dist_group)) - -AREA90Trips90 <- AREA90Trips90 %>% - filter(main_mode!="freight") %>% - mutate(dist_group = cut(traveled_distance, breaks=breaks, labels= c("0-1000","1000-2000","2000-5000","5000-10000","10000-20000",">20000"))) %>% - filter(!is.na(dist_group)) - -AREA90Trips95 <- AREA90Trips95 %>% - filter(main_mode!="freight") %>% - mutate(dist_group = cut(traveled_distance, breaks=breaks, labels= c("0-1000","1000-2000","2000-5000","5000-10000","10000-20000",">20000"))) %>% - filter(!is.na(dist_group)) - -AREA90Trips99 <- AREA90Trips99 %>% - filter(main_mode!="freight") %>% - mutate(dist_group = cut(traveled_distance, breaks=breaks, labels= c("0-1000","1000-2000","2000-5000","5000-10000","10000-20000",">20000"))) %>% - filter(!is.na(dist_group)) - -AREA95TripsRegion <- AREA95TripsRegion %>% - filter(main_mode!="freight") %>% - mutate(dist_group = cut(traveled_distance, breaks=breaks, labels= c("0-1000","1000-2000","2000-5000","5000-10000","10000-20000",">20000"))) %>% - filter(!is.na(dist_group)) - -AREA95TripsCity <- AREA95TripsCity %>% - filter(main_mode!="freight") %>% - mutate(dist_group = cut(traveled_distance, breaks=breaks, labels= c("0-1000","1000-2000","2000-5000","5000-10000","10000-20000",">20000"))) %>% - filter(!is.na(dist_group)) - -AREA95Trips90 <- AREA95Trips90 %>% - filter(main_mode!="freight") %>% - mutate(dist_group = cut(traveled_distance, breaks=breaks, labels= c("0-1000","1000-2000","2000-5000","5000-10000","10000-20000",">20000"))) %>% - filter(!is.na(dist_group)) - -AREA95Trips95 <- AREA95Trips95 %>% - filter(main_mode!="freight") %>% - mutate(dist_group = cut(traveled_distance, breaks=breaks, labels= c("0-1000","1000-2000","2000-5000","5000-10000","10000-20000",">20000"))) %>% - filter(!is.na(dist_group)) - -AREA95Trips99 <- AREA95Trips99 %>% - filter(main_mode!="freight") %>% - mutate(dist_group = cut(traveled_distance, breaks=breaks, labels= c("0-1000","1000-2000","2000-5000","5000-10000","10000-20000",">20000"))) %>% - filter(!is.na(dist_group)) - -AREA99TripsRegion <- AREA99TripsRegion %>% - filter(main_mode!="freight") %>% - mutate(dist_group = cut(traveled_distance, breaks=breaks, labels= c("0-1000","1000-2000","2000-5000","5000-10000","10000-20000",">20000"))) %>% - filter(!is.na(dist_group)) - -AREA99TripsCity <- AREA99TripsCity %>% - filter(main_mode!="freight") %>% - mutate(dist_group = cut(traveled_distance, breaks=breaks, labels= c("0-1000","1000-2000","2000-5000","5000-10000","10000-20000",">20000"))) %>% - filter(!is.na(dist_group)) - -AREA99Trips90 <- AREA99Trips90 %>% - filter(main_mode!="freight") %>% - mutate(dist_group = cut(traveled_distance, breaks=breaks, labels= c("0-1000","1000-2000","2000-5000","5000-10000","10000-20000",">20000"))) %>% - filter(!is.na(dist_group)) - -AREA99Trips95 <- AREA99Trips95 %>% - filter(main_mode!="freight") %>% - mutate(dist_group = cut(traveled_distance, breaks=breaks, labels= c("0-1000","1000-2000","2000-5000","5000-10000","10000-20000",">20000"))) %>% - filter(!is.na(dist_group)) - -AREA99Trips99 <- AREA99Trips99 %>% - filter(main_mode!="freight") %>% - mutate(dist_group = cut(traveled_distance, breaks=breaks, labels= c("0-1000","1000-2000","2000-5000","5000-10000","10000-20000",">20000"))) %>% - filter(!is.na(dist_group)) - - - - - -####LEGS SPATIAL FILTER #### - -BaseLegsRegion <- filterByRegion(BASELegsTable,RegionShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -BaseLegsCity <- filterByRegion(BASELegsTable,CityShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -BaseLegs90 <- filterByRegion(BASELegsTable,AreaShape90,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -BaseLegs95 <- filterByRegion(BASELegsTable,AreaShape95,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -BaseLegs99 <- filterByRegion(BASELegsTable,AreaShape99,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA90LegsRegion <- filterByRegion(AREA90LegsTable,RegionShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA90LegsCity <- filterByRegion(AREA90LegsTable,CityShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA90Legs90 <- filterByRegion(AREA90LegsTable,AreaShape90,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA90Legs95 <- filterByRegion(AREA90LegsTable,AreaShape95,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA90Legs99 <- filterByRegion(AREA90LegsTable,AreaShape99,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA95LegsRegion <- filterByRegion(AREA95LegsTable,RegionShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA95LegsCity <- filterByRegion(AREA95LegsTable,CityShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA95Legs90 <- filterByRegion(AREA95LegsTable,AreaShape90,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA95Legs95 <- filterByRegion(AREA95LegsTable,AreaShape95,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA95Legs99 <- filterByRegion(AREA95LegsTable,AreaShape99,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA99LegsRegion <- filterByRegion(AREA99LegsTable,RegionShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA99LegsCity <- filterByRegion(AREA99LegsTable,CityShape,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA99Legs90 <- filterByRegion(AREA99LegsTable,AreaShape90,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA99Legs95 <- filterByRegion(AREA99LegsTable,AreaShape95,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -AREA99Legs99 <- filterByRegion(AREA99LegsTable,AreaShape99,crs=25832,start.inshape = TRUE,end.inshape = TRUE) -####LEGS MORE FILTERS #### - -#### #0 General Numbers #### - -# Average Distance -average_distance_trips <- mean(BASEtripsTable$traveled_distance) -average_distance_legs <- mean(BASELegsTable$distance) - -# Average Distance by Mode -average_distance_mode_trips <- function(x){ - x %>% - group_by(main_mode) %>% - summarise(mean(traveled_distance)) - -} -average_distance_mode_legs <- function(x){ - x %>% - group_by(mode) %>% - summarise(mean(distance)) - -} - -average_distance_base_case_trips <- average_distance_mode_trips(BASEtripsTable) -average_distance_base_case_legs <- average_distance_mode_trips(BASElegsTable) - -#### #1.1 ModalSplit - trips based - main mode (count) #### -# x) Functions - -modal_split_trips_main_mode <- function(x){ - x %>% - count(main_mode) %>% - mutate(percent = 100*n/sum(n)) -} - -# a) Region - -ms_main_mode_trips_BaseRegion <- modal_split_trips_main_mode(BaseTripsRegion) -ms_main_mode_trips_Area90Region <- modal_split_trips_main_mode(AREA90TripsRegion) -ms_main_mode_trips_Area95Region <- modal_split_trips_main_mode(AREA95TripsRegion) -ms_main_mode_trips_Area99Region <- modal_split_trips_main_mode(AREA99TripsRegion) - -ms_main_mode_trips_Region <- bind_rows(ms_main_mode_trips_BaseRegion, - ms_main_mode_trips_Area90Region, - ms_main_mode_trips_Area95Region, - ms_main_mode_trips_Area99Region, .id = "id") - -ms_trips_Region_plot <- ms_main_mode_trips_Region %>% - group_by(id) %>% - ggplot(mapping= aes(x = main_mode, y= n, fill = id))+ - geom_col(position = "dodge")+ - ylab("Number of Trips")+ - labs(fill = "Case")+ - scale_fill_discrete(labels= c("Base Case", "90%","95%", "99%" )) -ms_trips_Region_plot - -# b) City - -ms_main_mode_trips_BaseCity <- modal_split_trips_main_mode(BaseTripsCity) -ms_main_mode_trips_Area90City <- modal_split_trips_main_mode(AREA90TripsCity) -ms_main_mode_trips_Area95City <- modal_split_trips_main_mode(AREA95TripsCity) -ms_main_mode_trips_Area99City <- modal_split_trips_main_mode(AREA99TripsCity) - -ms_main_mode_trips_CITY <- bind_rows(ms_main_mode_BaseCity, - ms_main_mode_trips_Area90City, - ms_main_mode_trips_Area95City, - ms_main_mode_trips_Area99City, .id = "id") - -ms_trips_city_plot <- ms_main_mode_trips_CITY %>% - group_by(id) %>% - ggplot(mapping= aes(x = main_mode, y= n, fill = id))+ - geom_col(position = "dodge")+ - ylab("Number of Trips")+ - labs(fill = "Case")+ - scale_fill_discrete(labels= c("Base Case", "90%","95%", "99%" )) -ms_trips_city_plot - - -# c) area90 - -ms_main_mode_trips_Base_90 <- modal_split_trips_main_mode(BaseTrips90) -ms_main_mode_trips_Area90_90 <- modal_split_trips_main_mode(AREA90Trips90) -ms_main_mode_trips_Area95_90 <- modal_split_trips_main_mode(AREA95Trips90) -ms_main_mode_trips_Area99_90 <- modal_split_trips_main_mode(AREA99Trips90) - -ms_main_mode_trips_90 <- bind_rows(ms_main_mode_trips_Base_90, - ms_main_mode_trips_Area90_90, - ms_main_mode_trips_Area95_90, - ms_main_mode_trips_Area99_90, .id = "id") - -ms_trips_90_plot <- ms_main_mode_trips_90 %>% - group_by(id) %>% - ggplot(mapping= aes(x = main_mode, y= n, fill = id))+ - geom_col(position = "dodge")+ - ylab("Number of Trips")+ - labs(fill = "Case")+ - scale_fill_discrete(labels= c("Base Case", "90%","95%", "99%" )) -ms_trips_90_plot - -# d) area95 - -ms_main_mode_trips_Base_95 <- modal_split_trips_main_mode(BaseTrips95) -ms_main_mode_trips_Area90_95 <- modal_split_trips_main_mode(AREA90Trips95) -ms_main_mode_trips_Area95_95 <- modal_split_trips_main_mode(AREA95Trips95) -ms_main_mode_trips_Area99_95 <- modal_split_trips_main_mode(AREA99Trips95) - -ms_main_mode_trips_95 <- bind_rows(ms_main_mode_trips_Base_95, - ms_main_mode_trips_Area90_95, - ms_main_mode_trips_Area95_95, - ms_main_mode_trips_Area99_95, .id = "id") - -ms_trips_95_plot <- ms_main_mode_trips_95 %>% - group_by(id) %>% - ggplot(mapping= aes(x = main_mode, y= n, fill = id))+ - geom_col(position = "dodge")+ - ylab("Number of Trips")+ - labs(fill = "Case")+ - scale_fill_discrete(labels= c("Base Case", "90%","95%", "99%" )) -ms_trips_90_plot - -# e) area99 - -ms_main_mode_trips_Base_99 <- modal_split_trips_main_mode(BaseTrips99) -ms_main_mode_trips_Area90_99 <- modal_split_trips_main_mode(AREA90Trips99) -ms_main_mode_trips_Area95_99 <- modal_split_trips_main_mode(AREA95Trips99) -ms_main_mode_trips_Area99_99 <- modal_split_trips_main_mode(AREA99Trips99) - -ms_main_mode_trips_99 <- bind_rows(ms_main_mode_trips_Base_99, - ms_main_mode_trips_Area90_99, - ms_main_mode_trips_Area95_99, - ms_main_mode_trips_Area99_99, .id = "id") - -ms_trips_99_plot <- ms_main_mode_trips_99 %>% - group_by(id) %>% - ggplot(mapping= aes(x = main_mode, y= n, fill = id))+ - geom_col(position = "dodge")+ - ylab("Number of Trips")+ - labs(fill = "Case")+ - scale_fill_discrete(labels= c("Base Case", "90%","95%", "99%" )) -ms_trips_99_plot - - - -#### #1.2 Modal Split - trips based - distance #### - -modal_split_trips_distance <- function(x){ - x %>% - group_by(mode) %>% - summarise(distance = sum(distance)) %>% - mutate(percent = round(100*distance/sum(distance),2)) -} - -# a) Region - -ms_distance_trips_BaseRegion <- modal_split_trips_distance(BaseTripsRegion) -ms_distance_trips_Area90Region <- modal_split_trips_distance(AREA90TripsRegion) -ms_distance_trips_Area95Region <- modal_split_trips_distance(AREA95TripsRegion) -ms_distance_trips_Area99Region <- modal_split_trips_distance(AREA99TripsRegion) - -ms_distance_trips_Region <- bind_rows(ms_distance_trips_BaseRegion, - ms_distance_trips_Area90Region, - ms_distance_trips_Area95Region, - ms_distance_trips_Area99Region, .id = "id") - -ms_trips_Region_plot <- ms_distance_trips_Region %>% - group_by(id) %>% - ggplot(mapping= aes(x = distance, y= n, fill = id))+ - geom_col(position = "dodge")+ - ylab("Distance")+ - labs(fill = "Case")+ - scale_fill_discrete(labels= c("Base Case", "90%","95%", "99%" )) -ms_trips_Region_plot - -# b) City - -ms_distance_trips_BaseCity <- modal_split_trips_distance(BaseTripsCity) -ms_distance_trips_Area90City <- modal_split_trips_distance(AREA90TripsCity) -ms_distance_trips_Area95City <- modal_split_trips_distance(AREA95TripsCity) -ms_distance_trips_Area99City <- modal_split_trips_distance(AREA99TripsCity) - -ms_distance_trips_CITY <- bind_rows(ms_distance_BaseCity, - ms_distance_trips_Area90City, - ms_distance_trips_Area95City, - ms_distance_trips_Area99City, .id = "id") - -ms_trips_city_plot <- ms_distance_trips_CITY %>% - group_by(id) %>% - ggplot(mapping= aes(x = distance, y= n, fill = id))+ - geom_col(position = "dodge")+ - ylab("Distance")+ - labs(fill = "Case")+ - scale_fill_discrete(labels= c("Base Case", "90%","95%", "99%" )) -ms_trips_city_plot - - -# c) area90 - -ms_distance_trips_Base_90 <- modal_split_trips_distance(BaseTrips90) -ms_distance_trips_Area90_90 <- modal_split_trips_distance(AREA90Trips90) -ms_distance_trips_Area95_90 <- modal_split_trips_distance(AREA95Trips90) -ms_distance_trips_Area99_90 <- modal_split_trips_distance(AREA99Trips90) - -ms_distance_trips_90 <- bind_rows(ms_distance_trips_Base_90, - ms_distance_trips_Area90_90, - ms_distance_trips_Area95_90, - ms_distance_trips_Area99_90, .id = "id") - -ms_trips_90_plot <- ms_distance_trips_90 %>% - group_by(id) %>% - ggplot(mapping= aes(x = distance, y= n, fill = id))+ - geom_col(position = "dodge")+ - ylab("Distance")+ - labs(fill = "Case")+ - scale_fill_discrete(labels= c("Base Case", "90%","95%", "99%" )) -ms_trips_90_plot - -# d) area95 - -ms_distance_trips_Base_95 <- modal_split_trips_distance(BaseTrips95) -ms_distance_trips_Area90_95 <- modal_split_trips_distance(AREA90Trips95) -ms_distance_trips_Area95_95 <- modal_split_trips_distance(AREA95Trips95) -ms_distance_trips_Area99_95 <- modal_split_trips_distance(AREA99Trips95) - -ms_distance_trips_95 <- bind_rows(ms_distance_trips_Base_95, - ms_distance_trips_Area90_95, - ms_distance_trips_Area95_95, - ms_distance_trips_Area99_95, .id = "id") - -ms_trips_95_plot <- ms_distance_trips_95 %>% - group_by(id) %>% - ggplot(mapping= aes(x = distance, y= n, fill = id))+ - geom_col(position = "dodge")+ - ylab("Distance")+ - labs(fill = "Case")+ - scale_fill_discrete(labels= c("Base Case", "90%","95%", "99%" )) -ms_trips_90_plot - -# e) area99 - -ms_distance_trips_Base_99 <- modal_split_trips_distance(BaseTrips99) -ms_distance_trips_Area90_99 <- modal_split_trips_distance(AREA90Trips99) -ms_distance_trips_Area95_99 <- modal_split_trips_distance(AREA95Trips99) -ms_distance_trips_Area99_99 <- modal_split_trips_distance(AREA99Trips99) - -ms_distance_trips_99 <- bind_rows(ms_distance_trips_Base_99, - ms_distance_trips_Area90_99, - ms_distance_trips_Area95_99, - ms_distance_trips_Area99_99, .id = "id") - -ms_trips_99_plot <- ms_distance_trips_99 %>% - group_by(id) %>% - ggplot(mapping= aes(x = distance, y= n, fill = id))+ - geom_col(position = "dodge")+ - ylab("Distance")+ - labs(fill = "Case")+ - scale_fill_discrete(labels= c("Base Case", "90%","95%", "99%" )) -ms_trips_99_plot - - -#### #1.3 Modal Split - legs based - main mode (count) #### - -modal_split_legs_mode <- function(x){ - #### - - x %>% - count(mode) %>% - mutate(percent = 100*n/sum(n)) -} - -# a) Region - -ms_mode_legs_BaseRegion <- modal_split_legs_mode(BaseLegsRegion) -ms_mode_legs_Area90Region <- modal_split_legs_mode(AREA90LegsRegion) -ms_mode_legs_Area95Region <- modal_split_legs_mode(AREA95LegsRegion) -ms_mode_legs_Area99Region <- modal_split_legs_mode(AREA99LegsRegion) - -ms_mode_legs_Region <- bind_rows(ms_mode_legs_BaseRegion, - ms_mode_legs_Area90Region, - ms_mode_legs_Area95Region, - ms_mode_legs_Area99Region, .id = "id") - -ms_legs_Region_plot <- ms_mode_legs_Region %>% - group_by(id) %>% - ggplot(mapping= aes(x = mode, y= n, fill = id))+ - geom_col(position = "dodge")+ - ylab("Number of legs")+ - labs(fill = "Case")+ - scale_fill_discrete(labels= c("Base Case", "90%","95%", "99%" )) -ms_legs_Region_plot - -# b) City - -ms_mode_legs_BaseCity <- modal_split_legs_mode(BaseLegsCity) -ms_mode_legs_Area90City <- modal_split_legs_mode(AREA90LegsCity) -ms_mode_legs_Area95City <- modal_split_legs_mode(AREA95LegsCity) -ms_mode_legs_Area99City <- modal_split_legs_mode(AREA99LegsCity) - -ms_mode_legs_CITY <- bind_rows(ms_mode_legs_BaseCity, - ms_mode_legs_Area90City, - ms_mode_legs_Area95City, - ms_mode_legs_Area99City, .id = "id") - -ms_legs_city_plot <- ms_mode_legs_CITY %>% - group_by(id) %>% - ggplot(mapping= aes(x = mode, y= n, fill = id))+ - geom_col(position = "dodge")+ - ylab("Number of legs")+ - labs(fill = "Case")+ - scale_fill_discrete(labels= c("Base Case", "90%","95%", "99%" )) -ms_legs_city_plot - - -# c) area90 - -ms_mode_legs_Base_90 <- modal_split_legs_mode(BaseLegs90) -ms_mode_legs_Area90_90 <- modal_split_legs_mode(AREA90Legs90) -ms_mode_legs_Area95_90 <- modal_split_legs_mode(AREA95Legs90) -ms_mode_legs_Area99_90 <- modal_split_legs_mode(AREA99Legs90) - -ms_mode_legs_90 <- bind_rows(ms_mode_legs_Base_90, - ms_mode_legs_Area90_90, - ms_mode_legs_Area95_90, - ms_mode_legs_Area99_90, .id = "id") - -ms_legs_90_plot <- ms_mode_legs_90 %>% - group_by(id) %>% - ggplot(mapping= aes(x = mode, y= n, fill = id))+ - geom_col(position = "dodge")+ - ylab("Number of legs")+ - labs(fill = "Case")+ - scale_fill_discrete(labels= c("Base Case", "90%","95%", "99%" )) -ms_legs_90_plot - -# d) area95 - -ms_mode_legs_Base_95 <- modal_split_legs_mode(BaseLegs95) -ms_mode_legs_Area90_95 <- modal_split_legs_mode(AREA90Legs95) -ms_mode_legs_Area95_95 <- modal_split_legs_mode(AREA95Legs95) -ms_mode_legs_Area99_95 <- modal_split_legs_mode(AREA99Legs95) - -ms_mode_legs_95 <- bind_rows(ms_mode_legs_Base_95, - ms_mode_legs_Area90_95, - ms_mode_legs_Area95_95, - ms_mode_legs_Area99_95, .id = "id") - -ms_legs_95_plot <- ms_mode_legs_95 %>% - group_by(id) %>% - ggplot(mapping= aes(x = mode, y= n, fill = id))+ - geom_col(position = "dodge")+ - ylab("Number of legs")+ - labs(fill = "Case")+ - scale_fill_discrete(labels= c("Base Case", "90%","95%", "99%" )) -ms_legs_90_plot - -# e) area99 - -ms_mode_legs_Base_99 <- modal_split_legs_mode(BaseLegs99) -ms_mode_legs_Area90_99 <- modal_split_legs_mode(AREA90Legs99) -ms_mode_legs_Area95_99 <- modal_split_legs_mode(AREA95Legs99) -ms_mode_legs_Area99_99 <- modal_split_legs_mode(AREA99Legs99) - -ms_mode_legs_99 <- bind_rows(ms_mode_legs_Base_99, - ms_mode_legs_Area90_99, - ms_mode_legs_Area95_99, - ms_mode_legs_Area99_99, .id = "id") - -ms_legs_99_plot <- ms_mode_legs_99 %>% - group_by(id) %>% - ggplot(mapping= aes(x = mode, y= n, fill = id))+ - geom_col(position = "dodge")+ - ylab("Number of legs")+ - labs(fill = "Case")+ - scale_fill_discrete(labels= c("Base Case", "90%","95%", "99%" )) -ms_trips_99_plot - - -#### #1.4 Modal Split - legs based - distance #### -modal_split_legs_distance <- function(x){ - x %>% - group_by(mode) %>% - summarise(distance = sum(distance)) %>% - mutate(percent = round(100*distance/sum(distance),2)) -} - -# a) Region - -ms_distance_legs_BaseRegion <- modal_split_legs_distance(BaseLegsRegion) -ms_distance_legs_Area90Region <- modal_split_legs_distance(AREA90LegsRegion) -ms_distance_legs_Area95Region <- modal_split_legs_distance(AREA95LegsRegion) -ms_distance_legs_Area99Region <- modal_split_legs_distance(AREA99LegsRegion) - -ms_distance_legs_Region <- bind_rows(ms_distance_legs_BaseRegion, - ms_distance_legs_Area90Region, - ms_distance_legs_Area95Region, - ms_distance_legs_Area99Region, .id = "id") - -ms_legs_Region_plot <- ms_distance_legs_Region %>% - group_by(id) %>% - ggplot(mapping= aes(x = distance, y= n, fill = id))+ - geom_col(position = "dodge")+ - ylab("Distance")+ - labs(fill = "Case")+ - scale_fill_discrete(labels= c("Base Case", "90%","95%", "99%" )) -ms_legs_Region_plot - -# b) City - -ms_distance_legs_BaseCity <- modal_split_legs_distance(BaseLegsCity) -ms_distance_legs_Area90City <- modal_split_legs_distance(AREA90LegsCity) -ms_distance_legs_Area95City <- modal_split_legs_distance(AREA95LegsCity) -ms_distance_legs_Area99City <- modal_split_legs_distance(AREA99LegsCity) - -ms_distance_legs_CITY <- bind_rows(ms_distance_BaseCity, - ms_distance_legs_Area90City, - ms_distance_legs_Area95City, - ms_distance_legs_Area99City, .id = "id") - -ms_legs_city_plot <- ms_distance_legs_CITY %>% - group_by(id) %>% - ggplot(mapping= aes(x = distance, y= n, fill = id))+ - geom_col(position = "dodge")+ - ylab("Distance")+ - labs(fill = "Case")+ - scale_fill_discrete(labels= c("Base Case", "90%","95%", "99%" )) -ms_legs_city_plot - - -# c) area90 - -ms_distance_legs_Base_90 <- modal_split_legs_distance(BaseLegs90) -ms_distance_legs_Area90_90 <- modal_split_legs_distance(AREA90Legs90) -ms_distance_legs_Area95_90 <- modal_split_legs_distance(AREA95Legs90) -ms_distance_legs_Area99_90 <- modal_split_legs_distance(AREA99Legs90) - -ms_distance_legs_90 <- bind_rows(ms_distance_legs_Base_90, - ms_distance_legs_Area90_90, - ms_distance_legs_Area95_90, - ms_distance_legs_Area99_90, .id = "id") - -ms_legs_90_plot <- ms_distance_legs_90 %>% - group_by(id) %>% - ggplot(mapping= aes(x = distance, y= n, fill = id))+ - geom_col(position = "dodge")+ - ylab("Distance")+ - labs(fill = "Case")+ - scale_fill_discrete(labels= c("Base Case", "90%","95%", "99%" )) -ms_legs_90_plot - -# d) area95 - -ms_distance_legs_Base_95 <- modal_split_legs_distance(BaseLegs95) -ms_distance_legs_Area90_95 <- modal_split_legs_distance(AREA90Legs95) -ms_distance_legs_Area95_95 <- modal_split_legs_distance(AREA95Legs95) -ms_distance_legs_Area99_95 <- modal_split_legs_distance(AREA99Legs95) - -ms_distance_legs_95 <- bind_rows(ms_distance_legs_Base_95, - ms_distance_legs_Area90_95, - ms_distance_legs_Area95_95, - ms_distance_legs_Area99_95, .id = "id") - -ms_legs_95_plot <- ms_distance_legs_95 %>% - group_by(id) %>% - ggplot(mapping= aes(x = distance, y= n, fill = id))+ - geom_col(position = "dodge")+ - ylab("Distance")+ - labs(fill = "Case")+ - scale_fill_discrete(labels= c("Base Case", "90%","95%", "99%" )) -ms_legs_90_plot - -# e) area99 - -ms_distance_legs_Base_99 <- modal_split_legs_distance(BaseLegs99) -ms_distance_legs_Area90_99 <- modal_split_legs_distance(AREA90Legs99) -ms_distance_legs_Area95_99 <- modal_split_legs_distance(AREA95Legs99) -ms_distance_legs_Area99_99 <- modal_split_legs_distance(AREA99Legs99) - -ms_distance_legs_99 <- bind_rows(ms_distance_legs_Base_99, - ms_distance_legs_Area90_99, - ms_distance_legs_Area95_99, - ms_distance_legs_Area99_99, .id = "id") - -ms_legs_99_plot <- ms_distance_legs_99 %>% - group_by(id) %>% - ggplot(mapping= aes(x = distance, y= n, fill = id))+ - geom_col(position = "dodge")+ - ylab("Distance")+ - labs(fill = "Case")+ - scale_fill_discrete(labels= c("Base Case", "90%","95%", "99%" )) -ms_trips_99_plot - - - - - -#### #2 ModalSplit - pkm and ph#### - - - -#### #3 ModalShift#### - -#Function -sankey_dataframe <- function(x, y){ - inner_join(x, y, by = "trip_id") %>% - select(trip_id, main_mode.x, longest_distance_mode.x, main_mode.y, longest_distance_mode.y) %>% - group_by(main_mode.x, main_mode.y) %>% - summarise(Freq = n()) -} - -#Base Case > 90% -Base_case_city_to_Area90_city <- sankey_dataframe(BaseTripsCity, AREA90TripsCity) - -sk_main_mode_base_area90_city <- alluvial(Base_case_city_to_Area90_city[1:2], - freq= Base_case_city_to_Area90_city$Freq, - border = NA, - axis_labels = c("Base Case", "90%")) - - -#Base Case > 95% -Base_case_city_to_Area95_city <- sankey_dataframe(BaseTripsCity, AREA95TripsCity) - -sk_main_mode_base_area95_city <- alluvial(Base_case_city_to_Area95_city[1:2], - freq= Base_case_city_to_Area95_city$Freq, - border = NA, - axis_labels = c("Base Case", "95%")) - - -#Base Case > 99% - -Base_case_city_to_Area99_city <- sankey_dataframe(BaseTripsCity, AREA99TripsCity) - -sk_main_mode_base_area90_city <- alluvial(Base_case_city_to_Area99_city[1:2], - freq= Base_case_city_to_Area99_city$Freq, - border = NA, - axis_labels = c("Base Case", "99%")) - - - -#### #4 Traffic volumes#### - -# contains a set of links Ids which are to be considered. -shape <- st_read("/Users/mkreuschnervsp/Desktop/Projects/03_NaMaV/MATSim/shapefiles/Leipzig_puffer.shp", crs=25832) -#shape <- st_read("/Users/mkreuschnervsp/Desktop/Projects/03_NaMaV/MATSim/shapefiles/Leipzig_stadt.shp", crs=25832) -#shape <- st_read("/Users/mkreuschnervsp/Desktop/Projects/03_NaMaV/MATSim/shapefiles/Zonen90_update.shp", crs=25832) -#shape <- st_read("/Users/mkreuschnervsp/Desktop/Projects/03_NaMaV/MATSim/shapefiles/Zonen95_update.shp", crs=25832) -#shape <- st_read("/Users/mkreuschnervsp/Desktop/Projects/03_NaMaV/MATSim/shapefiles/Zonen99_update.shp", crs=25832) - -linkList <- read_delim(list.files("/Users/mkreuschnervsp/Desktop/Projects/03_NaMaV/MATSim/networks/leipzig-flexa-25pct-scaledFleet-baseCase_noDepot.output_network.xml.gz", full.names = T, include.dirs = F) , delim = ";", trim_ws = T, - col_types = cols( - person = col_character(), - good_type = col_integer() - )) %>% - st_as_sf(coords = c("x", "y"), crs = 25832) %>% - st_filter(shape) - - - - -#Leipzig -links_filtered <- network %>% - filter(links %in% linkList$link) - -traffic_base <- read_table("/Users/mkreuschnervsp/Desktop/Projects/03_NaMaV/MATSim/simwrapper/dashboard/simwrapper/05_traffic/base/traffic_output_base.csv") -mean_base_cong = mean(traffic_base$congestion_index) -print(mean_base_cong) -median_base_cong = median(traffic_base$congestion_index) -print(median_base_cong) - -mean_base_speed = mean(traffic_base$average_daily_speed) -print(mean_base_speed) -median_base_speed = median(traffic_base$average_daily_speed) -print(median_base_speed) - -traffic_99 <- read_table("/Users/mkreuschnervsp/Desktop/Projects/03_NaMaV/MATSim/simwrapper/dashboard/simwrapper/05_traffic/99/traffic_output_99.csv") -mean_99_cong = mean(traffic_99$congestion_index) -print(mean_99_cong) -median_99_cong = median(traffic_99$congestion_index) -print(median_99_cong) - -mean_99_speed = mean(traffic_99$average_daily_speed) -print(mean_99_speed) -median_99_speed = median(traffic_99$average_daily_speed) -print(median_99_speed) - -traffic_95 <- read_table("/Users/mkreuschnervsp/Desktop/Projects/03_NaMaV/MATSim/simwrapper/dashboard/simwrapper/05_traffic/95/traffic_output_95.csv") -mean_95_cong = mean(traffic_95$congestion_index) -print(mean_95_cong) -median_95_cong = median(traffic_95$congestion_index) -print(median_95_cong) - -mean_95_speed = mean(traffic_95$average_daily_speed) -print(mean_95_speed) -median_95_speed = median(traffic_95$average_daily_speed) -print(median_95_speed) - -traffic_90 <- read_table("/Users/mkreuschnervsp/Desktop/Projects/03_NaMaV/MATSim/simwrapper/dashboard/simwrapper/05_traffic/90/traffic_output_90.csv") -mean_90_cong = mean(traffic_90$congestion_index) -print(mean_90_cong) -median_90_cong = median(traffic_90$congestion_index) -print(median_90_cong) - -mean_90_speed = mean(traffic_90$average_daily_speed) -print(mean_90_speed) -median_90_speed = median(traffic_90$average_daily_speed) -print(median_90_speed) - -traffic_base_u <- read_table("/Users/mkreuschnervsp/Desktop/Projects/03_NaMaV/MATSim/carfree_scenario/R_results/traffic/traffic_output_base_umland.csv") -mean_base_cong_u = mean(traffic_base_u$congestion_index) -print(mean_base_cong) -median_base_cong_u = median(traffic_base_u$congestion_index) -print(median_base_cong) - -mean_base_speed_u = mean(traffic_base_u$average_daily_speed) -print(mean_base_speed_u) -median_base_speed_u = median(traffic_base_u$average_daily_speed) -print(median_base_speed_u) - -#### #5 Emissions#### - -network <- loadNetwork("/Users/mkreuschnervsp/Desktop/Projects/03_NaMaV/MATSim/networks/leipzig-flexa-25pct-scaledFleet-baseCase_noDepot.output_network.xml.gz") - -links_network <- data.frame(links_Leipzig[2]) - - - -shape_L <- st_read("/Users/mkreuschnervsp/Desktop/Projects/03_NaMaV/MATSim/shapefiles/Leipzig_stadt.shp", crs=25832) -shape_99 <- st_read("/Users/mkreuschnervsp/Desktop/Projects/03_NaMaV/MATSim/shapefiles/Zonen99_update.shp", crs=25832) -shape_95 <- st_read("/Users/mkreuschnervsp/Desktop/Projects/03_NaMaV/MATSim/shapefiles/Zonen95_update.shp", crs=25832) -shape_90 <- st_read("/Users/mkreuschnervsp/Desktop/Projects/03_NaMaV/MATSim/shapefiles/Zonen90_update.shp", crs=25832) - - -#links in Leipzig_Stadt -links_Leipzig <- links_network %>% st_as_sf(coords = c("links.x.from", "links.y.from"), crs = 25832) %>% st_filter(shape_L) -#links in Zonen -links_Zonen99 <- links_network %>% st_as_sf(coords = c("links.x.from", "links.y.from"), crs = 25832) %>% st_filter(shape_99) -links_Zonen95 <- links_network %>% st_as_sf(coords = c("links.x.from", "links.y.from"), crs = 25832) %>% st_filter(shape_95) -links_Zonen90 <- links_network %>% st_as_sf(coords = c("links.x.from", "links.y.from"), crs = 25832) %>% st_filter(shape_90) - -length(which(links_Leipzig$links.type == "highway.motorway")) -length(which(links_Leipzig$links.type == "highway.motorway_link")) -length(which(links_Leipzig$links.type == "highway.service")) -length(which(links_Leipzig$links.type == "highway.primary")) -length(which(links_Leipzig$links.type == "highway.primary_link")) -length(which(links_Leipzig$links.type == "highway.secondary")) -length(which(links_Leipzig$links.type == "highway.secondary_link")) -length(which(links_Leipzig$links.type == "highway.tertiary")) -length(which(links_Leipzig$links.type == "highway.residential")) -length(which(links_Leipzig$links.type == "highway.living_street")) -length(which(links_Leipzig$links.type == "highway.trunk")) -length(which(links_Leipzig$links.type == "highway.trunk_link")) -length(which(links_Leipzig$links.type == "highway.unclassified")) - -Haupt_Leipzig <- filter(links_Leipzig, links.type == "highway.primary" | links.type =="highway.primary_link" | links.type == "highway.secondary" | links.type =="highway.secondary_link" | links.type =="highway.service" | links.type =="highway.motorway") -Neben_Leipzig <- filter(links_Leipzig, links.type == "highway.residential" | links.type == "highway.tertiary" | links.type == "highway.living_street" | links.type == "highway.unclassified") - - -emissions_file_base <- read_delim("/Users/...") -emissions_file_99 <- read_delim("/Users/...") -emissions_file_95 <- read_delim("/Users/...") -emissions_file_90 <- read_delim("/Users/...") -#Emissionen auf Hauptstraßenlinks in Leipzig -emissions_haupt_leipzig <- filter(emissions_file_base, ) -mean() - -# Transform our 'x' vector -x <- data.frame(x) - -# Boxplot with vector -ggplot(data = x, aes(x = "", y = x)) + - stat_boxplot(geom = "errorbar", # Error bars - width = 0.2) + - geom_boxplot(fill = "#4271AE", # Box color - outlier.colour = "red", # Outliers color - alpha = 0.9) + # Box color transparency - ggtitle("Boxplot with vector") + # Plot title - xlab("") + # X-axis label - coord_flip() # Horizontal boxplot - -#Emissionen auf Nebenstraßenlinks in Leipzig -emissions_neben_leipzig <- - mean() - -#Emissionen auf Nebenstraßenlinks innerhalb Zonen -emissions_neben_zonen99 <- - mean() -emissions_neben_zonen95 <- - mean() -emissions_neben_zonen90 <- - mean() - -#Verkehr auf Hauptstraßenlinks in Leipzig -traffic_haupt_leipzig <- - mean() - - -#Verkehr auf Nebenstraßenlinks in Leipzig -traffic_neben_leipzig <- - mean() - - -#Verkehr auf Nebenstraßenlinks innerhalb Zonen -traffic_neben_zonen99 <- - mean() -traffic_neben_zonen95 <- - mean() -traffic_neben_zonen90 <- - mean() - - -####Sim - Trips#### -RegionSim <- RegionTrips %>% - group_by(dist_group, main_mode) %>% - summarise(RegionTrips=n()) %>% - mutate(mode = fct_relevel(main_mode, "walk", "bike", "pt", "ride", "car","freight")) %>% - mutate(scaled_trips=sim_scale * RegionTrips) %>% - mutate(source = "sim") - -CitySim <- CityTrips %>% - group_by(dist_group, main_mode) %>% - summarise(CityTrips=n()) %>% - mutate(mode = fct_relevel(main_mode, "walk", "bike", "pt", "ride", "car","freight")) %>% - mutate(scaled_trips=sim_scale * CityTrips) %>% - mutate(source = "sim") - -AreaSim1 <- AreaTrips1 %>% - group_by(dist_group, main_mode) %>% - summarise(AreaTrips1=n()) %>% - mutate(mode = fct_relevel(main_mode, "walk", "bike", "pt", "ride", "car","freight")) %>% - mutate(scaled_trips=sim_scale * AreaTrips1) %>% - mutate(source = "sim") - -AreaSim2 <- AreaTrips2 %>% - group_by(dist_group, main_mode) %>% - summarise(AreaTrips2=n()) %>% - mutate(mode = fct_relevel(main_mode, "walk", "bike", "pt", "ride", "car","freight")) %>% - mutate(scaled_trips=sim_scale * AreaTrips2) %>% - mutate(source = "sim") - -AreaSim3 <- AreaTrips3 %>% - group_by(dist_group, main_mode) %>% - summarise(AreaTrips3=n()) %>% - mutate(mode = fct_relevel(main_mode, "walk", "bike", "pt", "ride", "car","freight")) %>% - mutate(scaled_trips=sim_scale * AreaTrips3) %>% - mutate(source = "sim") - -RegionSim <- RegionSim %>% - mutate(share=RegionTrips/sum(RegionSim$RegionTrips)) -CitySim <- CitySim %>% - mutate(share=CityTrips/sum(CitySim$CityTrips)) -AreaSim1 <- AreaSim1 %>% - mutate(share=AreaTrips1/sum(AreaSim1$AreaTrips1)) -AreaSim2 <- AreaSim2 %>% - mutate(share=AreaTrips2/sum(AreaSim2$AreaTrips2)) -AreaSim3 <- AreaSim3 %>% - mutate(share=AreaTrips3/sum(AreaSim3$AreaTrips3)) - -#PATH where to write output -write_csv(RegionSim, "") -write_csv(CitySim, "") -write_csv(AreaSim1, "") -write_csv(AreaSim2, "") -write_csv(AreaSim3, "") - - - -####OUTPUT path where to put the R ouput e.g. for simwrapper#### diff --git a/src/main/R/master_drt.R b/src/main/R/master_drt.R new file mode 100644 index 00000000..dcbd5f9a --- /dev/null +++ b/src/main/R/master_drt.R @@ -0,0 +1,173 @@ +#### counting DRT services #### + +drt.modes <- unique(scenario.trips.table$main_mode) %>% + str_subset(pattern ="^drt") %>% + as.list() + +##### Step 1: reading files and doing calculations for each DRT service ##### +# create dfs, which are to be filled with values when iterating +df.supply.names <- c("Number of simulated vehicles", "Total Service Hours", "Total Fleet Km") +df.supply <- data.frame(df.supply.names) +colnames(df.supply) <- "Title" + +df.demand.names <- c("Nr of rides", "Mean in-vehicle travel time [s]", "Mean euclidean stop2stop distance [m]") +df.demand <- data.frame(df.demand.names) +colnames(df.demand) <- "Title" + +df.performance.names <- c("Total fleet km", "Total passenger km", "Empty ratio", "Total nr of rides", "Avg. rides per vehicle", "Rides per veh-km", "Rides per operating hour") +df.performance <- data.frame(df.performance.names) +colnames(df.performance) <- "Title" + +df.waiting.time.names <- c("Waiting mean [s]", "Waiting median [s]", "Waiting p95 [s]") +df.waiting.time <- data.frame(df.waiting.time.names) +colnames(df.waiting.time) <- "Title" + +for (drtMode in drt.modes){ + #### reading DRT files #### + drt.files <- as.vector(list.files(path = scenario.run.path, pattern = drtMode)) + drt.vehicle.stats <- read.csv(paste0(scenario.run.path, str_subset(drt.files, pattern = "vehicle_stats")), sep = ";") %>% + tail(n=1) + drt.customers <- read.csv(paste0(scenario.run.path, str_subset(drt.files, pattern = "customer_stats")), sep = ";")%>% + tail(n=1) + drt.KPI <- read_tsv(paste0(outputDirectoryScenarioDrt, drtMode, "_KPI.tsv")) + + #temporary solution: drt stops file do not follow same naming pattern + + if (drtMode == "drtNorth"){ + stop.pattern = "drt.*north" + } else if (drtMode == "drtSoutheast") { + stop.pattern = "drt.*southeast" + } else { + stop.pattern = "drt.*" + } + + ### XML files ### + drt.vehicles <- xmlParse(paste0(scenario.run.path, str_subset(drt.files, pattern = "vehicles"))) %>% + xmlToList(vehicle) + drt.vehicles <- data.frame(do.call(rbind.data.frame,drt.vehicles)) + names(drt.vehicles) <- c("vehicle", "start_link", "t_0", "t_1", "capacity") + + # talk about omitting this analysis as it is rather useless anyways, we can just use a hexagon plot.. -sme0623 + # result of discussion: lets use hexagon plots + # we keep this here though, we may want to include this analysis sometime + #mb use this: https://vsp.berlin/avoev/v/aggregate-od/gladbeck/output-snzDrtO444l/viz-od-2.yml + # DRT stops files can now be read as XML - the correct ones just need to be put into the folder on public svn & the code un-commented + # drt.stops.raw <- xmlParse(paste0(outputDirectoryScenarioDrt, str_subset(list.files(path = outputDirectoryScenarioDrt), pattern = stop.pattern))) + # drt.stops <- xmlToList(drt_stops_raw) + # drt.stops <- data.frame(do.call(cbind.data.frame,drt.stops)) + # drt.stops <- data.frame(t(drt.stops[-1])) + + + #### 9.1 DRT supply #### + if (x_drt_supply ==1){ + print("#### in 9.1 ####") + + nr.conventional.vehicles <- drt.vehicle.stats$vehicles + conventional.fleet.distance <- round(drt.vehicle.stats$totalDistance/1000) + op.hours <- (as.numeric(drt.vehicles[1, 4]) - as.numeric(drt.vehicles[1, 3]))/3600 + + Title <- df.supply.names + Value <- c(nr.conventional.vehicles, op.hours, conventional.fleet.distance) + + df.supply.drtMode <- data.frame(Title, Value) + colnames(df.supply.drtMode)[2] <- drtMode + write.csv(df.supply.drtMode, file = paste0(outputDirectoryScenarioDrt, "/table.supply.", drtMode, ".csv"), row.names = FALSE, quote=FALSE) + + df.supply <- cbind(df.supply, df.supply.drtMode[2]) + } + + + #### 9.2 DRT demand #### + if (x_drt_demand ==1) { + print("#### in 9.2 ####") + + rides <- drt.customers$rides + in.vehicle.trav.time.mean <- round(drt.customers$inVehicleTravelTime_mean) + euclidean.distance.traveled.mean <- round(drt.KPI$trips_euclidean_distance_mean) + + Title <- df.demand.names + Value <- c(rides, in.vehicle.trav.time.mean, euclidean.distance.traveled.mean) + + df.demand.drtMode <- data.frame(Title, Value) + colnames(df.demand.drtMode)[2] <- drtMode + write.csv(df.demand.drtMode, file = paste0(outputDirectoryScenarioDrt, "/table.demand.", drtMode, ".csv"), row.names = FALSE, quote=FALSE) + + df.demand <- cbind(df.demand, df.demand.drtMode[2]) + } + #### 9.3 DRT performance #### + if (x_drt_performance) { + print("#### in 9.3 ####") + nr.conventional.vehicles <- drt.vehicle.stats$vehicles + conventional.fleet.distance <- round(drt.vehicle.stats$totalDistance/1000) + + flexa.passengerdistance <- round(drt.vehicle.stats$totalPassengerDistanceTraveled/1000) + flexa.empty_ratio <- drt.vehicle.stats$emptyRatio + flexa.rides <- drt.customers$rides + flexa.rides.per.vehicle <- flexa.rides / nr.conventional.vehicles + flexa.rides.per.vehKM<- round(flexa.rides / conventional.fleet.distance, digits = 2) + + op.hours <- (as.numeric(drt.vehicles[1, 4]) - as.numeric(drt.vehicles[1, 3]))/3600 + flexa.rides.per.opHour <- round(flexa.rides / op.hours, digits = 2) + + Title <- df.performance.names + Value <- c(conventional.fleet.distance, flexa.passengerdistance, flexa.empty_ratio, flexa.rides, flexa.rides.per.vehicle, flexa.rides.per.vehKM, flexa.rides.per.opHour) + + df.performance.drtMode <- data.frame(Title, Value) + colnames(df.performance.drtMode)[2] <- drtMode + write.csv(df.performance.drtMode, file = paste0(outputDirectoryScenarioDrt, "/table.performance.", drtMode, ".csv"), row.names = FALSE, quote=FALSE) + + df.performance <- cbind(df.performance, df.performance.drtMode[2]) + + #---------- + + flexa.waiting.mean <- drt.KPI$waiting_time_mean + flexa.waiting.median <- drt.KPI$waiting_time_median + flexa.waiting.p95 <- drt.KPI$waiting_time_95_percentile + + Title <- df.waiting.time.names + Value <- c(flexa.waiting.mean, flexa.waiting.median, flexa.waiting.p95) + + df.waiting.time.drtMode <- data.frame(Title, Value) + colnames(df.waiting.time.drtMode)[2] <- drtMode + write.csv(df.waiting.time.drtMode, file = paste0(outputDirectoryScenarioDrt, "/table.waitingtime.", drtMode, ".csv"), row.names = FALSE, quote=FALSE) + + df.waiting.time <- cbind(df.waiting.time, df.waiting.time.drtMode[2]) + } +} + +##### Step 2: print the joined tables for supply, demand and performance ##### +#### DRT supply #### +if (x_drt_supply == 1){ + write.csv(df.supply, file = paste0(outputDirectoryScenarioDrt, "/table.supply.csv"), row.names = FALSE, quote=FALSE) +} + + +#### DRT demand #### + +if (x_drt_demand == 1) { + write.csv(df.demand, file = paste0(outputDirectoryScenarioDrt, "/table.demand.csv"), row.names = FALSE, quote=FALSE) +} + +#### DRT performance #### +if (x_drt_performance == 1) { + write.csv(df.performance, file = paste0(outputDirectoryScenarioDrt, "/table.performance.csv"), row.names = FALSE, quote=FALSE) + write.csv(df.waiting.time, file = paste0(outputDirectoryScenarioDrt, "/table.waitingtime.csv"), row.names = FALSE, quote=FALSE) +} + +#### DRT volumes #### + #no calculations in R necessary at the moment + +#### 9.4 DRT trip purpose analysis #### +if (x_drt_trip_purposes == 1) { + print("#### in 9.4 ####") + + source_with_args <- function(file, ...){ + system(paste("Rscript", file, ...)) + } + source_with_args( + "../matsim-leipzig/src/main/R/Analysis/ODAnalysis/Trippurpose-Analysis.R", + paste0("--runDir=", scenario.run.path) + ) +} + + diff --git a/src/main/R/masteranalyse.R b/src/main/R/masteranalyse.R index 41224397..66ce88dd 100644 --- a/src/main/R/masteranalyse.R +++ b/src/main/R/masteranalyse.R @@ -1,467 +1,765 @@ #### reading shp files #### -RegionShape <- st_read(region_shp_path, crs=CRS) #study area -CityShape <- st_read(city_shp_path, crs=CRS) #city of Leipzig -AreaShape <- st_read(area_shp_path, crs=CRS)#scenario area -print("#### Shapes geladen! ####") +region.shape <- st_read(region.shp.path, crs=CRS) #study area = leipzig + surroundings +city.shape <- st_read(city.shp.path, crs=CRS) #city of Leipzig +carfree.area.shape <- st_read(carfree.area.shp.path, crs=CRS)# carfree area +print("#### Shape files loaded! ####") #### reading trips/legs files #### ## Trip File -scenarioTripsTable <- readTripsTable(pathToMATSimOutputDirectory = paste0(scenario_run_path,list.files(path = scenario_run_path, pattern = "output_trips"))) -print("#### Trips geladen! ####") +scenario.trips.table <- readTripsTable(pathToMATSimOutputDirectory = scenario.run.path) +print("#### Trips loaded! ####") ## Leg Files -scenariolegsTable <- read_delim(paste0(scenario_run_path,list.files(path = scenario_run_path, pattern = "output_legs")), delim= ";", n_max = 3000) -print("#### Legs geladen! ####") +#do you really only want 3000 legs? -jr April'23 +# I removed the n_max = 3000 for now -jr May'23 +# n_max = 3000 is useful for testing / debugging so it is just commented out now - sme0623 +scenario.legs.table <- read_delim(paste0(scenario.run.path,"/",list.files(path = scenario.run.path, pattern = "output_legs")), delim= ";")#, n_max = 3000) +print("#### Legs loaded! ####") ## Filters -scenario_trips_region <- filterByRegion(scenarioTripsTable,RegionShape,crs=CRS,start.inshape = TRUE,end.inshape = TRUE) -scenario_trips_city <- filterByRegion(scenarioTripsTable,CityShape,crs=CRS,start.inshape = TRUE,end.inshape = TRUE) -scenario_trips_area <- filterByRegion(scenarioTripsTable,AreaShape,crs=CRS,start.inshape = TRUE,end.inshape = TRUE) -print("#### Trips gefiltert! ####") -scenario_legs_region <- filterByRegion(scenariolegsTable,RegionShape,crs=CRS,start.inshape = TRUE,end.inshape = TRUE) -scenario_legs_city <- filterByRegion(scenariolegsTable,CityShape,crs=CRS,start.inshape = TRUE,end.inshape = TRUE) -scenario_legs_area <- filterByRegion(scenariolegsTable,AreaShape,crs=CRS,start.inshape = TRUE,end.inshape = TRUE) -print("#### Legs gefiltert! ####") - -#### reading persons #### -scenario_persons <- read_delim(paste0(scenario_run_path,list.files(path = scenario_run_path, pattern = "output_persons")), delim = ";") +scenario.trips.region <- filterByRegion(scenario.trips.table,region.shape,crs=CRS,start.inshape = TRUE,end.inshape = TRUE) +scenario.trips.city <- filterByRegion(scenario.trips.table,city.shape,crs=CRS,start.inshape = TRUE,end.inshape = TRUE) +scenario.trips.carfree.area <- filterByRegion(scenario.trips.table, carfree.area.shape, crs=CRS, start.inshape = TRUE, end.inshape = TRUE) + +print("#### Trips filtered! ####") +scenario.legs.region <- filterByRegion(scenario.legs.table,region.shape,crs=CRS,start.inshape = TRUE,end.inshape = TRUE) +scenario.legs.city <- filterByRegion(scenario.legs.table,city.shape,crs=CRS,start.inshape = TRUE,end.inshape = TRUE) +scenario.legs.carfree.area <- filterByRegion(scenario.legs.table, carfree.area.shape, crs=CRS, start.inshape = TRUE, end.inshape = TRUE) +print("#### Legs filtered! ####") + +#### Define functions which are needed more than once #### + +avg_trav_distance <- function(x){ + x %>% + select(main_mode, traveled_distance) %>% + group_by(main_mode) %>% + summarise(avg_trav_distance = round(mean(traveled_distance/1000), digits = 3)) %>% + pivot_wider(names_from = main_mode, values_from = avg_trav_distance) +} + +avg_beeline_distance <- function(x){ + x %>% + select(main_mode, euclidean_distance) %>% + group_by(main_mode) %>% + summarise(avg_beeline_distance = round(mean(euclidean_distance))) %>% + pivot_wider(names_from = main_mode, values_from = avg_beeline_distance) +} + +avg_trav_time <- function(x){ + x %>% + select(main_mode, trav_time) %>% + mutate(trav_time = hms(trav_time)) %>% + group_by(main_mode) %>% + summarise(avgTime_s = round(mean(hour(trav_time)*3600 + minute(trav_time) *60 + second(trav_time) ))) %>% + pivot_wider(names_from = main_mode, values_from = avgTime_s) +} + +if (x_sankey_diagram == 1){ + base.trips.table <- readTripsTable(pathToMATSimOutputDirectory = base.run.path) + + base.trips.region <- filterByRegion(base.trips.table,region.shape,crs=CRS,start.inshape = TRUE,end.inshape = TRUE) + base.trips.city <- filterByRegion(base.trips.table,city.shape,crs=CRS,start.inshape = TRUE,end.inshape = TRUE) + base.trips.carfree.area <- filterByRegion(base.trips.table, carfree.area.shape, crs=CRS, start.inshape = TRUE, end.inshape = TRUE) + +} + +if (x_winner_loser == 1){ + base.persons <- readPersonsTable(base.run.path) + #### reading persons #### just needed for winner-loser-analysis + scenario.persons <- readPersonsTable(scenario.run.path) + print("#### Population loaded! ####") +} + #### 0. Parameters #### #BREAKING DIFFERENT DISTANCES IN M -breaks = c(0, 1000, 2000, 5000, 10000, 20000, Inf) -breaks2 = c(0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000, 11000, 12000, 13000, 14000, 15000, 16000, 17000, 18000, 19000, 20000, Inf) +# for modal split +breaks <- c(0, 1000, 2000, 5000, 10000, 20000, Inf) +# for heatmap +dist_breaks <- c(0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000, 11000, 12000, 13000, 14000, 15000, 16000, 17000, 18000, 19000, 20000, Inf) +time_breaks <- c(-Inf, 0, 1200, 2400,3600,4800,6000,7200,8400,9600,10800,12000,13200,14400,15600,16800,18000,Inf) +time_labels <- c("0 mins", "<20 mins","<40 mins", "<60 mins","<80 mins","<100 mins","<120 mins", + "<140 mins" ,"<160 mins", "<180 mins" , "<200 mins", "<220 mins" ,"<240 mins", + "<260 mins", "<280 mins", "<300 mins", ">= 300 mins") #NAMES OF THE CASES cases <- c("base", "scenario") +print("#### Parameters specified! ####") #### #1.1 Modal Split - trips based - main mode (count) #### - if (x_ms_trips_count == 1){ - - modal_split_trips_main_mode <- function(x){ + print("#### in 1.1 ####") + + modal_split.trips.main_mode <- function(x){ x %>% count(main_mode) %>% mutate(percent = 100*n/sum(n)) } - - ms_main_mode_trips_scenarioCity <- modal_split_trips_main_mode(scenario_trips_city) - ms_main_mode_trips_ScenarioCity <- t(ms_main_mode_trips_scenarioCity) - colnames(ms_main_mode_trips_ScenarioCity) <- ms_main_mode_trips_ScenarioCity[1, ] - write.csv(ms_main_mode_trips_ScenarioCity,file = paste0(outputDirectoryScenario,"/ms_main_mode_trips_city.modestats.csv")) + + ms.main_mode.trips.city <- modal_split.trips.main_mode(scenario.trips.city) + ms.main_mode.trips.city <- t(ms.main_mode.trips.city) + colnames(ms.main_mode.trips.city) <- ms.main_mode.trips.city[1, ] + write.csv(ms.main_mode.trips.city, file = paste0(outputDirectoryScenario, "/pie.ms.counts.trips.city.csv"), row.names = FALSE, quote=FALSE) - ms_main_mode_trips_scenarioRegion <- modal_split_trips_main_mode(scenario_trips_region) - ms_main_mode_trips_ScenarioRegion <- t(ms_main_mode_trips_scenarioRegion) - colnames(ms_main_mode_trips_ScenarioRegion) <- ms_main_mode_trips_ScenarioRegion[1, ] - write.csv(ms_main_mode_trips_scenarioRegion,file = paste0(outputDirectoryScenario,"/ms_main_mode_trips_region.modestats.csv")) + ms.main_mode.trips.region <- modal_split.trips.main_mode(scenario.trips.region) + ms.main_mode.trips.region <- t(ms.main_mode.trips.region) + colnames(ms.main_mode.trips.region) <- ms.main_mode.trips.region[1, ] + write.csv(ms.main_mode.trips.region, file = paste0(outputDirectoryScenario, "/pie.ms.counts.trips.regio.csv"), row.names = FALSE, quote=FALSE) + + ms.main_mode.trips.carfree.area <- modal_split.trips.main_mode(scenario.trips.carfree.area) + ms.main_mode.trips.carfree.area <- t(ms.main_mode.trips.carfree.area) + colnames(ms.main_mode.trips.carfree.area) <- ms.main_mode.trips.carfree.area[1, ] + write.csv(ms.main_mode.trips.carfree.area, file = paste0(outputDirectoryScenario, "/pie.ms.counts.trips.carfree.area.csv"), row.names = FALSE, quote=FALSE) } #### #1.2 Modal Split - trips based - distance #### if (x_ms_trips_distance == 1){ - modal_split_trips_distance <- function(x){ + print("#### in 1.2 ####") + modal_split.trips.distance <- function(x){ x %>% group_by(main_mode) %>% summarise(distance = sum(traveled_distance)) %>% mutate(percent = round(100*distance/sum(distance),2)) } - ms_dist_trips_scenarioCity <- modal_split_trips_distance(scenario_trips_city) - ms_dist_trips_ScenarioCity <- t(ms_dist_trips_scenarioCity) - colnames(ms_dist_trips_ScenarioCity) <- ms_dist_trips_ScenarioCity[1, ] - write.csv(ms_dist_trips_ScenarioCity,file = paste0(outputDirectoryScenario,"/ms_dist_trips_city.modestats.csv")) - - ms_dist_trips_scenarioRegion <- modal_split_trips_distance(scenario_trips_region) - ms_dist_trips_ScenarioRegion <- t(ms_dist_trips_scenarioRegion) - colnames(ms_dist_trips_ScenarioRegion) <- ms_dist_trips_ScenarioRegion[1, ] - write.csv(ms_dist_trips_scenarioRegion,file = paste0(outputDirectoryScenario,"/ms_dist_trips_region.modestats.csv")) + ms.dist.trips.city <- modal_split.trips.distance(scenario.trips.city) + ms.dist.trips.city <- t(ms.dist.trips.city) + colnames(ms.dist.trips.city) <- ms.dist.trips.city[1, ] + write.csv(ms.dist.trips.city, file = paste0(outputDirectoryScenario, "/pie.ms.dist.trips.city.csv"), row.names = FALSE, quote=FALSE) + + ms.dist.trips.region <- modal_split.trips.distance(scenario.trips.region) + ms.dist.trips.region <- t(ms.dist.trips.region) + colnames(ms.dist.trips.region) <- ms.dist.trips.region[1, ] + write.csv(ms.dist.trips.region, file = paste0(outputDirectoryScenario, "/pie.ms.dist.trips.region.csv"), row.names = FALSE, quote=FALSE) + + ms.dist.trips.carfree.area <- modal_split.trips.distance(scenario.trips.carfree.area) + ms.dist.trips.carfree.area <- t(ms.dist.trips.carfree.area) + colnames(ms.dist.trips.carfree.area) <- ms.dist.trips.carfree.area[1, ] + write.csv(ms.dist.trips.carfree.area, file = paste0(outputDirectoryScenario, "/pie.ms.dist.trips.carfree.area.csv"), row.names = FALSE, quote=FALSE) + + ms.dist.trips.network <- modal_split.trips.distance(scenario.trips.table) + ms.dist.trips.network <- t(ms.dist.trips.network) + colnames(ms.dist.trips.network) <- ms.dist.trips.network[1, ] + write.csv(ms.dist.trips.network, file = paste0(outputDirectoryScenario, "/pie.ms.dist.trips.network.csv"), row.names = FALSE, quote=FALSE) } #### #1.3 Modal Split - legs based - main mode (count) #### if (x_ms_legs_count == 1){ - modal_split_legs_mode <- function(x){ + print("#### in 1.3 ####") + modal_split.legs.mode <- function(x){ x %>% mutate(distance_cut = cut(distance, breaks = breaks, labels = c("<1000m", "1 - 2km", "2 - 5km", "5 - 10km", "10 - 20km", ">20km" ))) %>% - group_by(distance_cut) %>% + group_by(distance_cut) %>% count(mode) %>% mutate(percent = 100*n/sum(n)) } - ms_mode_legs_scenarioCity <- modal_split_legs_mode(scenario_trips_city) - ms_mode_legs_ScenarioCity <- t(ms_mode_legs_scenarioCity) - colnames(ms_mode_legs_ScenarioCity) <- ms_mode_legs_ScenarioCity[1, ] - write.csv(ms_mode_legs_ScenarioCity,file = paste0(outputDirectoryScenario,"/ms_mode_legs_city.modestats.csv")) - - ms_mode_legs_scenarioRegion <- modal_split_legs_mode(scenario_trips_region) - ms_mode_legs_ScenarioRegion <- t(ms_mode_legs_scenarioRegion) - colnames(ms_mode_legs_ScenarioRegion) <- ms_mode_legs_ScenarioRegion[1, ] - write.csv(ms_mode_legs_scenarioRegion,file = paste0(outputDirectoryScenario,"/ms_mode_legs_region.modestats.csv")) + ms.mode.legs.city <- modal_split.legs.mode(scenario.legs.city) + ms.mode.legs.city <- t(ms.mode.legs.city) + colnames(ms.mode.legs.city) <- ms.mode.legs.city[1, ] + write.csv(ms.mode.legs.city,file = paste0(outputDirectoryScenario,"/pie.ms.counts.legs.city.csv"), row.names = FALSE, quote=FALSE) + + ms.mode.legs.region <- modal_split.legs.mode(scenario.legs.region) + ms.mode.legs.region <- t(ms.mode.legs.region) + colnames(ms.mode.legs.region) <- ms.mode.legs.region[1, ] + write.csv(ms.mode.legs.region,file = paste0(outputDirectoryScenario,"/pie.ms.counts.legs.region.csv"), row.names = FALSE, quote=FALSE) + + ms.mode.legs.carfree.area <- modal_split.legs.mode(scenario.legs.carfree.area) + ms.mode.legs.carfree.area <- t(ms.mode.legs.carfree.area) + colnames(ms.mode.legs.carfree.area) <- ms.mode.legs.carfree.area[1, ] + write.csv(ms.mode.legs.carfree.area,file = paste0(outputDirectoryScenario,"/pie.ms.counts.legs.carfree.area.csv"), row.names = FALSE, quote=FALSE) } #### #1.4 Modal Split - legs based - distance #### if (x_ms_legs_distance == 1){ - modal_split_legs_distance <- function(x){ + print("#### in 1.4 ####") + modal_split.legs.distance <- function(x){ x %>% group_by(mode) %>% summarise(distance = sum(distance)) %>% mutate(percent = round(100*distance/sum(distance),2)) } - ms_dist_legs_scenarioCity <- modal_split_legs_distance(scenario_trips_city) - ms_dist_legs_ScenarioCity <- t(ms_dist_legs_scenarioCity) - colnames(ms_dist_legs_ScenarioCity) <- ms_dist_legs_ScenarioCity[1, ] - write.csv(ms_dist_legs_ScenarioCity,file = paste0(outputDirectoryScenario,"/ms_dist_legs_city.modestats.csv")) - - ms_dist_legs_scenarioRegion <- modal_split_legs_distance(scenario_trips_region) - ms_dist_legs_ScenarioRegion <- t(ms_dist_legs_scenarioRegion) - colnames(ms_dist_legs_ScenarioRegion) <- ms_dist_legs_ScenarioRegion[1, ] - write.csv(ms_dist_legs_scenarioRegion,file = paste0(outputDirectoryScenario,"/ms_dist_legs_region.modestats.csv")) + ms.dist.legs.city <- modal_split.legs.distance(scenario.legs.city) + ms.dist.legs.city <- t(ms.dist.legs.city) + colnames(ms.dist.legs.city) <- ms.dist.legs.city[1, ] + write.csv(ms.dist.legs.city,file = paste0(outputDirectoryScenario,"/pie.ms.dist.legs.city.csv"), row.names = FALSE, quote=FALSE) + + ms.dist.legs.region <- modal_split.legs.distance(scenario.legs.region) + ms.dist.legs.region <- t(ms.dist.legs.region) + colnames(ms.dist.legs.region) <- ms.dist.legs.region[1, ] + write.csv(ms.dist.legs.region,file = paste0(outputDirectoryScenario,"/pie.ms.dist.legs.region.csv"), row.names = FALSE, quote=FALSE) + + ms.dist.legs.carfree.area <- modal_split.legs.distance(scenario.legs.carfree.area) + ms.dist.legs.carfree.area <- t(ms.dist.legs.carfree.area) + colnames(ms.dist.legs.carfree.area) <- ms.dist.legs.carfree.area[1, ] + write.csv(ms.dist.legs.carfree.area,file = paste0(outputDirectoryScenario,"/pie.ms.dist.legs.carfree.area.csv"), row.names = FALSE, quote=FALSE) } #### #2.1 Sankey Modal Shift #### - if (x_sankey_diagram == 1){ - sankey_dataframe <- function(x, y){ + print("#### in 2.1 ####") + sankey.dataframe <- function(x, y){ inner_join(x, y, by = "trip_id") %>% - select(trip_id, main_mode.x, longest_distance_mode.x, main_mode.y, longest_distance_mode.y) %>% + select(main_mode.x, main_mode.y) %>% group_by(main_mode.x, main_mode.y) %>% summarise(Freq = n()) } - + #Base Case > Policy Case CITY - Base_city_to_Scenario_city <- sankey_dataframe(base_trips_city, scenario_trips_city) - - sankey_city <- alluvial(Base_city_to_Scenario_city[1:2], - freq= Base_case_city_to_Scenario_city$Freq, + base.city.to.scenario.city <- sankey.dataframe(base.trips.city, scenario.trips.city) + + # Here the sankey is plotted to csv -> dashboard. + # Afterwards a local plot is created. The plot is just a nice to have, it could be deleted, too -sm30623 + write.csv(base.city.to.scenario.city, file = paste0(outputDirectoryScenario, "/sankey.shift.city.csv"), row.names = FALSE, quote=FALSE) + + sankey.city <- alluvial(base.city.to.scenario.city[1:2], + freq= base.city.to.scenario.city$Freq, border = NA, axis_labels = c("Base Case", "Scenario Case")) - - sankey_city <- as_tibble(t(sankey_city)) - write.csv(sankey_city, file = paste0(outputDirectoryBase,"/sankey_city.csv")) - + #Base Case > Policy Case REGION - Base_city_to_Scenario_city <- sankey_dataframe(base_trips_region, scenario_trips_region) - - sankey_region <- alluvial(Base_region_to_Scenario_region[1:2], - freq= Base_case_region_to_Scenario_region$Freq, + base.region.to.scenario.region <- sankey.dataframe(base.trips.region, scenario.trips.region) + + # Here the sankey is plotted to csv -> dashboard. + # Afterwards a local plot is created. The plot is just a nice to have, it could be deleted, too -sm30623 + write.csv(base.region.to.scenario.region, file = paste0(outputDirectoryScenario, "/sankey.shift.region.csv"), row.names = FALSE, quote=FALSE) + + sankey.region <- alluvial(base.region.to.scenario.region[1:2], + freq= base.region.to.scenario.region$Freq, + border = NA, + axis_labels = c("Base Case", "Scenario Case")) + + #Base Case > Policy Case CARFREE AREA + base.carfree.area.to.scenario.carfree.area <- sankey.dataframe(base.trips.carfree.area, scenario.trips.carfree.area) + + # Here the sankey is plotted to csv -> dashboard. + # Afterwards a local plot is created. The plot is just a nice to have, it could be deleted, too -sm30623 + write.csv(base.carfree.area.to.scenario.carfree.area, file = paste0(outputDirectoryScenario, "/sankey.shift.carfree.area.csv"), row.names = FALSE, quote=FALSE) + + sankey.carfree.area <- alluvial(base.carfree.area.to.scenario.carfree.area[1:2], + freq= base.carfree.area.to.scenario.carfree.area$Freq, border = NA, axis_labels = c("Base Case", "Scenario Case")) - - sankey_region <- as_tibble(t(sankey_region)) - write.csv(sankey_region, file = paste0(outputDirectoryBase,"/sankey_region.csv")) } #### #3.1 Average Traveled Distance - trips based#### - if (x_average_traveled_distance_trips == 1){ - - avg_trav_distance_trips_by_mode <- function(x){ - x %>% - group_by(main_mode) %>% - summarise_at(vars(traveled_distance), list(name=mean)) - } + print("#### in 3.1 ####") #calculation - avg_trav_dist_trips_scenario_network <- avg_trav_distance_trips_by_mode(scenarioTripsTable) - avg_trav_dist_trips_scenario_region <- avg_trav_distance_trips_by_mode(scenario_trips_region) - avg_trav_dist_trips_scenario_city <- avg_trav_distance_trips_by_mode(scenario_trips_city) - + avg.trav_dist.trips.scenario.network <- avg_trav_distance(scenario.trips.table) + avg.trav_dist.trips.scenario.region <- avg_trav_distance(scenario.trips.region) + avg.trav_dist.trips.scenario.city <- avg_trav_distance(scenario.trips.city) + avg.trav_dist.trips.scenario.carfree.area <- avg_trav_distance(scenario.trips.carfree.area) + #write table - write.csv(avg_trav_dist_trips_scenario_network, file = paste0(outputDirectoryScenario,"/avg_trav_dist_trips_network.csv")) - write.csv(avg_trav_dist_trips_scenario_region, file = paste0(outputDirectoryScenario,"/avg_trav_dist_trips_region.csv")) - write.csv(avg_trav_dist_trips_scenario_city, file = paste0(outputDirectoryScenario,"/avg_trav_dist_trips_city.csv")) + write.csv(avg.trav_dist.trips.scenario.network, file = paste0(outputDirectoryScenario,"/df.avg_trav_dist.trips.network.csv"), row.names = FALSE, quote=FALSE) + write.csv(avg.trav_dist.trips.scenario.region, file = paste0(outputDirectoryScenario,"/df.avg_trav_dist.trips.region.csv"), row.names = FALSE, quote=FALSE) + write.csv(avg.trav_dist.trips.scenario.city, file = paste0(outputDirectoryScenario,"/df.avg_trav_dist.trips.city.csv"), row.names = FALSE, quote=FALSE) + write.csv(avg.trav_dist.trips.scenario.carfree.area, file = paste0(outputDirectoryScenario,"/df.avg_trav_dist.trips.carfree.area.csv"), row.names = FALSE, quote=FALSE) + + df.list <- list(network = avg.trav_dist.trips.scenario.network, + region = avg.trav_dist.trips.scenario.region, + city = avg.trav_dist.trips.scenario.city) + write.csv(bind_rows(df.list, + .id = "id"), + file = paste0(outputDirectoryScenario, "/df.avg.trav_dist.trips.csv"), row.names = FALSE, quote=FALSE) + } + #### #3.2 Average Euclidean Distance - trips based#### if (x_average_euclidean_distance_trips == 1){ - - avg_eucl_distance_trips_by_mode <- function(x){ - x %>% - group_by(main_mode) %>% - #summarise_at(vars(traveled_distance), list(name=mean)) - summarise_at(vars(euclidean_distance), list(name=mean)) - } + print("#### in 3.2 ####") + #calculation - avg_eucl_dist_trips_scenario_network <- avg_eucl_distance_trips_by_mode(scenarioTripsTable) - avg_eucl_dist_trips_scenario_region <- avg_eucl_distance_trips_by_mode(scenario_trips_region) - avg_eucl_dist_trips_scenario_city <- avg_eucl_distance_trips_by_mode(scenario_trips_city) + avg.eucl_dist.trips.scenario.network <- avg_beeline_distance(scenario.trips.table) + avg.eucl_dist.trips.scenario.region <- avg_beeline_distance(scenario.trips.region) + avg.eucl_dist.trips.scenario.city <- avg_beeline_distance(scenario.trips.city) + avg.eucl_dist.trips.scenario.carfree.area <- avg_beeline_distance(scenario.trips.carfree.area) #write table - write.csv(avg_eucl_dist_trips_scenario_network, file = paste0(outputDirectoryScenario,"/avg_eucl_dist_trips_network.csv")) - write.csv(avg_eucl_dist_trips_scenario_region, file = paste0(outputDirectoryScenario,"/avg_eucl_dist_trips_region.csv")) - write.csv(avg_eucl_dist_trips_scenario_city, file = paste0(outputDirectoryScenario,"/avg_eucl_dist_trips_city.csv")) + write.csv(avg.eucl_dist.trips.scenario.network, file = paste0(outputDirectoryScenario,"/df.avg_eucl_dist.trips.network.csv"), row.names = FALSE, quote=FALSE) + write.csv(avg.eucl_dist.trips.scenario.region, file = paste0(outputDirectoryScenario,"/df.avg_eucl_dist.trips.region.csv"), row.names = FALSE, quote=FALSE) + write.csv(avg.eucl_dist.trips.scenario.city, file = paste0(outputDirectoryScenario,"/df.avg_eucl_dist.trips.city.csv"), row.names = FALSE, quote=FALSE) + write.csv(avg.eucl_dist.trips.scenario.carfree.area, file = paste0(outputDirectoryScenario,"/df.avg_eucl_dist.trips.carfree.area.csv"), row.names = FALSE, quote=FALSE) + + df.list <- list(network = avg.eucl_dist.trips.scenario.network, + region = avg.eucl_dist.trips.scenario.region, + city = avg.eucl_dist.trips.scenario.city) + write.csv(bind_rows(df.list, + .id = "id"), + file = paste0(outputDirectoryScenario, "/df.avg.eucl_dist.trips.csv"), row.names = FALSE, quote=FALSE) +} +#### #3.3 Traveled Distance Heatmap - trips based #### +if (x_heatmap_distance_trips == 1){ + print("#### in 3.3 ####") + + heatmap.trav_distance.trips.by.mode <- function(x){ + x %>% + mutate(dist_bin = as.numeric(cut(traveled_distance, breaks = dist_breaks))) %>% + group_by(main_mode, dist_bin) %>% + summarise(freq = n()) %>% + pivot_wider(names_from = main_mode, values_from = freq) + } + + heatmap.trav_distance.trips.city <- heatmap.trav_distance.trips.by.mode(scenario.trips.city) + + write.csv(heatmap.trav_distance.trips.city,file = paste0(outputDirectoryScenario,"/heatmap.trav_distance.trips.city.csv"), row.names = FALSE, quote=FALSE) + } -#### #3.3 Personen KM - trips based #### + +#### #3.4 Personen KM - trips based #### if (x_personen_km_trips == 1){ - personen_km_trips <- function (x){ + print("#### in 3.4 ####") + personen.km.trips <- function (x){ x %>% filter(main_mode!="freight") %>% group_by(main_mode) %>% - summarise(pers_km = sum(traveled_distance)/1000) - + summarise(pers_km = round(sum(traveled_distance)/1000)) %>% + pivot_wider(names_from = main_mode, values_from = pers_km) + } - pkm_trips_scenario_city <- personen_km_trips(scenario_trips_city) - pkm_trips_scenario_region <- personen_km_trips(scenario_trips_region) - pkm_trips_scenario_network <- personen_km_trips(scenarioTripsTable) - - write.csv(pkm_trips_scenario_city, file = paste0(outputDirectoryScenario,"/trips_city_pkm.csv")) - write.csv(pkm_trips_scenario_region, file = paste0(outputDirectoryScenario,"/trips_region_pkm.csv")) - write.csv(pkm_trips_scenario_network, file = paste0(outputDirectoryScenario,"/trips_network_pkm.csv")) - + pkm.trips.city <- personen.km.trips(scenario.trips.city) + pkm.trips.region <- personen.km.trips(scenario.trips.region) + pkm.trips.network <- personen.km.trips(scenario.trips.table) + pkm.trips.carfree.area <- personen.km.trips(scenario.trips.carfree.area) + + write.csv(pkm.trips.city, file = paste0(outputDirectoryScenario,"/df.pkm.trips.city.csv"), row.names = FALSE, quote=FALSE) + write.csv(pkm.trips.region, file = paste0(outputDirectoryScenario,"/df.pkm.trips.region.csv"), row.names = FALSE, quote=FALSE) + write.csv(pkm.trips.network, file = paste0(outputDirectoryScenario,"/df.pkm.trips.network.csv"), row.names = FALSE, quote=FALSE) + write.csv(pkm.trips.carfree.area, file = paste0(outputDirectoryScenario,"/df.pkm.trips.carfree.area.csv"), row.names = FALSE, quote=FALSE) + + df.list <- list(network = pkm.trips.network, + region = pkm.trips.region, + city = pkm.trips.city) + write.csv(bind_rows(df.list, + .id = "id"), + file = paste0(outputDirectoryScenario, "/df.pkm.trips.csv"), row.names = FALSE, quote=FALSE) } -#### #3.4 Average Traveled Distance - legs based##### +#### #3.5 Average Traveled Distance - legs based##### if (x_average_traveled_distance_legs == 1){ - - avg_trav_distance_legs_by_mode <- function(x){ + print("#### in 3.5 ####") + + avg.trav_distance.legs.by.mode <- function(x){ x %>% - group_by(main_mode) %>% - summarise_at(vars(traveled_distance), list(name=mean)) - #summarise_at(vars(euclidean_distance), list(name=mean)) + group_by(mode) %>% + summarise_at(vars(distance), list(name=mean)) %>% + pivot_wider(names_from = mode, values_from = name) + } #calculation - avg_trav_dist_legs_scenario_network <- avg_trav_distance_legs_by_mode(scenarioLegsTable) - avg_trav_dist_legs_scenario_region <- avg_trav_distance_legs_by_mode(scenario_legs_region) - avg_trav_dist_legs_scenario_city <- avg_trav_distance_legs_by_mode(scenario_legs_city) + avg.trav_dist.legs.scenario.network <- avg.trav_distance.legs.by.mode(scenario.legs.table) + avg.trav_dist.legs.scenario.region <- avg.trav_distance.legs.by.mode(scenario.legs.region) + avg.trav_dist.legs.scenario.city <- avg.trav_distance.legs.by.mode(scenario.legs.city) + avg.trav_dist.legs.scenario.carfree.area <- avg.trav_distance.legs.by.mode(scenario.legs.carfree.area) #write table - write.csv(avg_trav_dist_legs_scenario_network, file = paste0(outputDirectoryScenario,"/avg_trav_dist_legs_network.csv")) - write.csv(avg_trav_dist_legs_scenario_region, file = paste0(outputDirectoryScenario,"/avg_trav_dist_legs_region.csv")) - write.csv(avg_trav_dist_legs_scenario_city, file = paste0(outputDirectoryScenario,"/avg_trav_dist_legs_city.csv")) -} -#### #3.5 Average Euclidean Distance - legs based##### -if (x_average_euclidean_distance_legs == 1){ - - avg_eucl_distance_legs_by_mode <- function(x){ - x %>% - group_by(main_mode) %>% - summarise_at(vars(traveled_distance), list(name=mean)) - #summarise_at(vars(euclidean_distance), list(name=mean)) + write.csv(avg.trav_dist.legs.scenario.network, file = paste0(outputDirectoryScenario,"/df.avg_trav_dist.legs.network.csv"), row.names = FALSE, quote=FALSE) + write.csv(avg.trav_dist.legs.scenario.region, file = paste0(outputDirectoryScenario,"/df.avg_trav_dist.legs.region.csv"), row.names = FALSE, quote=FALSE) + write.csv(avg.trav_dist.legs.scenario.city, file = paste0(outputDirectoryScenario,"/df.avg_trav_dist.legs.city.csv"), row.names = FALSE, quote=FALSE) + write.csv(avg.trav_dist.legs.scenario.carfree.area, file = paste0(outputDirectoryScenario,"/df.avg_trav_dist.legs.carfree.area.csv"), row.names = FALSE, quote=FALSE) + + df.list <- list(network = avg.trav_dist.legs.scenario.network, + region = avg.trav_dist.legs.scenario.region, + city = avg.trav_dist.legs.scenario.city) + write.csv(bind_rows(df.list, + .id = "id"), + file = paste0(outputDirectoryScenario, "/df.trav_dist.legs.csv"), row.names = FALSE, quote=FALSE) } - #calculation - avg_eucl_dist_legs_scenario_network <- avg_eucl_distance_legs_by_mode(scenarioLegsTable) - avg_eucl_dist_legs_scenario_region <- avg_eucl_distance_legs_by_mode(scenario_legs_region) - avg_eucl_dist_legs_scenario_city <- avg_eucl_distance_legs_by_mode(scenario_legs_city) - #write table - write.csv(avg_eucl_dist_legs_scenario_network, file = paste0(outputDirectoryScenario,"/avg_eucl_dist_legs_network.csv")) - write.csv(avg_eucl_dist_legs_scenario_region, file = paste0(outputDirectoryScenario,"/avg_eucl_dist_legs_region.csv")) - write.csv(avg_eucl_dist_legs_scenario_city, file = paste0(outputDirectoryScenario,"/avg_eucl_dist_legs_city.csv")) -} + #### #3.6 Personen KM - legs based #### if (x_personen_km_legs == 1){ - personen_km_legs <- function (x){ + print("#### in 3.6 ####") + personen_km.legs <- function (x){ x %>% group_by(mode) %>% - summarise(pers_km = sum(distance)/1000) + summarise(pers_km = round(sum(distance)/1000)) %>% + pivot_wider(names_from = mode, values_from = pers_km) } - pkm_legs_scenario_city <- personen_km_legs(scenario_legs_city) - pkm_legs_scenario_region <- personen_km_legs(scenario_legs_region) - pkm_legs_scenario_network <- personen_km_legs(scenarioLegsTable) - - write.csv(pkm_legs_scenario_city, file = paste0(outputDirectoryScenario,"/legs_city_pkm.csv")) - write.csv(pkm_legs_scenario_region, file = paste0(outputDirectoryScenario,"/legs_region_pkm.csv")) - write.csv(pkm_legs_scenario_network, file = paste0(outputDirectoryScenario,"/legs_network_pkm.csv")) - + pkm.legs.city <- personen_km.legs(scenario.legs.city) + pkm.legs.region <- personen_km.legs(scenario.legs.region) + pkm.legs.network <- personen_km.legs(scenario.legs.table) + pkm.legs.carfree.area <- personen_km.legs(scenario.legs.carfree.area) + + write.csv(pkm.legs.city, file = paste0(outputDirectoryScenario,"/df.pkm.legs.city.csv"), row.names = FALSE, quote=FALSE) + write.csv(pkm.legs.region, file = paste0(outputDirectoryScenario,"/df.pkm.legs.region.csv"), row.names = FALSE, quote=FALSE) + write.csv(pkm.legs.network, file = paste0(outputDirectoryScenario,"/df.pkm.legs.network.csv"), row.names = FALSE, quote=FALSE) + write.csv(pkm.legs.carfree.area, file = paste0(outputDirectoryScenario,"/df.pkm.legs.carfree.area.csv"), row.names = FALSE, quote=FALSE) + + df.list <- list(network = pkm.legs.network, + region = pkm.legs.region, + city = pkm.legs.city) + write.csv(bind_rows(df.list, + .id = "id"), + file = paste0(outputDirectoryScenario, "/df.pkm.legs.csv"), row.names = FALSE, quote=FALSE) } #### #4.1 Average Travel Time - trips based ##### -if (x_average_traveled_distance_trips == 1){ - - avg_time_trips_by_mode <- function(x){ - x %>% - group_by(main_mode) %>% - summarise_at(vars(trav_time), list(name=mean)) - } +if (x_average_time_trips == 1){ + print("#### in 4.1 ####") + #calculation - avg_time_trips_scenario_network <- avg_time_trips_by_mode(scenarioTripsTable) - avg_time_trips_scenario_region <- avg_time_trips_by_mode(scenario_trips_region) - avg_time_trips_scenario_city <- avg_time_trips_by_mode(scenario_trips_city) + avg_time.trips.network <- avg_trav_time(scenario.trips.table) + avg_time.trips.region <- avg_trav_time(scenario.trips.region) + avg_time.trips.city <- avg_trav_time(scenario.trips.city) + avg_time.trips.carfree.area <- avg_trav_time(scenario.trips.carfree.area) #write table - write.csv(avg_time_trips_scenario_network, file = paste0(outputDirectoryScenario,"/avg_time_trips_network.csv")) - write.csv(avg_time_trips_scenario_region, file = paste0(outputDirectoryScenario,"/avg_time_trips_region.csv")) - write.csv(avg_time_trips_scenario_city, file = paste0(outputDirectoryScenario,"/avg_time_trips_city.csv")) -} - -#### #4.2 Personen Stunden - trips based #### -if (x_personen_h_trips == 1){ - personen_stunden_trips <- function (x){ - x %>% - filter(main_mode!="freight") %>% - group_by(mode) %>% - summarise(personen_stunden_trips = (sum(trav_time)) + write.csv(avg_time.trips.network, file = paste0(outputDirectoryScenario,"/df.avg_time.trips.network.csv"), row.names = FALSE, quote=FALSE) + write.csv(avg_time.trips.region, file = paste0(outputDirectoryScenario,"/df.avg_time.trips.region.csv"), row.names = FALSE, quote=FALSE) + write.csv(avg_time.trips.city, file = paste0(outputDirectoryScenario,"/df.avg_time.trips.city.csv"), row.names = FALSE, quote=FALSE) + write.csv(avg_time.trips.carfree.area, file = paste0(outputDirectoryScenario,"/df.avg_time.trips.carfree.area.csv"), row.names = FALSE, quote=FALSE) + + df.list <- list(network = avg_time.trips.network, + region = avg_time.trips.region, + city = avg_time.trips.city) + write.csv(bind_rows(df.list, + .id = "id"), + file = paste0(outputDirectoryScenario, "/df.avg_time.trips.csv"), row.names = FALSE, quote=FALSE) } - ph_trips_scenario_city <- personen_km_trips(scenario_trips_city) - ph_trips_scenario_region <- personen_km_trips(scenario_trips_region) - ph_trips_scenario_network <- personen_km_trips(scenarioTripsTable) - write.csv(ph_trips_scenario_city, file = paste0(outputDirectoryScenario,"/trips_city_ph.csv")) - write.csv(ph_trips_scenario_region, file = paste0(outputDirectoryScenario,"/trips_region_ph.csv")) - write.csv(ph_trips_scenario_network, file = paste0(outputDirectoryScenario,"/trips_network_ph.csv")) +#### #4.2 Average Travel Time - legs based ##### +if (x_average_time_legs == 1){ + print("#### in 4.2 ####") -} + avg_time.legs.by.mode <- function(x){ + x %>% + group_by(mode) %>% + summarise_at(vars(trav_time), list(name=mean)) %>% + pivot_wider(names_from = mode, values_from = name) + } -#### #4.3 Average Travel Time - legs based ##### + #calculation + avg_time.legs.network <- avg_time.legs.by.mode(scenario.legs.table) + avg_time.legs.region <- avg_time.legs.by.mode(scenario.legs.region) + avg_time.legs.city <- avg_time.legs.by.mode(scenario.legs.city) + avg_time.legs.carfree.area <- avg_time.legs.by.mode(scenario.legs.carfree.area) + #write table + write.csv(avg_time.legs.network, file = paste0(outputDirectoryScenario,"/df.avg_time.legs.network.csv"), row.names = FALSE, quote=FALSE) + write.csv(avg_time.legs.region, file = paste0(outputDirectoryScenario,"/df.avg_time.legs.region.csv"), row.names = FALSE, quote=FALSE) + write.csv(avg_time.legs.city, file = paste0(outputDirectoryScenario,"/df.avg_time.legs.city.csv"), row.names = FALSE, quote=FALSE) + write.csv(avg_time.legs.carfree.area, file = paste0(outputDirectoryScenario,"/df.avg_time.legs.carfree.area.csv"), row.names = FALSE, quote=FALSE) + + df.list <- list(network = avg_time.legs.network, + region = avg_time.legs.region, + city = avg_time.legs.city) + write.csv(bind_rows(df.list, + .id = "id"), + file = paste0(outputDirectoryScenario, "/df.avg_time.legs.csv"), row.names = FALSE, quote=FALSE) + } -if (x_average_traveled_distance_legs == 1){ - - avg_time_legs_by_mode <- function(x){ +#### #4.3 Personen Stunden - trips based #### +if (x_personen_h_trips == 1){ + print("#### in 4.3 ####") + person.hours.trips <- function (x){ x %>% + filter(main_mode!="freight") %>% group_by(main_mode) %>% - summarise_at(vars(trav_time), list(name=mean)) + # we want the total trav time values in h -sme0723 + summarise(personen_stunden_trips = round(sum(trav_time)/3600, digits = 2)) %>% + pivot_wider(names_from = main_mode, values_from = personen_stunden_trips) } - #calculation - avg_time_legs_scenario_network <- avg_time_legs_by_mode(scenarioLegsTable) - avg_time_legs_scenario_region <- avg_time_legs_by_mode(scenario_legs_region) - avg_time_legs_scenario_city <- avg_time_legs_by_mode(scenario_legs_city) - #write table - write.csv(avg_time_legs_scenario_network, file = paste0(outputDirectoryScenario,"/avg_time_legs_network.csv")) - write.csv(avg_time_legs_scenario_region, file = paste0(outputDirectoryScenario,"/avg_time_legs_region.csv")) - write.csv(avg_time_legs_scenario_city, file = paste0(outputDirectoryScenario,"/avg_time_legs_city.csv")) + ph.trips.city <- person.hours.trips(scenario.trips.city) + ph.trips.region <- person.hours.trips(scenario.trips.region) + ph.trips.network <- person.hours.trips(scenario.trips.table) + ph.trips.carfree.area <- person.hours.trips(scenario.trips.carfree.area) + + write.csv(ph.trips.city, file = paste0(outputDirectoryScenario,"/df.ph.trips.city.csv"), row.names = FALSE, quote=FALSE) + write.csv(ph.trips.region, file = paste0(outputDirectoryScenario,"/df.ph.trips.region.csv"), row.names = FALSE, quote=FALSE) + write.csv(ph.trips.network, file = paste0(outputDirectoryScenario,"/df.ph.trips.network.csv"), row.names = FALSE, quote=FALSE) + write.csv(ph.trips.carfree.area, file = paste0(outputDirectoryScenario,"/df.ph.trips.carfree.area.csv"), row.names = FALSE, quote=FALSE) + + df.list <- list(network = ph.trips.network, + region = ph.trips.region, + city = ph.trips.city) + write.csv(bind_rows(df.list, + .id = "id"), + file = paste0(outputDirectoryScenario, "/df.ph.trips.csv"), row.names = FALSE, quote=FALSE) } #### #4.4 Personen Stunden - legs based #### if (x_personen_h_legs == 1){ - personen_stunden_legs <- function (x){ + print("#### in 4.4 ####") + person_hours.legs <- function (x){ x %>% group_by(mode) %>% - summarise(personen_stunden_legs = (sum(trav_time)) - } - ph_legs_scenario_city <- personen_stunden_legs(scenario_legs_city) - ph_legs_scenario_region <- personen_stunden_legs(scenario_legs_region) - ph_legs_scenario_network <- personen_stunden_legs(scenarioLegsTable) + summarise(person_hours_legs = round(sum(trav_time))) %>% + pivot_wider(names_from = mode, values_from = person_hours_legs) - write.csv(ph_legs_scenario_city, file = paste0(outputDirectoryScenario,"/legs_city_ph.csv")) - write.csv(ph_legs_scenario_region, file = paste0(outputDirectoryScenario,"/legs_region_ph.csv")) - write.csv(ph_legs_scenario_network, file = paste0(outputDirectoryScenario,"/legs_network_ph.csv")) - + } + ph.legs.city <- person_hours.legs(scenario.legs.city) + ph.legs.region <- person_hours.legs(scenario.legs.region) + ph.legs.network <- person_hours.legs(scenario.legs.table) + ph.legs.carfree.area <- person_hours.legs(scenario.legs.carfree.area) + + write.csv(ph.legs.city, file = paste0(outputDirectoryScenario,"/df.ph.legs.city.csv"), row.names = FALSE, quote=FALSE) + write.csv(ph.legs.region, file = paste0(outputDirectoryScenario,"/df.ph.legs.region.csv"), row.names = FALSE, quote=FALSE) + write.csv(ph.legs.network, file = paste0(outputDirectoryScenario,"/df.ph.legs.network.csv"), row.names = FALSE, quote=FALSE) + write.csv(ph.legs.carfree.area, file = paste0(outputDirectoryScenario,"/df.ph.legs.carfree.area.csv"), row.names = FALSE, quote=FALSE) + + df.list <- list(network = ph.legs.network, + region = ph.legs.region, + city = ph.legs.city) + write.csv(bind_rows(df.list, + .id = "id"), + file = paste0(outputDirectoryScenario, "/df.ph.legs.csv"), row.names = FALSE, quote=FALSE) } -#### #5.1 Average Speed #### -if (x_average_traveled_speed_trips == 1){ -# x) function -avg_trav_distance <- function(x){ - x %>% - select(main_mode, traveled_distance) %>% - group_by(main_mode) %>% - summarise(avg_trav_distance = mean(traveled_distance)) %>% - column_to_rownames(var = "main_mode") +#### #4.5 Travel Time Heatmap - trips based #### +if (x_heatmap_time_trips == 1){ + print("#### in 4.5 ####") + + heatmap.trav_time.trips.by.mode <- function(x){ + + x %>% + mutate(trav_time_s = as.integer(x$trav_time, scientific = FALSE) ) %>% + mutate(time_bin = cut(trav_time_s, breaks = time_breaks, labels = time_labels)) %>% + group_by(main_mode, time_bin) %>% + summarise(freq = n()) %>% + pivot_wider(names_from = main_mode, values_from = freq) + } + + + heatmap.trav_time.trips.city <- heatmap.trav_time.trips.by.mode(scenario.trips.city) + + write.csv(heatmap.trav_time.trips.city,file = paste0(outputDirectoryScenario,"/heatmap.trav_distance.trips.city.csv"), row.names = FALSE, quote=FALSE) + } -avg_trav_time <- function(x){ - x %>% - select(main_mode, trav_time) %>% - mutate(trav_time = hms(trav_time)) %>% - group_by(main_mode) %>% - summarise(avgTime_s = mean(hour(trav_time)*3600 + minute(trav_time) *60 + second(trav_time) )) %>% - column_to_rownames(var = "main_mode") -} -avg_trav_dist_scenario_city <- avg_trav_distance(scenario_trips_city) -avg_trav_dist_scenario_region <- avg_trav_distance(scenario_trips_region) -avg_trav_dist_scenario_network <-avg_trav_distance(scenarioTripsTable) -avg_trav_time_scenario_city <- avg_trav_time(scenario_trips_city) -avg_trav_time_scenario_region <- avg_trav_time(scenario_trips_region) -avg_trav_time_scenario_network <- avg_trav_time(scenarioTripsTable) -avg_trav_speed_scenario_city = avg_trav_dist_scenario_city/avg_trav_time_scenario_city*3.6 #km/h -avg_trav_speed_scenario_region = avg_trav_dist_scenario_region/avg_trav_time_scenario_region*3.6 #km/h -avg_trav_speed_scenario_network = avg_trav_dist_scenario_network/avg_trav_time_scenario_network*3.6 - -#write tables -write.csv(avg_trav_speed_scenario_network, file = paste0(outputDirectoryScenario,"/avg_trav_speed_trips_network.csv")) -write.csv(avg_trav_speed_scenario_region, file = paste0(outputDirectoryScenario,"/avg_trav_speed_trips_region.csv")) -write.csv(avg_trav_speed_scenario_city, file = paste0(outputDirectoryScenario,"/avg_trav_speed_trips_city.csv")) +#### #5.1 Average Speed - trips based#### +if (x_average_traveled_speed_trips == 1){ + print("#### in 5.1 ####") + + avg_trav_dist.city <- avg_trav_distance(scenario.trips.city) + avg_trav_dist.region <- avg_trav_distance(scenario.trips.region) + avg_trav_dist.network <-avg_trav_distance(scenario.trips.table) + avg_trav_dist.carfree.area <-avg_trav_distance(scenario.trips.carfree.area) + + avg_trav_time.city <- avg_trav_time(scenario.trips.city) + avg_trav_time.region <- avg_trav_time(scenario.trips.region) + avg_trav_time.network <- avg_trav_time(scenario.trips.table) + avg_trav_time.carfree.area <- avg_trav_time(scenario.trips.carfree.area) + + avg_trav_speed.city = round(avg_trav_dist.city*1000/avg_trav_time.city*3.6) #km/h + avg_trav_speed.region = round(avg_trav_dist.region*1000/avg_trav_time.region*3.6) #km/h + avg_trav_speed.network = round(avg_trav_dist.network*1000/avg_trav_time.network*3.6) + avg_trav_speed.carfree.area = round(avg_trav_dist.carfree.area*1000/avg_trav_time.carfree.area*3.6) + + #write tables + write.csv(avg_trav_speed.network, file = paste0(outputDirectoryScenario,"/df.avg_trav_speed.trips.network.csv"), row.names = FALSE, quote=FALSE) + write.csv(avg_trav_speed.region, file = paste0(outputDirectoryScenario,"/df.avg_trav_speed.trips.region.csv"), row.names = FALSE, quote=FALSE) + write.csv(avg_trav_speed.city, file = paste0(outputDirectoryScenario,"/df.avg_trav_speed.trips.city.csv"), row.names = FALSE, quote=FALSE) + write.csv(avg_trav_speed.carfree.area, file = paste0(outputDirectoryScenario,"/df.avg_trav_speed.trips.carfree.area.csv"), row.names = FALSE, quote=FALSE) + + df.list <- list(network = avg_trav_speed.network, + region = avg_trav_speed.region, + city = avg_trav_speed.city) + write.csv(bind_rows(df.list, + .id = "id"), + file = paste0(outputDirectoryScenario, "/df.avg_trav_speed.csv"), row.names = FALSE, quote=FALSE) } -#### #5.2 Average Beeline Speed #### - +#### #5.2 Average Beeline Speed - trips based #### if (x_average_beeline_speed_trips == 1){ -# x) function -avg_beeline_distance <- function(x){ - x %>% - select(main_mode, euclidean_distance) %>% - group_by(main_mode) %>% - summarise(avg_beeline_distance = mean(euclidean_distance)) %>% - column_to_rownames(var = "main_mode") + print("#### in 5.2 ####") + + # average beeline distance and average travel time + avg_beeline_dist.city <- avg_beeline_distance(scenario.trips.city) + avg_beeline_dist.region <- avg_beeline_distance(scenario.trips.region) + avg_beeline_dist.network <- avg_beeline_distance(scenario.trips.table) + avg_beeline_dist.carfree.area <- avg_beeline_distance(scenario.trips.carfree.area) + + avg_trav_time.city <- avg_trav_time(scenario.trips.city) + avg_trav_time.region <- avg_trav_time(scenario.trips.region) + avg_trav_time.network <- avg_trav_time(scenario.trips.table) + avg_trav_time.carfree.area <- avg_trav_time(scenario.trips.carfree.area) + # average beeline speed + avg_beeline_speed.city = round(avg_beeline_dist.city/avg_trav_time.city*3.6) #km/h + avg_beeline_speed.region = round(avg_beeline_dist.region/avg_trav_time.region*3.6) #km/h + avg_beeline_speed.network = round(avg_beeline_dist.network/avg_trav_time.network*3.6) + avg_beeline_speed.carfree.area = round(avg_beeline_dist.carfree.area/avg_trav_time.carfree.area*3.6) + #write tables + write.csv(avg_beeline_speed.network, file = paste0(outputDirectoryScenario,"/df.avg_bee_speed_trips_network.csv"), row.names = FALSE, quote=FALSE) + write.csv(avg_beeline_speed.region, file = paste0(outputDirectoryScenario,"/df.avg_bee_speed_trips_region.csv"), row.names = FALSE, quote=FALSE) + write.csv(avg_beeline_speed.city, file = paste0(outputDirectoryScenario,"/df.avg_bee_speed_trips_city.csv"), row.names = FALSE, quote=FALSE) + write.csv(avg_beeline_speed.carfree.area, file = paste0(outputDirectoryScenario,"/df.avg_bee_speed_trips_carfree.area.csv"), row.names = FALSE, quote=FALSE) + + df.list <- list(network = avg_beeline_speed.network, + region = avg_beeline_speed.region, + city = avg_beeline_speed.city) + write.csv(bind_rows(df.list, + .id = "id"), + file = paste0(outputDirectoryScenario, "/df.avg_bee_speed.csv"), row.names = FALSE, quote=FALSE) } -avg_trav_time <- function(x){ - x %>% - select(main_mode, trav_time) %>% - mutate(trav_time = hms(trav_time)) %>% - group_by(main_mode) %>% - summarise(avgTime_s = mean(hour(trav_time)*3600 + minute(trav_time) *60 + second(trav_time) )) %>% - column_to_rownames(var = "main_mode") -} -# average beeline distance and average travel time -avg_beeline_dist_scenario_city <- avg_beeline_distance(scenario_trips_city) -avg_beeline_dist_scenario_region <- avg_beeline_distance(scenario_trips_region) -avg_beeline_dist_scenario_network <- avg_beeline_distance(scenarioTripsTable) -avg_trav_time_scenario_city <- avg_trav_time(scenario_trips_city) -avg_trav_time_scenario_region <- avg_trav_time(scenario_trips_region) -avg_trav_time_scenario_network <- avg_trav_time(scenarioTripsTable) -# average beeline speed -avg_beeline_speed_scenario_city = avg_beeline_dist_scenario_city/avg_trav_time_scenario_city*3.6 #km/h -avg_beeline_speed_scenario_region = avg_beeline_dist_scenario_region/avg_trav_time_scenario_region*3.6 #km/h -avg_beeline_speed_scenario_network = avg_beeline_dist_scenario_network/avg_trav_time_scenario_network*3.6 -#write tables -write.csv(avg_beeline_speed_scenario_network, file = paste0(outputDirectoryScenario,"/avg_bee_speed_trips_network.csv")) -write.csv(avg_beeline_speed_scenario_region, file = paste0(outputDirectoryScenario,"/avg_bee_speed_trips_region.csv")) -write.csv(avg_beeline_speed_scenario_city, file = paste0(outputDirectoryScenario,"/avg_bee_speed_trips_city.csv")) -} - -#### #6.1 Emissions #### +#### #7.1 Emissions #### if (x_emissions == 1){ + print("#### in 7.1 ####") } -#### #7.1 Traffic #### -if (x_traffic == 1){ -} -#### #8.1 Execution Scores Winner-Loser #### +#### #8.1 Equity / Winner-Loser Analysis #### if (x_winner_loser == 1){ - base_scenario_persons <- inner_join(base_persons, scenario_persons, by= "person") %>% - select(person, executed_score.x, executed_score.y, income.x, sex.x, age.x, carAvail.x, first_act_x.x, first_act_y.x) %>% - mutate(score_change = format((executed_score.y - executed_score.x), scientific = FALSE), person = as.character(person)) - - home_trips <- baseTripsTable %>% - filter(grepl("home", start_activity_type)) %>% - distinct(person, .keep_all = TRUE) %>% - select(person, start_link, start_x, start_y) - - base_scenario_persons <- full_join(base_scenario_persons, home_trips, by = "person") %>% - mutate(home_x = ifelse(is.na(start_x), first_act_x.x, start_x), - home_y = ifelse(is.na(start_y), first_act_y.x, start_y)) %>% - select(person, executed_score.x, executed_score.y, score_change, income.x, sex.x, age.x, carAvail.x, home_x, home_y) + print("#### in 8.1 ####") + # Data Prep + persons_joined <- join_base_and_policy_persons(base.persons, scenario.persons, city.shape) - write.csv(base_scenario_persons, file = paste0(outputDirectoryBase,"/ScoreTable.csv")) + persons_joined_sf <- transform_persons_sf(persons_joined, filter_shp = city.shape, first_act_type_filter = "home") %>% + mutate(score_diff = executed_score_policy - executed_score_base, + score_pct_change = score_diff/executed_score_base * 100) %>% + drop_na(score_pct_change) + # 8.1.1. SCORE + # a) Score Plot - Base vs. Policy Case + + persons_joined_sf %>% + st_drop_geometry() %>% + select(executed_score_base,executed_score_policy) %>% + pivot_longer(starts_with("executed_score"), names_to = "scenario", values_to = "score") %>% + transmute(scenario = scenario, score = as.factor(round(score))) %>% + group_by(scenario, score) %>% + summarise(count = n()) %>% + ungroup() %>% + pivot_wider(names_from = scenario, values_from = count, values_fill = NA) %>% + arrange(score) %>% + write.csv(file = paste0(outputDirectoryScenario,"/chart.equity.score_distrib.csv"), quote = FALSE, row.names = FALSE) - AgentsInNetwork <- nrow(base_scenario_persons) - MaxScoreNetworkBase <- max(base_scenario_persons$executed_score.x) - MinScoreNetworkBase <- min(base_scenario_persons$executed_score.x) - AvgScoreNetworkBase <- mean(base_scenario_persons$executed_score.x) - MaxScoreNetworkScenario <- max(base_scenario_persons$executed_score.y) - MinScoreNetworkScenario <- min(base_scenario_persons$executed_score.y) - AvgScoreNetworkScenario <- mean(base_scenario_persons$executed_score.y) - BiggestLoserNetwork <- min(base_scenario_persons$score_change) - GoodOrBadForNetwork = AvgScoreNetworkScenario - AvgScoreNetworkBase - BiggestWinnerNetwork <- max(base_scenario_persons$score_change) - - AgentsInRegion <- nrow(base_scenario_persons) - MaxScoreRegionBase <- max(base_scenario_persons$executed_score.x) - MinScoreRegionBase <- min(base_scenario_persons$executed_score.x) - AvgScoreRegionBase <- mean(base_scenario_persons$executed_score.x) - MaxScoreRegionScenario <- max(base_scenario_persons$executed_score.y) - MinScoreRegionScenario <- min(base_scenario_persons$executed_score.y) - AvgScoreRegionScenario <- mean(base_scenario_persons$executed_score.y) - BiggestLoserRegion <- min(base_scenario_persons$score_change) - GoodOrBadForRegion = AvgScoreRegionScenario - AvgScoreRegionBase - BiggestWinnerRegion <- max(base_scenario_persons$score_change) - - AgentsInCity <- nrow(base_scenario_persons) - MaxScoreCityBase <- max(base_scenario_persons$executed_score.x) - MinScoreCityBase <- min(base_scenario_persons$executed_score.x) - AvgScoreCityBase <- mean(base_scenario_persons$executed_score.x) - MaxScoreCityScenario <- max(base_scenario_persons$executed_score.y) - MinScoreCityScenario <- min(base_scenario_persons$executed_score.y) - AvgScoreCityScenario <- mean(base_scenario_persons$executed_score.y) - BiggestLoserCity <- min(base_scenario_persons$score_change) - GoodOrBadForCity = AvgScoreCityScenario - AvgScoreCityBase - BiggestWinnerCity <- max(base_scenario_persons$score_change) + # b) Score Summary Stats Table + pct_change_mean <- (mean(persons_joined_sf$executed_score_policy) - mean(persons_joined_sf$executed_score_base)) / mean(persons_joined_sf$executed_score_base) * 100 + pct_change_median <- (median(persons_joined_sf$executed_score_policy) - median(persons_joined_sf$executed_score_base)) / median(persons_joined_sf$executed_score_base) * 100 + -} + score_stats_table <- data.frame( + scenario = c("base", "policy"), + mean_score = c(mean(persons_joined_sf$executed_score_base),mean(persons_joined_sf$executed_score_policy)), + pct_change_mean = c(NA,pct_change_mean), + median_score = c(median(persons_joined_sf$executed_score_base),median(persons_joined_sf$executed_score_policy)), + pct_change_median = c(NA,pct_change_median), + sd_score = c(sd(persons_joined_sf$executed_score_base),sd(persons_joined_sf$executed_score_policy))) + + score_stats_table$mean_score %>% round(3) %>% print() + + write.csv(score_stats_table,file = paste0(outputDirectoryScenario,"/df.equity.score_stats.csv"), quote = FALSE, row.names = FALSE) + + + # c) Score Maps + + joined_hex <- persons_attributes_on_hex_grid(persons_joined_sf, city.shape, n = 50) %>% + dplyr::rename(score_base = executed_score_base, score_policy = executed_score_policy) + + joined_centroids <- joined_hex %>% st_centroid() + + st_write(joined_hex, paste0(outputDirectoryScenario, "/shp.equity.hex.shp"), append = FALSE) + st_write(joined_centroids, paste0(outputDirectoryScenario, "/shp.equity.centroid.shp"), append = FALSE) + + # 8.1.2. MAIN MODE + main_modes <- unique(base.trips.city$main_mode) + mode_user_cnt <- c() + mode_score_pct_change_mean <- c(length(main_modes)) + mode_score_pct_change_median <- c(length(main_modes)) + mode_score_pct_change_sd <- c(length(main_modes)) + + for(i in seq(length(main_modes))){ + + user_ids <- base.trips.city %>% + filter(main_mode == main_modes[i]) %>% + pull(person) %>% + unique() + + users_score_pct_changes <- persons_joined_sf %>% + filter(person %in% user_ids) %>% + pull(score_pct_change) + + mode_user_cnt[i] <- length(user_ids) + + mode_score_pct_change_mean[i] <- users_score_pct_changes %>% + mean(na.rm = TRUE) %>% + round(3) + + mode_score_pct_change_median[i] <- users_score_pct_changes %>% + median(na.rm = TRUE) %>% + round(3) + + mode_score_pct_change_sd[i] <- users_score_pct_changes %>% + sd(na.rm = TRUE) %>% + round(3) + + } + + mode_user_score_diff <- data.frame("Main_Mode" = main_modes, + "Count" = mode_user_cnt, + "Score_Pct_Change_Mean" = mode_score_pct_change_mean, + "Score_Pct_Change_Median" = mode_score_pct_change_median, + "Score_Pct_Change_sd" = mode_score_pct_change_sd) + + mode_user_score_diff + write.csv(mode_user_score_diff, file = paste0(outputDirectoryScenario,"/df.equity.mode.csv"), quote = FALSE, row.names = FALSE) + + # 8.1.3. SEX + sex_diff <- persons_joined_sf %>% + st_drop_geometry() %>% + group_by(sex) %>% + summarise(Count = n(), + "Mean_Percent_Change_Score" = round(mean(score_pct_change),3), + "Median_Percent_Change_Score" = round(median(score_pct_change),3), + "sd" = sd(score_pct_change)) + + write.csv(sex_diff, file = paste0(outputDirectoryScenario,"/df.equity.sex.csv"), quote = FALSE, row.names = FALSE) + + # 8.1.4. INCOME GROUP + income_diff <- persons_joined_sf %>% + st_drop_geometry() %>% + dplyr::rename("Income_Group" = "MiD.hheink_gr2") %>% + group_by(Income_Group) %>% + summarise(Count = n(), + "Mean_Percent_Change_Score" = round(mean(score_pct_change),3), + "Median_Percent_Change_Score" = round(median(score_pct_change),3), + "sd" = sd(score_pct_change)) + + income_diff + + write.csv(income_diff, file = paste0(outputDirectoryScenario,"/df.equity.income.csv"), quote = FALSE, row.names = FALSE) + + + # 8.1.5. HH Size + householdsize_diff <- persons_joined_sf %>% + st_drop_geometry() %>% + dplyr::rename("Household_Size" = "MiD.hhgr_gr") %>% + group_by(Household_Size) %>% + summarise(Count = n(), + "Mean_Percent_Change_Score" = round(mean(score_pct_change),3), + "Median_Percent_Change_Score" = round(median(score_pct_change),3), + "sd" = sd(score_pct_change)) + householdsize_diff + + write.csv(householdsize_diff, file = paste0(outputDirectoryScenario,"/df.equity.hh_size.csv"), quote = FALSE, row.names = FALSE) + + + # 8.1.6 PT ABO AVAILABLE + pt_subscription_diff <- persons_joined_sf %>% + st_drop_geometry() %>% + group_by(sim_ptAbo) %>% + summarise(Count = n(), + "Mean_Percent_Change_Score" = round(mean(score_pct_change),3), + "Median_Percent_Change_Score" = round(median(score_pct_change),3), + "sd" = sd(score_pct_change)) + + pt_subscription_diff + + write.csv(pt_subscription_diff, file = paste0(outputDirectoryScenario,"/df.equity.pt_abo.csv"), quote = FALSE, row.names = FALSE) + + # 8.1.7 CAR AVAILABLE + car_avail_diff <- persons_joined_sf %>% + st_drop_geometry() %>% + group_by(carAvail) %>% + summarise(Count = n(), + "Mean_Percent_Change_Score" = round(mean(score_pct_change),3), + "Median_Percent_Change_Score" = round(median(score_pct_change),3), + "sd" = sd(score_pct_change)) + + car_avail_diff + + write.csv(car_avail_diff, file = paste0(outputDirectoryScenario,"/df.equity.car_avail.csv"), quote = FALSE, row.names = FALSE) + +} + diff --git a/src/main/R/masterscript.R b/src/main/R/masterscript.R index 0f6e7a0e..f03addf8 100644 --- a/src/main/R/masterscript.R +++ b/src/main/R/masterscript.R @@ -1,139 +1,169 @@ #Masterscript ################################################################################ Libraries #### -library(gridExtra) -library(tidyr) library(tidyverse) -library(lubridate) -library(viridis) -library(ggsci) library(sf) -library(dplyr) -library(ggplot2) -library(matsim) -library(purrr) -library(networkD3) library(alluvial) +library(lubridate) +library(XML) +# make sure you use winnerLoserUtils branch of matsim-r until the changes are merged +# the following 2 lines are needed for winner loser analysis, which currently is under development +# hence they are commented put for now -sme0623 +#devtools::install_github("matsim-vsp/matsim-r", ref="winnerLoserUtils", force = TRUE) +# devtools::load_all("~/git/matsim-r", reset = TRUE) +library(matsim) library(ggalluvial) -library(stringr) -library(data.table) -print("#### Libraries geladen! ####") -################################################################################ CASES #### please put (1=yes/0=no) for analyses -#base-case = 0 -#carfree-area-90 = 0 -#carfree-area-95 = 0 -#carfree-area-99 = 0 -#drt-outskirts = 0 -#drt-whole-city = 0 -#slow-speed-absolute = 0 -#slow-speed-relative = 0 -#combined_scenarioA = 0 -################################################################################ INPUT #### -publicSVN = "/Users/mkreuschnervsp/Desktop/git/public-svn/matsim/scenarios/countries/de/leipzig/projects/namav/" -#local = /Users/mkreuschnervsp/Desktop/VSP_projects/02_NaMAV - -runID = "carfree-area-90" -network <- paste(publicSVN,"base-case/leipzig-25pct-base.output_network.xml.gz") -CRS <- 25832 - -scenario_run_path <- paste(publicSVN,runID) - -#base path nur für Sankey und Winner/Loser Analysis -base_run_path <- "/Users/mkreuschnervsp/Desktop/git/public-svn/matsim/scenarios/countries/de/leipzig/projects/namav/base-case/" - +library(getopt) -region_shp_path <- "/Users/mkreuschnervsp/Desktop/VSP_projects/02_NaMAV/R/shapefiles/Leipzig_puffer.shp" -city_shp_path <- "/Users/mkreuschnervsp/Desktop/VSP_projects/02_NaMAV/R/shapefiles/Leipzig_stadt.shp" -area_shp_path <- "/Users/mkreuschnervsp/Desktop/VSP_projects/02_NaMAV/R/shapefiles/Zonen90_update.shp" - - -print("#### Inputspath definiert! ####") -################################################################################ OUTPUT #### - -outputDirectoryBase <- "/Users/mkreuschnervsp/Desktop/VSP_projects/02_NaMAV/R/base-analysis-R" # the plots are going to be saved here -if(!file.exists(outputDirectoryBase)){ - print("creating analysis sub-directory") - dir.create(outputDirectoryBase) -} -#/Users/mkreuschnervsp/Desktop/git/public-svn/matsim/scenarios/countries/de/leipzig/projects/namav/",runID,"/analysis/analysis-R -outputDirectoryScenario <- "/Users/mkreuschnervsp/Desktop/VSP_projects/02_NaMAV/R/policy-analysis-R" - #paste(scenario_run_path, "analysis/analysis-R", sep = "") # the plots are going to be saved here -if(!file.exists(outputDirectoryScenario)){ - print("creating analysis sub-directory") - dir.create(outputDirectoryScenario) -} - -print("#### Output folder geladen! ####") -################################################################################ ANALYSIS #### -# PLEASE put (1=yes/0=no) for certain analysis +print("#### Libraries loaded! ####") +################################################################################ CASES #### please put (1=yes/0=no) for analyses +scenarios <- list( + #so we're comparing the base-case to the base-case? -jr May'23 + # I guess well use base-case for analyzing the base case here. All the diffplots / comparisons are rather useless then, but thats normal I guess -sme0623 + # "base-case", + "carfree-area-large" + # "carfree-area-medium" + # "carfree-area-small" + # "drt-outskirts" + # "drt-whole-city" + #,"slow-speed-absolute" + #,"slow-speed-relative" + #,"combined_scenarioA" + #,"combined_scenarioB" + #,"combined_scenarioC" + #,"combined_scenarioD" +) -#### #1.1 Modal Split COUNTS - trips based - x_ms_trips_count = 1 -#### #1.2 Modal Split DISTANCE - trips based - x_ms_trips_distance = 1 -#### #1.3 Modal Split COUNTS- legs based - x_ms_legs_count = 0 -#### #1.4 Modal Split DISTANCE - legs based - x_ms_legs_distance = 0 +################################################################################ INPUT #### -#### #2.1 Modal Shift - trips based +for (scenario in scenarios){ + + publicSVN <- "../../public-svn/matsim/scenarios/countries/de/leipzig/projects/namav/" + + runID <- paste0(scenario, "/") + + #base path nur für Sankey und Winner/Loser Analysis + base.run.path <- "../../public-svn/matsim/scenarios/countries/de/leipzig/projects/namav/base-case/" + + region.shp.path <- "../../shared-svn/projects/NaMAV/data/shapefiles/leipzig_region/Leipzig_puffer.shp" + city.shp.path <- "../../shared-svn/projects/NaMAV/data/shapefiles/leipzig_stadt/Leipzig_stadt.shp" + + # choose shp path for carfree-area-scenarios, choose carfree_area_large for all other scenarios to avoid errors + if (scenario == "carfree-area-small") { + carfree.area.shp.path <- "../../shared-svn/projects/NaMAV/data/shapefiles/leipzig_carfree_area_small/Zonen99_update.shp" + } else if (scenario == "carfree-area-medium") { + carfree.area.shp.path <- "../../shared-svn/projects/NaMAV/data/shapefiles/leipzig_carfree_area_medium/Zonen95_update.shp" + } else { + carfree.area.shp.path <- "../../shared-svn/projects/NaMAV/data/shapefiles/leipzig_carfree_area_large/Zonen90_update.shp" + } + + network <- Sys.glob(file.path(base.run.path, "*output_network.xml.gz")) + CRS <- 25832 + + scenario.run.path <- paste0(publicSVN,runID) + # if you want to run the masterscript on your mounted cluster, you have to define the scenario.run.path here + ################################################################################################################################################################################################################ + # somehow there are problem when readTripsTable() has to handle a path that is too long. I could not resolve said issue. + # you might have to re-name your dirs in order to run the masterscript + # the following example is a path which apparently is just too long + # scenario.run.path <- "Y:/net/ils/matsim-leipzig/run-drt/namav-output/runsScaledFleet3-2/drtDemandExperiments/ASC0.00837001732397158-dist0.0-travel0.0-intermodal-leipzig-flexa-25pct-scaledFlee/" + ################################################################################################################################################################################################################ + # scenario.run.path <- "Y:/net/ils/matsim-leipzig/run-drt/namav-output/runsScaledFleet3-2/drtDemandExperiments/wholeCity-210veh/" + + print("#### Input paths defined! ####") + ################################################################################ OUTPUT #### + + ifelse(endsWith(scenario.run.path, "/"),print(""),scenario.run.path <- paste0(scenario.run.path,"/")) + + outputDirectoryScenario <- paste0(scenario.run.path, "analysis/analysis-R") # the plots are going to be saved here + + if(!file.exists(paste0(scenario.run.path,"analysis"))) { + print("creating general analysis sub-directory") + dir.create(paste0(scenario.run.path,"analysis")) + } + if(!file.exists(outputDirectoryScenario)){ + print("creating analysis sub-directory") + dir.create(outputDirectoryScenario) + } + + print("#### Output folder geladen! ####") + ################################################################################ ANALYSIS #### + # PLEASE put (1=yes/0=no) for certain analysis + + #### #1.1 Modal Split COUNTS - trips based + x_ms_trips_count = 1 + #### #1.2 Modal Split DISTANCE - trips based + x_ms_trips_distance = 1 + #### #1.3 Modal Split COUNTS- legs based + x_ms_legs_count = 1 + #### #1.4 Modal Split DISTANCE - legs based + x_ms_legs_distance = 1 + + #### #2.1 Modal Shift - trips based x_sankey_diagram = 1 -#### #3.1 Distances TRAVELED - trips based - x_average_traveled_distance_trips = 1 -#### #3.2 Distances EUCLIDEAN - trips based - x_average_euclidean_distance_trips = 1 -#### #3.3 PKM - trips based - x_personen_km_trips = 0 -#### #3.4 Distances TRAVELED - legs based - x_average_traveled_distance_legs = 0 -#### #3.5 Distances EUCLIDEAN - legs based - x_average_euclidean_distance_legs = 0 -#### #3.6 PKM - legs based - x_personen_km_legs = 1 - -#### #4.1 Time Traveled - trips based - x_average_time_trips = 1 -#### #4.2 Time Traveled - legs based - x_average_time_legs = 0 -#### #4.3 ph - trips based - x_personen_h_trips = 0 -#### #4.4 ph - legs based - x_personen_h_legs = 1 - -#### #5.1 Speed TRAVELED - trips based - x_average_traveled_speed_trips = 1 -#### #5.2 Speed BEELINE - trips based - x_average_beeline_speed_trips = 1 - -#### #6.1 Traffic Volumes - #x_traffic = 0 - -#### #7.1 Emissions Analysis - #x_emissions = 0 - -#### #8.1 Winner/Loser Analysis - x_winner_loser = 1 - -#x_distance_distribution_trips = 1 -#x_distance_distribution_legs = 0 - - - - - -print("#### Auswahl getroffen! ####") -################################################################################ SOURCE #### - -source("/Users/mkreuschnervsp/Desktop/R_Studio/mastersolver.R") - -print("#### Masterscript fertig! ####") - - - - - - - - - - + #### #3.1 Distances TRAVELED - trips based + x_average_traveled_distance_trips = 1 + #### #3.2 Distances EUCLIDEAN - trips based + x_average_euclidean_distance_trips = 1 + #### #3.3 Heatmap Distances traveled - trips based + x_heatmap_distance_trips = 0 + #### #3.4 PKM - trips based + x_personen_km_trips = 1 + #### #3.5 Distances TRAVELED - legs based + x_average_traveled_distance_legs = 1 + #### #3.6 PKM - legs based + x_personen_km_legs = 1 + #### #3.7 Distances EUCLIDEAN - legs based + # not implemented, not needed though? -sme0723 + # x_average_euclidean_distance_legs = 1 + + #### #4.1 Time Traveled - trips based + x_average_time_trips = 1 + #### #4.2 Time Traveled - legs based + x_average_time_legs = 1 + #### #4.3 ph - trips based + x_personen_h_trips = 1 + #### #4.4 ph - legs based + x_personen_h_legs = 1 + #### #4.5 Time Traveled Heatmap - trips based + x_heatmap_time_trips = 0 + + #### #5.1 Speed TRAVELED - trips based + x_average_traveled_speed_trips = 1 + #### #5.2 Speed BEELINE - trips based + x_average_beeline_speed_trips = 1 + + #### #7.1 Emissions Analysis + x_emissions = 0 + + # this analysis should stay inactive as it is not finished yet -sme0623 + #### #8.1 Winner/Loser Analysis + x_winner_loser = 0 + + #### #9.1 DRT supply + x_drt_supply = 1 + + #### #9.2 DRT demand + x_drt_demand = 1 + + #### #9.3 DRT performance + x_drt_performance = 1 + + #### #9.4 DRT trip purposes + x_drt_trip_purposes = 1 + + print("#### Analysis choice succesful! ####") + print(paste0("#### Starting to analyze output for dir: ", scenario.run.path, " ####")) + ################################################################################ SOURCE #### + + source("../matsim-leipzig/src/main/R/masteranalyse.R") + + if (x_drt_supply == 1 || x_drt_demand == 1|| x_drt_performance == 1 || x_drt_trip_purposes == 1){ + + outputDirectoryScenarioDrt <- paste0(scenario.run.path, "analysis/analysis-drt/") + + source("../matsim-leipzig/src/main/R/master_drt.R") + } + + print("#### Masterscript done! ####") +} diff --git a/src/main/java/org/matsim/analysis/emissions/RunOfflineAirPollutionAnalysisByVehicleCategory.java b/src/main/java/org/matsim/analysis/emissions/RunOfflineAirPollutionAnalysisByVehicleCategory.java index 51661dbd..654aab09 100644 --- a/src/main/java/org/matsim/analysis/emissions/RunOfflineAirPollutionAnalysisByVehicleCategory.java +++ b/src/main/java/org/matsim/analysis/emissions/RunOfflineAirPollutionAnalysisByVehicleCategory.java @@ -68,14 +68,12 @@ public final class RunOfflineAirPollutionAnalysisByVehicleCategory implements MA private static final Logger log = LogManager.getLogger(RunOfflineAirPollutionAnalysisByVehicleCategory.class); private final String runDirectory; - private final String runId; private final String hbefaWarmFile; private final String hbefaColdFile; private final String analysisOutputDirectory; - public RunOfflineAirPollutionAnalysisByVehicleCategory(String runDirectory, String runId, String hbefaFileWarm, String hbefaFileCold, String analysisOutputDirectory) { + public RunOfflineAirPollutionAnalysisByVehicleCategory(String runDirectory, String hbefaFileWarm, String hbefaFileCold, String analysisOutputDirectory) { this.runDirectory = runDirectory; - this.runId = runId; this.hbefaWarmFile = hbefaFileWarm; this.hbefaColdFile = hbefaFileCold; @@ -85,40 +83,44 @@ public RunOfflineAirPollutionAnalysisByVehicleCategory(String runDirectory, Stri public static void main(String[] args) { + String runDirectory = null; + String analysisOutputDirectory = null; + String outputDir = "analysis/analysis-emissions/"; + if (args.length == 1) { - String runDirectory = args[0]; + runDirectory = args[0]; if (!runDirectory.endsWith("/")) runDirectory = runDirectory + "/"; - - // based on the simulation output available in this project - final String runId = "leipzig-25pct"; - - String hbefaFileWarm = "https://svn.vsp.tu-berlin.de/repos/public-svn/3507bb3997e5657ab9da76dbedbb13c9b5991d3e/0e73947443d68f95202b71a156b337f7f71604ae/7eff8f308633df1b8ac4d06d05180dd0c5fdf577.enc"; - String hbefaFileCold = "https://svn.vsp.tu-berlin.de/repos/public-svn/3507bb3997e5657ab9da76dbedbb13c9b5991d3e/0e73947443d68f95202b71a156b337f7f71604ae/ColdStart_Vehcat_2020_Average_withHGVetc.csv.enc"; - - RunOfflineAirPollutionAnalysisByVehicleCategory analysis = new RunOfflineAirPollutionAnalysisByVehicleCategory( - runDirectory, - runId, - hbefaFileWarm, - hbefaFileCold, - runDirectory + "emission-analysis-offline"); - try { - analysis.call(); - } catch (Exception e) { - throw new RuntimeException(e); - } - + analysisOutputDirectory = runDirectory + outputDir; + } else if ( args.length == 2) { + runDirectory = args[0]; + if (!runDirectory.endsWith("/")) runDirectory = runDirectory + "/"; + analysisOutputDirectory = args[1]; } else { throw new RuntimeException("Please set the run directory path and/or password. \nCheck the class description for more details. Aborting..."); } + + String hbefaFileWarm = "https://svn.vsp.tu-berlin.de/repos/public-svn/3507bb3997e5657ab9da76dbedbb13c9b5991d3e/0e73947443d68f95202b71a156b337f7f71604ae/7eff8f308633df1b8ac4d06d05180dd0c5fdf577.enc"; + String hbefaFileCold = "https://svn.vsp.tu-berlin.de/repos/public-svn/3507bb3997e5657ab9da76dbedbb13c9b5991d3e/0e73947443d68f95202b71a156b337f7f71604ae/r9230ru2n209r30u2fn0c9rn20n2rujkhkjhoewt84202.enc"; + + RunOfflineAirPollutionAnalysisByVehicleCategory analysis = new RunOfflineAirPollutionAnalysisByVehicleCategory( + runDirectory, + hbefaFileWarm, + hbefaFileCold, + analysisOutputDirectory); + try { + analysis.call(); + } catch (Exception e) { + throw new RuntimeException(e); + } } public Integer call() throws Exception { Config config = ConfigUtils.createConfig(); - config.vehicles().setVehiclesFile(String.valueOf(globFile(Path.of(runDirectory), runId, "output_vehicles"))); - config.network().setInputFile(String.valueOf(globFile(Path.of(runDirectory), runId, "network"))); - config.transit().setTransitScheduleFile(String.valueOf(globFile(Path.of(runDirectory), runId, "transitSchedule"))); - config.transit().setVehiclesFile(String.valueOf(globFile(Path.of(runDirectory), runId, "transitVehicles"))); + config.vehicles().setVehiclesFile(String.valueOf(globFile(Path.of(runDirectory), "*output_vehicles*"))); + config.network().setInputFile(String.valueOf(globFile(Path.of(runDirectory), "*output_network*"))); + config.transit().setTransitScheduleFile(String.valueOf(globFile(Path.of(runDirectory), "*output_transitSchedule*"))); + config.transit().setVehiclesFile(String.valueOf(globFile(Path.of(runDirectory), "*output_transitVehicles*"))); config.global().setCoordinateSystem("EPSG:25832"); log.info("Using coordinate system '{}'", config.global().getCoordinateSystem()); @@ -134,15 +136,15 @@ public Integer call() throws Exception { eConfig.setNonScenarioVehicles(NonScenarioVehicles.ignore); // input and outputs of emissions analysis - final String eventsFile = globFile(Path.of(runDirectory), runId, "output_events"); + final String eventsFile = String.valueOf(globFile(Path.of(runDirectory), "*output_events*")); File dir = new File(analysisOutputDirectory); if (!dir.exists()) { dir.mkdir(); } - final String emissionEventOutputFile = analysisOutputDirectory + runId + ".emission.events.offline.xml.gz"; + final String emissionEventOutputFile = analysisOutputDirectory + "emission.events.offline.xml.gz"; log.info("Writing emissions (link totals) to: {}", emissionEventOutputFile); // for SimWrapper - final String linkEmissionPerMOutputFile = analysisOutputDirectory + runId + ".emissionsPerLinkPerM.csv"; + final String linkEmissionPerMOutputFile = analysisOutputDirectory + "emissionsPerLinkPerM.csv"; log.info("Writing emissions per link [g/m] to: {}", linkEmissionPerMOutputFile); Scenario scenario = ScenarioUtils.loadScenario(config); diff --git a/src/main/java/org/matsim/run/LeipzigPtFareModule.java b/src/main/java/org/matsim/run/LeipzigPtFareModule.java index d3601300..e1dd603e 100644 --- a/src/main/java/org/matsim/run/LeipzigPtFareModule.java +++ b/src/main/java/org/matsim/run/LeipzigPtFareModule.java @@ -13,6 +13,13 @@ */ public class LeipzigPtFareModule extends AbstractModule { + final double minFare = 2.0; + final int longDistanceThreshold = 50000; + final double normalTripSlope = 0.00017987993018495408; + final double longTripSlope = 0.000; + final double normalBaseFare = 2.4710702921120262; + final double longBaseFare = 18.90; + @Override public void install() { // Set the money related thing in the config (planCalcScore) file to 0. @@ -31,20 +38,20 @@ public void install() { // https://www.mdv.de/site/uploads/tarifzonenplan.pdf // Minimum fare (e.g. short trip or 1 zone ticket) - distanceBasedPtFareParams.setMinFare(2.0); + distanceBasedPtFareParams.setMinFare(minFare); // Division between long trip and short trip (unit: m) - distanceBasedPtFareParams.setLongDistanceTripThreshold(50000); + distanceBasedPtFareParams.setLongDistanceTripThreshold(longDistanceThreshold); // y = ax + b --> a value, for short trips - distanceBasedPtFareParams.setNormalTripSlope(0.00017987993018495408); + distanceBasedPtFareParams.setNormalTripSlope(normalTripSlope); // y = ax + b --> b value, for short trips - distanceBasedPtFareParams.setNormalTripIntercept(2.4710702921120262); + distanceBasedPtFareParams.setNormalTripIntercept(normalBaseFare); // Base price is the daily ticket for long trips // y = ax + b --> a value, for long trips - distanceBasedPtFareParams.setLongDistanceTripSlope(0.000); + distanceBasedPtFareParams.setLongDistanceTripSlope(longTripSlope); // y = ax + b --> b value, for long trips - distanceBasedPtFareParams.setLongDistanceTripIntercept(18.90); + distanceBasedPtFareParams.setLongDistanceTripIntercept(longBaseFare); // Add bindings @@ -55,4 +62,18 @@ public void install() { addControlerListenerBinding().toInstance(ptFareUpperBoundHandler); } } + + /** + * base fare for pt ride. + */ + public Double getNormalPtBaseFare() { + return normalBaseFare; + } + + /** + * distance based fare for pt ride. + */ + public Double getNormalDistanceBasedFare() { + return normalTripSlope; + } } diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index 9a704afe..eb416493 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -1,16 +1,16 @@ package org.matsim.run; -import ch.sbb.matsim.config.SwissRailRaptorConfigGroup; -import ch.sbb.matsim.routing.pt.raptor.RaptorIntermodalAccessEgress; -import com.google.common.collect.ImmutableSet; +import ch.sbb.matsim.routing.pt.raptor.SwissRailRaptorModule; import com.google.common.collect.Sets; import com.google.inject.TypeLiteral; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.matsim.analysis.*; import org.matsim.analysis.personMoney.PersonMoneyEventsAnalysisModule; +import org.matsim.analysis.pt.stop2stop.PtStop2StopAnalysisModule; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Plan; import org.matsim.application.MATSimApplication; @@ -18,7 +18,9 @@ import org.matsim.application.analysis.noise.NoiseAnalysis; import org.matsim.application.analysis.population.SubTourAnalysis; import org.matsim.application.analysis.traffic.LinkStats; +import org.matsim.application.analysis.traffic.TrafficAnalysis; import org.matsim.application.options.SampleOptions; +import org.matsim.application.options.ShpOptions; import org.matsim.application.prepare.CreateLandUseShp; import org.matsim.application.prepare.freight.tripExtraction.ExtractRelevantFreightTrips; import org.matsim.application.prepare.network.CleanNetwork; @@ -27,15 +29,6 @@ import org.matsim.application.prepare.pt.CreateTransitScheduleFromGtfs; import org.matsim.contrib.bicycle.BicycleConfigGroup; import org.matsim.contrib.bicycle.BicycleModule; -import org.matsim.contrib.drt.fare.DrtFareParams; -import org.matsim.contrib.drt.routing.DrtRoute; -import org.matsim.contrib.drt.routing.DrtRouteFactory; -import org.matsim.contrib.drt.run.DrtConfigs; -import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup; -import org.matsim.contrib.drt.run.MultiModeDrtModule; -import org.matsim.contrib.dvrp.run.DvrpConfigGroup; -import org.matsim.contrib.dvrp.run.DvrpModule; -import org.matsim.contrib.dvrp.run.DvrpQSimComponents; import org.matsim.contrib.vsp.scenario.SnzActivities; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; @@ -51,12 +44,6 @@ import org.matsim.core.router.MultimodalLinkChooser; import org.matsim.core.router.TripStructureUtils; import org.matsim.core.scoring.functions.ScoringParametersForPerson; -import org.matsim.extensions.pt.fare.intermodalTripFareCompensator.IntermodalTripFareCompensatorConfigGroup; -import org.matsim.extensions.pt.fare.intermodalTripFareCompensator.IntermodalTripFareCompensatorsConfigGroup; -import org.matsim.extensions.pt.fare.intermodalTripFareCompensator.IntermodalTripFareCompensatorsModule; -import org.matsim.extensions.pt.routing.EnhancedRaptorIntermodalAccessEgress; -import org.matsim.extensions.pt.routing.ptRoutingModes.PtIntermodalRoutingModesConfigGroup; -import org.matsim.extensions.pt.routing.ptRoutingModes.PtIntermodalRoutingModesModule; import org.matsim.run.prepare.*; import org.matsim.simwrapper.SimWrapperConfigGroup; import org.matsim.simwrapper.SimWrapperModule; @@ -66,6 +53,7 @@ import playground.vsp.simpleParkingCostHandler.ParkingCostConfigGroup; import javax.annotation.Nullable; +import java.net.URISyntaxException; import java.util.*; /** @@ -82,7 +70,7 @@ }) @MATSimApplication.Analysis({ CheckPopulation.class, LinkStats.class, SubTourAnalysis.class, DrtServiceQualityAnalysis.class, - DrtVehiclesRoadUsageAnalysis.class, ParkedVehiclesAnalysis.class, NoiseAnalysis.class + DrtVehiclesRoadUsageAnalysis.class, ParkedVehiclesAnalysis.class, NoiseAnalysis.class, TrafficAnalysis.class }) public class RunLeipzigScenario extends MATSimApplication { @@ -109,6 +97,15 @@ public class RunLeipzigScenario extends MATSimApplication { @CommandLine.Option(names = "--parking-cost-time-period-end", defaultValue = "0", description = "End of time period for which parking cost will be charged.") private Double parkingCostTimePeriodEnd; + @CommandLine.Option(names = "--income-dependent", defaultValue = "true", description = "Income dependent scoring", negatable = true) + private boolean incomeDependent; + + @CommandLine.Option(names = "--drt-case", defaultValue = "oneServiceArea", description = "Defines how drt is modelled. For a more detailed description see class DrtCaseSetup.") + private DrtCaseSetup.DrtCase drtCase; + + @CommandLine.Option(names = "--intermodality", defaultValue = "drtAndPtSeparateFromEachOther", description = "Define if drt should be used as access and egress mode for pt.") + private DrtCaseSetup.PtDrtIntermodality ptDrtIntermodality; + public RunLeipzigScenario(@Nullable Config config) { super(config); } @@ -199,9 +196,12 @@ protected Config prepareConfig(Config config) { // but we do not know where the facilities are. (Facilities are not written to file.) if (networkOpt.hasDrtArea()) { - MultiModeDrtConfigGroup multiModeDrtConfigGroup = ConfigUtils.addOrGetModule(config, MultiModeDrtConfigGroup.class); - ConfigUtils.addOrGetModule(config, DvrpConfigGroup.class); - DrtConfigs.adjustMultiModeDrtConfig(multiModeDrtConfigGroup, config.planCalcScore(), config.plansCalcRoute()); + //drt + try { + DrtCaseSetup.prepareConfig(config, drtCase, new ShpOptions(networkOpt.getDrtArea(), null, null)); + } catch (URISyntaxException e) { + log.fatal(e); + } } config.qsim().setUsingTravelTimeCheckInTeleportation(true); @@ -253,35 +253,46 @@ protected Config prepareConfig(Config config) { } - // Need to initialize even if disabled - ConfigUtils.addOrGetModule(config, DvrpConfigGroup.class); - ConfigUtils.addOrGetModule(config, MultiModeDrtConfigGroup.class); - return config; } @Override protected void prepareScenario(Scenario scenario) { + // TODO: can be removed once v1.2 is done, because this is done in the preparation phase + for (Link link : scenario.getNetwork().getLinks().values()) { + Set modes = link.getAllowedModes(); - if (networkOpt.hasDrtArea()) { - scenario.getPopulation().getFactory().getRouteFactories().setRouteFactory(DrtRoute.class, new DrtRouteFactory()); - // (matsim core does not know about DRT routes. This makes it possible to read them before the controler is there.) + // allow freight traffic together with cars + if (modes.contains("car")) { + Set newModes = Sets.newHashSet(modes); + newModes.add("freight"); + + link.setAllowedModes(newModes); + } } + //this has to be executed before DrtCaseSetup.prepareScenario() as the latter method relies on the drt mode being added to the network networkOpt.prepare(scenario.getNetwork()); // (passt das Netz an aus den mitgegebenen shape files, z.B. parking area, car-free area, ...) + + if (networkOpt.hasDrtArea()) { + DrtCaseSetup.prepareScenario(scenario, drtCase, new ShpOptions(networkOpt.getDrtArea(), null, null), VERSION); + } + + } @Override protected void prepareControler(Controler controler) { - Config config = controler.getConfig(); controler.addOverridingModule(new SimWrapperModule()); + controler.addOverridingModule(new PtStop2StopAnalysisModule()); controler.addOverridingModule(new AbstractModule() { @Override public void install() { install(new LeipzigPtFareModule()); + install(new SwissRailRaptorModule()); addTravelTimeBinding(TransportMode.ride).to(networkTravelTime()); addTravelDisutilityFactoryBinding(TransportMode.ride).to(carTravelDisutilityFactoryKey()); @@ -318,34 +329,7 @@ public void install() { }); if (networkOpt.hasDrtArea()) { - // TODO yyyy move above into prepareConfig - // TODO will be integrated into DrtCaseSetup class - - MultiModeDrtConfigGroup multiModeDrtConfigGroup = ConfigUtils.addOrGetModule(config, MultiModeDrtConfigGroup.class); - - //set fare params; flexa has the same prices as leipzig PT: Values taken out of LeipzigPtFareModule -sm0522 - Double ptBaseFare = 2.4710702921120262; - Double ptDistanceFare = 0.00017987993018495408; - - DrtFareParams drtFareParams = new DrtFareParams(); - drtFareParams.baseFare = ptBaseFare; - drtFareParams.distanceFare_m = ptDistanceFare; - drtFareParams.timeFare_h = 0.; - drtFareParams.dailySubscriptionFee = 0.; - - Set drtModes = new HashSet<>(); - - multiModeDrtConfigGroup.getModalElements().forEach(drtConfigGroup -> { - drtConfigGroup.addParameterSet(drtFareParams); - DrtConfigs.adjustDrtConfig(drtConfigGroup, config.planCalcScore(), config.plansCalcRoute()); - drtModes.add(drtConfigGroup.getMode()); - }); - - controler.addOverridingModule(new DvrpModule()); - controler.addOverridingModule(new MultiModeDrtModule()); - controler.configureQSimComponents(DvrpQSimComponents.activateAllModes(multiModeDrtConfigGroup)); - - prepareDrtFareCompensation(config, controler, drtModes, ptBaseFare); + DrtCaseSetup.prepareControler(controler, drtCase, new ShpOptions(networkOpt.getDrtArea(), null, null), ptDrtIntermodality); } if (bike == BicycleHandling.onNetworkWithBicycleContrib) { @@ -353,69 +337,8 @@ public void install() { } } - /** - * TODO: will be moved into separate class. - */ - private void prepareDrtFareCompensation(Config config, Controler controler, Set nonPtModes, Double ptBaseFare) { - IntermodalTripFareCompensatorsConfigGroup intermodalTripFareCompensatorsConfigGroup = - ConfigUtils.addOrGetModule(config, IntermodalTripFareCompensatorsConfigGroup.class); - - IntermodalTripFareCompensatorConfigGroup drtFareCompensator = new IntermodalTripFareCompensatorConfigGroup(); - drtFareCompensator.setCompensationCondition(IntermodalTripFareCompensatorConfigGroup.CompensationCondition.PtModeUsedAnywhereInTheDay); - - //Flexa is integrated into pt system, so users only pay once - drtFareCompensator.setCompensationMoneyPerTrip(ptBaseFare); - drtFareCompensator.setNonPtModes(ImmutableSet.copyOf(nonPtModes)); - - intermodalTripFareCompensatorsConfigGroup.addParameterSet(drtFareCompensator); - controler.addOverridingModule(new IntermodalTripFareCompensatorsModule()); - - //for intermodality between pt and drt the following modules have to be installed and configured - String artificialPtMode = "pt_w_drt_allowed"; - PtIntermodalRoutingModesConfigGroup ptIntermodalRoutingModesConfig = ConfigUtils.addOrGetModule(config, PtIntermodalRoutingModesConfigGroup.class); - PtIntermodalRoutingModesConfigGroup.PtIntermodalRoutingModeParameterSet ptIntermodalRoutingModesParamSet - = new PtIntermodalRoutingModesConfigGroup.PtIntermodalRoutingModeParameterSet(); - - ptIntermodalRoutingModesParamSet.setDelegateMode(TransportMode.pt); - ptIntermodalRoutingModesParamSet.setRoutingMode(artificialPtMode); - - PtIntermodalRoutingModesConfigGroup.PersonAttribute2ValuePair personAttrParamSet - = new PtIntermodalRoutingModesConfigGroup.PersonAttribute2ValuePair(); - personAttrParamSet.setPersonFilterAttribute("canUseDrt"); - personAttrParamSet.setPersonFilterValue("true"); - ptIntermodalRoutingModesParamSet.addPersonAttribute2ValuePair(personAttrParamSet); - - ptIntermodalRoutingModesConfig.addParameterSet(ptIntermodalRoutingModesParamSet); - - controler.addOverridingModule(new PtIntermodalRoutingModesModule()); - - //SRRConfigGroup needs to have the same personFilterAttr and Value as PtIntermodalRoutingModesConfigGroup - SwissRailRaptorConfigGroup ptConfig = ConfigUtils.addOrGetModule(config, SwissRailRaptorConfigGroup.class); - for (SwissRailRaptorConfigGroup.IntermodalAccessEgressParameterSet paramSet : ptConfig.getIntermodalAccessEgressParameterSets()) { - if (paramSet.getMode().contains("drt")) { - paramSet.setPersonFilterAttribute("canUseDrt"); - paramSet.setPersonFilterValue("true"); - } - } - - controler.addOverridingModule(new AbstractModule() { - @Override - public void install() { - bind(RaptorIntermodalAccessEgress.class).to(EnhancedRaptorIntermodalAccessEgress.class); - } - }); - - //finally the new pt mode has to be added to subtourModeChoice - SubtourModeChoiceConfigGroup modeChoiceConfigGroup = ConfigUtils.addOrGetModule(config, SubtourModeChoiceConfigGroup.class); - List modes = new ArrayList<>(); - Collections.addAll(modes, modeChoiceConfigGroup.getModes()); - modes.add(artificialPtMode); - modeChoiceConfigGroup.setModes(modes.toArray(new String[0])); - } - /** * Defines how bicycles are scored. */ enum BicycleHandling {onNetworkWithStandardMatsim, onNetworkWithBicycleContrib} - } diff --git a/src/main/java/org/matsim/run/prepare/CreateDrtStopsFromNetwork.java b/src/main/java/org/matsim/run/prepare/CreateDrtStopsFromNetwork.java index 5f946aea..d1e747c6 100644 --- a/src/main/java/org/matsim/run/prepare/CreateDrtStopsFromNetwork.java +++ b/src/main/java/org/matsim/run/prepare/CreateDrtStopsFromNetwork.java @@ -48,8 +48,8 @@ public final class CreateDrtStopsFromNetwork implements MATSimAppCommand { @CommandLine.Option(names = "--min-distance", description = "minimal distance between two stops in m", defaultValue = "100.") private double minDistance; - @CommandLine.Option(names = "--output-folder", description = "path to output folder", required = true) - private String outputFolder; + @CommandLine.Option(names = "--output", description = "output file name", required = true) + private String outputFile; private static final Logger log = LogManager.getLogger(CreateDrtStopsFromNetwork.class); @@ -66,7 +66,6 @@ public Integer call() throws Exception { String stopsData = shp.getShapeFile().toString() + "_" + mode + "_stops.csv"; Geometry drtServiceArea = null; - Map, Node> stopNodes = new HashMap<>(); if (shp.getShapeFile() != null) { drtServiceArea = shp.getGeometry(); @@ -75,6 +74,24 @@ public Integer call() throws Exception { return 2; } + processNetworkForStopCreation(network, modeFilteredNetwork, drtServiceArea, stopsData, mode, outputFile, shp); + + return 0; + } + + /** + * method, which is called by @DrtCaseSetup for drt config / input automation. + * @param network input network, which is used to create stops. + * @param modeFilteredNetwork use mode filtered network yes / no. + * @param drtServiceArea shp with drt service area. + * @param stopsData csv file with stop coordinates for comparison. + * @param mode drt mode, for which stops are created. + * @param outputFile output stops file.xml. + */ + public void processNetworkForStopCreation(Network network, boolean modeFilteredNetwork, Geometry drtServiceArea, String stopsData, String mode, String outputFile, ShpOptions shp) { + + Map, Node> stopNodes = new HashMap<>(); + if (modeFilteredNetwork) { Network filteredNetwork = NetworkUtils.createNetwork(); Set modes = new HashSet<>(); @@ -125,16 +142,17 @@ public Integer call() throws Exception { csvWriter.append(";"); csvWriter.append(Double.toString(filteredNodes.get(nodeId).getCoord().getY())); } + } catch (IOException e) { + log.fatal(e); } MATSimAppCommand prepareDrtStops = new PrepareDrtStops(); - String outputNet = outputFolder + "/" + mode + "networkForDrtStopCreation.xml.gz"; + String outputNet = "./" + mode + "networkForDrtStopCreation.xml.gz"; NetworkUtils.writeNetwork(network, outputNet); prepareDrtStops.execute("--stops-data", stopsData, "--network", outputNet, "--mode", mode, - "--shp", shp.getShapeFile().toString(), "--output-folder", outputFolder); + "--shp", shp.getShapeFile().toString(), "--output", outputFile); - return 0; } Map, Node> filterDistance(Double minDistance, Map, Node> nodes) { diff --git a/src/main/java/org/matsim/run/prepare/DrtCaseSetup.java b/src/main/java/org/matsim/run/prepare/DrtCaseSetup.java new file mode 100644 index 00000000..e0118129 --- /dev/null +++ b/src/main/java/org/matsim/run/prepare/DrtCaseSetup.java @@ -0,0 +1,442 @@ +package org.matsim.run.prepare; + +import ch.sbb.matsim.config.SwissRailRaptorConfigGroup; +import ch.sbb.matsim.routing.pt.raptor.RaptorIntermodalAccessEgress; +import com.google.common.collect.ImmutableSet; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.locationtech.jts.geom.Geometry; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.application.options.ShpOptions; +import org.matsim.contrib.drt.analysis.zonal.DrtZonalSystemParams; +import org.matsim.contrib.drt.fare.DrtFareParams; +import org.matsim.contrib.drt.optimizer.insertion.extensive.ExtensiveInsertionSearchParams; +import org.matsim.contrib.drt.optimizer.rebalancing.RebalancingParams; +import org.matsim.contrib.drt.optimizer.rebalancing.mincostflow.MinCostFlowRebalancingStrategyParams; +import org.matsim.contrib.drt.routing.DrtRoute; +import org.matsim.contrib.drt.routing.DrtRouteFactory; +import org.matsim.contrib.drt.run.*; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; +import org.matsim.contrib.dvrp.run.DvrpConfigGroup; +import org.matsim.contrib.dvrp.run.DvrpModule; +import org.matsim.contrib.dvrp.run.DvrpQSimComponents; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.config.groups.ChangeModeConfigGroup; +import org.matsim.core.config.groups.PlanCalcScoreConfigGroup; +import org.matsim.core.config.groups.SubtourModeChoiceConfigGroup; +import org.matsim.core.controler.AbstractModule; +import org.matsim.core.controler.Controler; +import org.matsim.core.controler.OutputDirectoryHierarchy; +import org.matsim.core.controler.events.StartupEvent; +import org.matsim.core.controler.listener.StartupListener; +import org.matsim.core.utils.io.IOUtils; +import org.matsim.extensions.pt.fare.intermodalTripFareCompensator.IntermodalTripFareCompensatorConfigGroup; +import org.matsim.extensions.pt.fare.intermodalTripFareCompensator.IntermodalTripFareCompensatorsConfigGroup; +import org.matsim.extensions.pt.fare.intermodalTripFareCompensator.IntermodalTripFareCompensatorsModule; +import org.matsim.extensions.pt.routing.EnhancedRaptorIntermodalAccessEgress; +import org.matsim.extensions.pt.routing.ptRoutingModes.PtIntermodalRoutingModesConfigGroup; +import org.matsim.extensions.pt.routing.ptRoutingModes.PtIntermodalRoutingModesModule; +import org.matsim.run.LeipzigPtFareModule; +import org.opengis.feature.simple.SimpleFeature; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +/** + * This class configures drt config, stops, transitStopsFile and vehicles regarding enum DrtCase of RunLeipzigScenario. + */ +public final class DrtCaseSetup { + + private static final Logger log = LogManager.getLogger(DrtCaseSetup.class); + private static final ShpOptions flexaArea2021 = new ShpOptions(Path.of( + "input/v1.2/drtServiceArea/leipzig_flexa_service_area_2021.shp"), + null, null); + + private static String errorMessage = "Unexpected value: "; + + static Set drtModes = new HashSet<>(); + + /** + * Defines if drt is modelled at all (none), with 2 separate modes (twoSeparateServiceAreas) or with 1 single drt mode (oneServiceArea). + * As this class is only triggered if a shp of the drt service area was provided, none is inactive for now + */ + public enum DrtCase {/*none,*/ twoSeparateServiceAreas, oneServiceArea} + + /** + * Defines if intermodality between drt and pt is modelled or not. + */ + public enum PtDrtIntermodality {drtAndPtSeparateFromEachOther, drtAsAccessEgressForPt} + + private DrtCaseSetup(){} + + /** + * prepare config for drt simulation. + */ + public static void prepareConfig(Config config, DrtCase drtCase, ShpOptions drtArea) throws URISyntaxException { + + MultiModeDrtConfigGroup multiModeDrtConfigGroup = ConfigUtils.addOrGetModule(config, MultiModeDrtConfigGroup.class); + DvrpConfigGroup dvrpConfigGroup = ConfigUtils.addOrGetModule(config, DvrpConfigGroup.class); + + LeipzigPtFareModule ptFareModule = new LeipzigPtFareModule(); + + //set fare params; flexa has the same prices as leipzig PT: Values taken out of LeipzigPtFareModule -sm0522 + Double ptBaseFare = ptFareModule.getNormalPtBaseFare(); + Double ptDistanceFare = ptFareModule.getNormalDistanceBasedFare(); + + DrtFareParams drtFareParams = new DrtFareParams(); + drtFareParams.baseFare = ptBaseFare; + drtFareParams.distanceFare_m = ptDistanceFare; + drtFareParams.timeFare_h = 0.; + drtFareParams.dailySubscriptionFee = 0.; + + switch (drtCase) { + case twoSeparateServiceAreas -> { + //flexa case with 2 separate drt bubbles (north and southeast) -> 2 separate drt modes + if (multiModeDrtConfigGroup.getModalElements().isEmpty()) { + createDrtModeConfigGroup(multiModeDrtConfigGroup, TransportMode.drt + "North", drtArea.getShapeFile().toString()); + createDrtModeConfigGroup(multiModeDrtConfigGroup, TransportMode.drt + "Southeast", drtArea.getShapeFile().toString()); + } + + multiModeDrtConfigGroup.getModalElements().forEach(drtConfigGroup -> { + drtConfigGroup.addParameterSet(drtFareParams); + DrtConfigs.adjustDrtConfig(drtConfigGroup, config.planCalcScore(), config.plansCalcRoute()); + drtModes.add(drtConfigGroup.getMode()); + + configureNecessaryConfigGroups(config, drtConfigGroup.getMode()); + }); + } + + case oneServiceArea -> { + //"normal" drt, modelled as one single drt mode + if (multiModeDrtConfigGroup.getModalElements().isEmpty()) { + createDrtModeConfigGroup(multiModeDrtConfigGroup, TransportMode.drt, drtArea.getShapeFile().toString()); + } + + multiModeDrtConfigGroup.getModalElements().forEach(drtConfigGroup -> { + drtConfigGroup.addParameterSet(drtFareParams); + DrtConfigs.adjustDrtConfig(drtConfigGroup, config.planCalcScore(), config.plansCalcRoute()); + drtModes.add(drtConfigGroup.getMode()); + + configureNecessaryConfigGroups(config, drtConfigGroup.getMode()); + }); + } + default -> throw new IllegalStateException(errorMessage + (drtCase)); + } + + //drt modes have to be set as network modes in dvrp CfgGroup + dvrpConfigGroup.networkModes = drtModes; + //after adding mode specific multiModeDrtParams -> adjust + } + + /** + * prepare scenario for drt simulation. + */ + public static void prepareScenario(Scenario scenario, DrtCase drtCase, ShpOptions drtArea, String version) { + + scenario.getPopulation().getFactory().getRouteFactories().setRouteFactory(DrtRoute.class, new DrtRouteFactory()); + // (matsim core does not know about DRT routes. This makes it possible to read them before the controler is there.) + + String drtMode = null; + Integer noVehicles = null; + + CreateDrtStopsFromNetwork drtStopsCreator = new CreateDrtStopsFromNetwork(); + MultiModeDrtConfigGroup multiModeDrtConfigGroup = ConfigUtils.addOrGetModule(scenario.getConfig(), MultiModeDrtConfigGroup.class); + + switch (drtCase) { + case twoSeparateServiceAreas -> { + //flexa case with 2 separate drt bubbles (north and southeast) -> 2 separate drt modes + + for (SimpleFeature feature : flexaArea2021.readFeatures()) { + if (feature.getAttribute("Name").equals("Nord")) { + drtMode = "drtNorth"; + noVehicles = 3; + + } else if (feature.getAttribute("Name").equals("Suedost")) { + drtMode = "drtSoutheast"; + noVehicles = 2; + } else { + log.fatal("Invalid shp feature name. Shp features must be named 'Nord' or 'Suedost'!"); + } + + new LeipzigDrtVehicleCreator().createDrtVehiclesForSingleArea(scenario.getVehicles(), scenario.getNetwork(), + feature, noVehicles, drtMode); + + //normally the following code would be set in prepareConfig, but.. + //.. the stops creator relies on a network with drt modes. Drt modes are added in RunLeipzigScenario.prepareScenario().. + //.. so stops are created after that step -sme0823 + multiModeDrtConfigGroup.getModalElements().forEach(drtConfigGroup -> { + + //path, tho which stops.xml is saved + URL path = IOUtils.extendUrl(scenario.getConfig().getContext(), "leipzig-v" + version + "-" + drtConfigGroup.getMode() + "-stops.xml"); + File stopsFile = null; + try { + stopsFile = new File(path.toURI()); + } catch (URISyntaxException e) { + log.fatal(e); + } + + //create drt stops and save them next to config -> put it as input stops file. + //unfortunately there is no scenario.setDrtStops, so we have to do this workaround. -sme0723 + drtStopsCreator.processNetworkForStopCreation(scenario.getNetwork(), true, (Geometry) feature.getDefaultGeometry(), + flexaArea2021.getShapeFile().toString() + "_" + drtConfigGroup.getMode() + "_stops.csv", drtConfigGroup.getMode(), + stopsFile.toString(), flexaArea2021); + + //naming pattern comes from @DrtStopsWriter line 81. Should be ok to hard code it here. -sme0523 + drtConfigGroup.transitStopFile = stopsFile.toString(); + + configureNecessaryConfigGroups(scenario.getConfig(), drtConfigGroup.getMode()); + }); + } + } + + case oneServiceArea -> { + //"normal" drt, modelled as one single drt mode + drtMode = TransportMode.drt; + + //make the 400 configurable??? -sme0723 + new LeipzigDrtVehicleCreator().createDrtVehicles(scenario.getVehicles(), scenario.getNetwork(), + drtArea, 400, drtMode); + + //normally the following code would be set in prepareConfig, but.. + //.. the stops creator relies on a network with drt modes. Drt modes are added in RunLeipzigScenario.prepareScenario().. + //.. so stops are created after that step -sme0823 + multiModeDrtConfigGroup.getModalElements().forEach(drtConfigGroup -> { + + //path, tho which stops.xml is saved + URL path = IOUtils.extendUrl(scenario.getConfig().getContext(), "leipzig-v" + version + "-" + drtConfigGroup.getMode() + "-stops.xml"); + File stopsFile = null; + try { + stopsFile = new File(path.toURI()); + } catch (URISyntaxException e) { + log.fatal(e); + } + + //create drt stops and save them next to config -> put it as input stops file. + //unfortunately there is no scenario.setDrtStops, so we have to do this workaround. -sme0723 + drtStopsCreator.processNetworkForStopCreation(scenario.getNetwork(), true, drtArea.getGeometry(), + drtArea.getShapeFile().toString() + "_" + drtConfigGroup.getMode() + "_stops.csv", drtConfigGroup.getMode(), + stopsFile.toString(), drtArea); + + //naming pattern comes from @DrtStopsWriter line 81. Should be ok to hard code it here. -sme0523 + drtConfigGroup.transitStopFile = stopsFile.toString(); + + }); + + } + default -> throw new IllegalStateException(errorMessage + (drtCase)); + } + } + + /** + * prepare controler for drt simulation. + */ + public static void prepareControler(Controler controler, DrtCase drtCase, ShpOptions drtArea, PtDrtIntermodality ptDrtIntermodality) { + + MultiModeDrtConfigGroup multiModeDrtConfigGroup = ConfigUtils.addOrGetModule(controler.getConfig(), MultiModeDrtConfigGroup.class); + controler.addOverridingModule(new DvrpModule()); + controler.addOverridingModule(new MultiModeDrtModule()); + controler.configureQSimComponents(DvrpQSimComponents.activateAllModes(multiModeDrtConfigGroup)); + + // if drt is stopBased, we want to write the drt stops into the global output -sme0723 + for (DrtConfigGroup drtCfg : multiModeDrtConfigGroup.getModalElements()) { + if (drtCfg.operationalScheme.equals(DrtConfigGroup.OperationalScheme.stopbased)) { + controler.addOverridingModule(new AbstractDvrpModeModule(drtCfg.getMode()) { + @Override + public void install() { + bindModal(DrtCaseSetup.StopsControlerListener.class).toProvider(modalProvider( + getter -> new DrtCaseSetup.StopsControlerListener(drtCfg.getMode(), + getter.get(OutputDirectoryHierarchy.class), drtCfg.transitStopFile + ))); + addControlerListenerBinding().to(modalKey(DrtCaseSetup.StopsControlerListener.class)); + } + }); + } + } + + switch (drtCase) { + case twoSeparateServiceAreas -> { + //flexa case with 2 separate drt bubbles (north and southeast) -> 2 separate drt modes + + //if intermodality between pt and drt -> only railways are tagged as intermodal stations (this is how it is handled in reality) -sme0723 + if (ptDrtIntermodality.equals(PtDrtIntermodality.drtAsAccessEgressForPt)) { + preparePtDrtIntermodality(controler, flexaArea2021, true); + } + } + + case oneServiceArea -> { + //"normal" drt, modelled as one single drt mode + + if (ptDrtIntermodality.equals(PtDrtIntermodality.drtAsAccessEgressForPt)) { + preparePtDrtIntermodality(controler, drtArea, false); + } + } + default -> throw new IllegalStateException(errorMessage + (drtCase)); + } + } + + /** + * if no modal params existing, we have to create them. + */ + private static void createDrtModeConfigGroup(MultiModeDrtConfigGroup multiModeDrtConfigGroup, String mode, String pathToShp) { + DrtConfigGroup drtConfigGroup = new DrtConfigGroup(); + drtConfigGroup.mode = mode; + drtConfigGroup.operationalScheme = DrtConfigGroup.OperationalScheme.stopbased; + drtConfigGroup.maxTravelTimeAlpha = 1.5; + drtConfigGroup.maxTravelTimeBeta = 1200.; + drtConfigGroup.maxWaitTime = 1200.; + drtConfigGroup.maxWalkDistance = 1500.; + drtConfigGroup.rejectRequestIfMaxWaitOrTravelTimeViolated = false; + drtConfigGroup.stopDuration = 60.; + drtConfigGroup.useModeFilteredSubnetwork = true; + + //add insertionSearch params + ExtensiveInsertionSearchParams insertionSearchParams = new ExtensiveInsertionSearchParams(); + drtConfigGroup.addParameterSet(insertionSearchParams); + + //add rebalancing params and configure standard values + RebalancingParams rebalancingParams = new RebalancingParams(); + + MinCostFlowRebalancingStrategyParams rebalancingStrategyParams = new MinCostFlowRebalancingStrategyParams(); + rebalancingStrategyParams.targetAlpha = 0.5; + rebalancingStrategyParams.targetBeta = 0.5; + rebalancingParams.addParameterSet(rebalancingStrategyParams); + drtConfigGroup.addParameterSet(rebalancingParams); + + DrtZonalSystemParams zonalSystemParams = new DrtZonalSystemParams(); + zonalSystemParams.zonesGeneration = DrtZonalSystemParams.ZoneGeneration.ShapeFile; + zonalSystemParams.zonesShapeFile = new File(pathToShp).getAbsolutePath(); + drtConfigGroup.addParameterSet(zonalSystemParams); + + multiModeDrtConfigGroup.addParameterSet(drtConfigGroup); + } + + /** + * configure all other config groups, where drt modes need to be included: SMC, ChangeMode, ModeParams. + */ + private static void configureNecessaryConfigGroups(Config config, String mode) { + + PlanCalcScoreConfigGroup planCalcScoreConfigGroup = ConfigUtils.addOrGetModule(config, PlanCalcScoreConfigGroup.class); + ChangeModeConfigGroup changeModeConfigGroup = ConfigUtils.addOrGetModule(config, ChangeModeConfigGroup.class); + SubtourModeChoiceConfigGroup smcCfg = ConfigUtils.addOrGetModule(config, SubtourModeChoiceConfigGroup.class); + + //add drt mode to modeParams if it does not exist yet + if (!planCalcScoreConfigGroup.getModes().containsKey(mode)) { + PlanCalcScoreConfigGroup.ModeParams modeParams = new PlanCalcScoreConfigGroup.ModeParams(mode); + modeParams.setConstant(planCalcScoreConfigGroup.getModes().get(TransportMode.pt).getConstant()); + modeParams.setMarginalUtilityOfTraveling(0.); + + PlanCalcScoreConfigGroup.ScoringParameterSet scoringParams = planCalcScoreConfigGroup.getOrCreateScoringParameters(null); + scoringParams.addModeParams(modeParams); + } + + //add drt modes to changeModeCfgGroup + if (!Arrays.stream(changeModeConfigGroup.getModes()).toList().contains(mode)) { + List modes = new ArrayList<>(Arrays.asList(changeModeConfigGroup.getModes())); + modes.add(mode); + changeModeConfigGroup.setModes(modes.toArray(new String[modes.size()])); + } + + //add drt modes to SMC + if (!Arrays.stream(smcCfg.getModes()).toList().contains(mode)) { + List modes = new ArrayList<>(Arrays.asList(smcCfg.getModes())); + modes.add(mode); + smcCfg.setModes(modes.toArray(new String[modes.size()])); + } + } + + private static void preparePtDrtIntermodality(Controler controler, ShpOptions shp, boolean railwaysOnly) { + + new PrepareTransitSchedule().prepareDrtIntermodality(controler.getScenario().getTransitSchedule(), shp, railwaysOnly); + + ConfigUtils.addOrGetModule(controler.getConfig(), MultiModeDrtConfigGroup.class).getModalElements().stream().findFirst().ifPresent(drtConfigGroup -> + drtConfigGroup.getDrtFareParams().ifPresent(drtFareParams -> + prepareDrtFareCompensation(controler, drtModes, drtFareParams.baseFare))); + } + + private static void prepareDrtFareCompensation(Controler controler, Set nonPtModes, Double ptBaseFare) { + IntermodalTripFareCompensatorsConfigGroup intermodalTripFareCompensatorsConfigGroup = + ConfigUtils.addOrGetModule(controler.getConfig(), IntermodalTripFareCompensatorsConfigGroup.class); + + IntermodalTripFareCompensatorConfigGroup drtFareCompensator = new IntermodalTripFareCompensatorConfigGroup(); + drtFareCompensator.setCompensationCondition(IntermodalTripFareCompensatorConfigGroup.CompensationCondition.PtModeUsedAnywhereInTheDay); + + //Flexa is integrated into pt system, so users only pay once + drtFareCompensator.setCompensationMoneyPerTrip(ptBaseFare); + drtFareCompensator.setNonPtModes(ImmutableSet.copyOf(nonPtModes)); + + intermodalTripFareCompensatorsConfigGroup.addParameterSet(drtFareCompensator); + controler.addOverridingModule(new IntermodalTripFareCompensatorsModule()); + + //for intermodality between pt and drt the following modules have to be installed and configured + String artificialPtMode = "pt_w_drt_allowed"; + PtIntermodalRoutingModesConfigGroup ptIntermodalRoutingModesConfig = ConfigUtils.addOrGetModule(controler.getConfig(), PtIntermodalRoutingModesConfigGroup.class); + PtIntermodalRoutingModesConfigGroup.PtIntermodalRoutingModeParameterSet ptIntermodalRoutingModesParamSet + = new PtIntermodalRoutingModesConfigGroup.PtIntermodalRoutingModeParameterSet(); + + ptIntermodalRoutingModesParamSet.setDelegateMode(TransportMode.pt); + ptIntermodalRoutingModesParamSet.setRoutingMode(artificialPtMode); + + PtIntermodalRoutingModesConfigGroup.PersonAttribute2ValuePair personAttrParamSet + = new PtIntermodalRoutingModesConfigGroup.PersonAttribute2ValuePair(); + personAttrParamSet.setPersonFilterAttribute("canUseDrt"); + personAttrParamSet.setPersonFilterValue("true"); + ptIntermodalRoutingModesParamSet.addPersonAttribute2ValuePair(personAttrParamSet); + + ptIntermodalRoutingModesConfig.addParameterSet(ptIntermodalRoutingModesParamSet); + + controler.addOverridingModule(new PtIntermodalRoutingModesModule()); + + //SRRConfigGroup needs to have the same personFilterAttr and Value as PtIntermodalRoutingModesConfigGroup + SwissRailRaptorConfigGroup ptConfig = ConfigUtils.addOrGetModule(controler.getConfig(), SwissRailRaptorConfigGroup.class); + ptConfig.setUseIntermodalAccessEgress(true); + + for (String drtMode : nonPtModes) { + SwissRailRaptorConfigGroup.IntermodalAccessEgressParameterSet intermodalParamSet = new SwissRailRaptorConfigGroup.IntermodalAccessEgressParameterSet(); + intermodalParamSet.setMode(drtMode); + ptConfig.addParameterSet(intermodalParamSet); + intermodalParamSet.setPersonFilterAttribute("canUseDrt"); + intermodalParamSet.setPersonFilterValue("true"); + intermodalParamSet.setInitialSearchRadius(10000.); + intermodalParamSet.setMaxRadius(10000.); + intermodalParamSet.setSearchExtensionRadius(1000.); + intermodalParamSet.setStopFilterAttribute("allowDrtAccessEgress"); + } + + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { + bind(RaptorIntermodalAccessEgress.class).to(EnhancedRaptorIntermodalAccessEgress.class); + } + }); + + //finally the new pt mode has to be added to subtourModeChoice + SubtourModeChoiceConfigGroup modeChoiceConfigGroup = ConfigUtils.addOrGetModule(controler.getConfig(), SubtourModeChoiceConfigGroup.class); + List modes = new ArrayList<>(); + Collections.addAll(modes, modeChoiceConfigGroup.getModes()); + modes.add(artificialPtMode); + modeChoiceConfigGroup.setModes(modes.toArray(new String[0])); + } + + private record StopsControlerListener(String mode, + OutputDirectoryHierarchy controlerIO, + String stopsFile) implements StartupListener { + + private static final String OUTPUT_FILE_NAME = "stops.xml"; + + @Override + public void notifyStartup(StartupEvent event) { + try { + Files.copy(Path.of(stopsFile), Path.of(controlerIO.getOutputFilename(mode + "_" + OUTPUT_FILE_NAME))); + } catch (IOException e) { + log.fatal(e); + } + } + } +} diff --git a/src/main/java/org/matsim/run/prepare/DrtStopsWriter.java b/src/main/java/org/matsim/run/prepare/DrtStopsWriter.java index 2ca310c0..21c33a5a 100644 --- a/src/main/java/org/matsim/run/prepare/DrtStopsWriter.java +++ b/src/main/java/org/matsim/run/prepare/DrtStopsWriter.java @@ -32,15 +32,16 @@ public final class DrtStopsWriter extends MatsimXmlWriter { private final String mode; private Geometry serviceArea = null; - private final String outputFolder; private final Network network; private final String stopsData; + private final String stopsFileName; - DrtStopsWriter(String stopsData, Network network, String mode, ShpOptions shp, String outputFolder) { + DrtStopsWriter(String stopsData, Network network, String mode, ShpOptions shp, String outputFile) { this.network = network; this.mode = mode; - this.outputFolder = outputFolder; this.stopsData = stopsData; + this.stopsFileName = outputFile; + //If you just say serviceArea = shp.getGeometry() instead of looping through features //somehow the first feature only is taken -sm0222 List features = shp.readFeatures(); @@ -56,7 +57,7 @@ public final class DrtStopsWriter extends MatsimXmlWriter { } void write() throws UncheckedIOException, IOException { - this.openFile(outputFolder + "/leipzig-v1.1-" + mode + "-stops.xml"); + this.openFile(this.stopsFileName); this.writeXmlHead(); this.writeDoctype("transitSchedule", "http://www.matsim.org/files/dtd/transitSchedule_v1.dtd"); this.writeStartTag("transitSchedule", null); @@ -69,51 +70,50 @@ void write() throws UncheckedIOException, IOException { private void writeTransitStops(Network network) throws IOException { // Write csv file for adjusted stop location - FileWriter csvWriter = new FileWriter(outputFolder + "/leipzig-v1.1-" - + mode + "-stops-locations.csv"); - csvWriter.append("Stop ID"); - csvWriter.append(","); - csvWriter.append("Link ID"); - csvWriter.append(","); - csvWriter.append("X"); - csvWriter.append(","); - csvWriter.append("Y"); - csvWriter.append("\n"); - - // Read original data csv - log.info("Start processing the network. This may take some time..."); - - BufferedReader csvReader = new BufferedReader(new FileReader(stopsData)); - csvReader.readLine(); - while (true) { - String stopEntry = csvReader.readLine(); - if (stopEntry == null) { - break; - } - String[] stopData = stopEntry.split(";"); - // write stop - Coord coord = new Coord(Double.parseDouble(stopData[2]), Double.parseDouble(stopData[3])); - - if (serviceArea == null || MGC.coord2Point(coord).within(serviceArea)) { - List> attributes = new ArrayList>(5); - attributes.add(createTuple("id", stopData[0])); - attributes.add(createTuple("x", stopData[2])); - attributes.add(createTuple("y", stopData[3])); - Link link = getStopLink(coord, network); - attributes.add(createTuple("linkRefId", link.getId().toString())); - this.writeStartTag("stopFacility", attributes, true); - - csvWriter.append(stopData[0]); - csvWriter.append(","); - csvWriter.append(link.getId().toString()); - csvWriter.append(","); - csvWriter.append(Double.toString(link.getToNode().getCoord().getX())); - csvWriter.append(","); - csvWriter.append(Double.toString(link.getToNode().getCoord().getY())); - csvWriter.append("\n"); + try (FileWriter csvWriter = new FileWriter(stopsFileName + "-stops-locations.csv")) { + csvWriter.append("Stop ID"); + csvWriter.append(","); + csvWriter.append("Link ID"); + csvWriter.append(","); + csvWriter.append("X"); + csvWriter.append(","); + csvWriter.append("Y"); + csvWriter.append("\n"); + + // Read original data csv + log.info("Start processing the network. This may take some time..."); + + BufferedReader csvReader = new BufferedReader(new FileReader(stopsData)); + csvReader.readLine(); + while (true) { + String stopEntry = csvReader.readLine(); + if (stopEntry == null) { + break; + } + String[] stopData = stopEntry.split(";"); + // write stop + Coord coord = new Coord(Double.parseDouble(stopData[2]), Double.parseDouble(stopData[3])); + + if (serviceArea == null || MGC.coord2Point(coord).within(serviceArea)) { + List> attributes = new ArrayList>(5); + attributes.add(createTuple("id", stopData[0])); + attributes.add(createTuple("x", stopData[2])); + attributes.add(createTuple("y", stopData[3])); + Link link = getStopLink(coord, network); + attributes.add(createTuple("linkRefId", link.getId().toString())); + this.writeStartTag("stopFacility", attributes, true); + + csvWriter.append(stopData[0]); + csvWriter.append(","); + csvWriter.append(link.getId().toString()); + csvWriter.append(","); + csvWriter.append(Double.toString(link.getToNode().getCoord().getX())); + csvWriter.append(","); + csvWriter.append(Double.toString(link.getToNode().getCoord().getY())); + csvWriter.append("\n"); + } } } - csvWriter.close(); } private Link getStopLink(Coord coord, Network network) { diff --git a/src/main/java/org/matsim/run/prepare/LeipzigDrtVehicleCreator.java b/src/main/java/org/matsim/run/prepare/LeipzigDrtVehicleCreator.java index 2b8f1aee..44713f01 100644 --- a/src/main/java/org/matsim/run/prepare/LeipzigDrtVehicleCreator.java +++ b/src/main/java/org/matsim/run/prepare/LeipzigDrtVehicleCreator.java @@ -1,5 +1,6 @@ package org.matsim.run.prepare; +import com.google.common.collect.Sets; import com.opencsv.CSVWriter; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -35,7 +36,7 @@ description = "Writes drt vehicles file" ) -public class LeipzigDrtVehicleCreator implements MATSimAppCommand { +public final class LeipzigDrtVehicleCreator implements MATSimAppCommand { private static final Logger log = LogManager.getLogger(LeipzigDrtVehicleCreator.class); @@ -82,11 +83,38 @@ public Integer call() throws Exception { TransportModeNetworkFilter filter = new TransportModeNetworkFilter(network); filter.filter(drtNetwork, modes); - List serviceAreas = shp.readFeatures(); - MatsimVehicleReader reader = new MatsimVehicleReader(vehicles); reader.readFile(vehTypesFile); + createDrtVehicles(vehicles, drtNetwork, shp, noVehiclesPerArea, drtMode); + + String string = vehTypesFile.split("xml")[0].substring(0, vehTypesFile.split("xml")[0].length() - 1) + "-scaledFleet-caseNamav-" + + noVehiclesPerArea + "veh.xml"; + + //write files + new MatsimVehicleWriter(vehicles).writeFile(vehTypesFile.split("xml")[0].substring(0, vehTypesFile.split("xml")[0].length() - 1) + "-scaledFleet-caseNamav-" + + noVehiclesPerArea + "veh.xml"); + writeVehStartPositionsCSV(drtNetwork, vehTypesFile.split("xml")[0].substring(0, vehTypesFile.split("xml")[0].length() - 1) + "-scaledFleet-caseNamav-" + + noVehiclesPerArea + "veh_startPositions.csv"); + + return 0; + } + + /** + * Creates Drt vehicles for an area, which can consist of multiple, connected single areas (features). + * Use this method if you want to create drt vehicles for one single service area. + */ + public void createDrtVehicles(Vehicles vehicles, Network network, ShpOptions shp, int noVehiclesPerArea, String drtMode) { + + List serviceAreas = shp.readFeatures(); + + //delete existing drtVehicles + for (Id vehId : vehicles.getVehicles().keySet()) { + vehicles.removeVehicle(vehId); + } + + createDrtVehTypeIfMissing(vehicles); + VehicleType drtType = null; //this is ugly hard coded and should maybe be converted into a run input parameter @@ -96,25 +124,43 @@ public Integer call() throws Exception { } } + Network filteredNetwork = NetworkUtils.createNetwork(); + TransportModeNetworkFilter filter = new TransportModeNetworkFilter(network); + filter.filter(filteredNetwork, Sets.newHashSet(drtMode)); + for (SimpleFeature serviceArea : serviceAreas) { - createVehiclesByRandomPointInShape(serviceArea, drtNetwork, noVehiclesPerArea, serviceStartTime, - serviceEndTime, serviceAreas.indexOf(serviceArea), drtType); + createVehiclesByRandomPointInShape(serviceArea, filteredNetwork, noVehiclesPerArea, serviceStartTime, + serviceEndTime, serviceAreas.indexOf(serviceArea), drtType, drtMode, vehicles); } + } - String string = vehTypesFile.split("xml")[0].substring(0, vehTypesFile.split("xml")[0].length() - 1) + "-scaledFleet-caseNamav-" - + noVehiclesPerArea + "veh.xml"; + /** + * Creates Drt vehicles for a single area (feature). + * Use this method if you want to create drt vehicles for one single, separated + independent service area. + */ + public void createDrtVehiclesForSingleArea(Vehicles vehicles, Network network, SimpleFeature feature, int noVehiclesPerArea, String drtMode) { - //write files - new MatsimVehicleWriter(vehicles).writeFile(vehTypesFile.split("xml")[0].substring(0, vehTypesFile.split("xml")[0].length() - 1) + "-scaledFleet-caseNamav-" - + noVehiclesPerArea + "veh.xml"); - writeVehStartPositionsCSV(drtNetwork, vehTypesFile.split("xml")[0].substring(0, vehTypesFile.split("xml")[0].length() - 1) + "-scaledFleet-caseNamav-" - + noVehiclesPerArea + "veh_startPositions.csv"); + createDrtVehTypeIfMissing(vehicles); - return 0; + VehicleType drtType = null; + + //this is ugly hard coded and should maybe be converted into a run input parameter + for (VehicleType type : vehicles.getVehicleTypes().values()) { + if (type.getId().toString().contains("conventional")) { + drtType = type; + } + } + + Network filteredNetwork = NetworkUtils.createNetwork(); + TransportModeNetworkFilter filter = new TransportModeNetworkFilter(network); + filter.filter(filteredNetwork, Sets.newHashSet(drtMode)); + + createVehiclesByRandomPointInShape(feature, filteredNetwork, noVehiclesPerArea, serviceStartTime, + serviceEndTime, 1, drtType, drtMode, vehicles); } private void createVehiclesByRandomPointInShape(SimpleFeature feature, Network network, int noVehiclesPerArea, - double serviceStartTime, double serviceEndTime, int serviceAreaCount, VehicleType drtType) { + double serviceStartTime, double serviceEndTime, int serviceAreaCount, VehicleType drtType, String drtMode, Vehicles vehicles) { Geometry geometry = (Geometry) feature.getDefaultGeometry(); for (int i = 0; i < noVehiclesPerArea; i++) { @@ -177,4 +223,18 @@ private void writeVehStartPositionsCSV(Network drtNetwork, String outputFile) { log.error(e); } } + + /** + *Creates a Leipzig standard drt vehicle type if its missing. + */ + private void createDrtVehTypeIfMissing(Vehicles vehicles) { + if (!vehicles.getVehicleTypes().containsKey(Id.create("conventional_vehicle", VehicleType.class))) { + VehicleType vehType = VehicleUtils.createVehicleType(Id.create("conventional_vehicle", VehicleType.class)); + VehicleCapacity capacity = vehType.getCapacity(); + capacity.setSeats(6); + + vehType.setDescription("conventional DRT"); + vehicles.addVehicleType(vehType); + } + } } diff --git a/src/main/java/org/matsim/run/prepare/NetworkOptions.java b/src/main/java/org/matsim/run/prepare/NetworkOptions.java index b87deefb..bcd47333 100644 --- a/src/main/java/org/matsim/run/prepare/NetworkOptions.java +++ b/src/main/java/org/matsim/run/prepare/NetworkOptions.java @@ -17,7 +17,7 @@ public class NetworkOptions { @CommandLine.Option(names = "--drt-area", description = "Path to SHP file specifying where DRT mode is allowed") - private Path drtArea; + private static Path drtArea; @CommandLine.Option(names = "--drt-modes", description = "List of modes to add. Use comma as delimiter", defaultValue = TransportMode.drt) private String drtModes; @CommandLine.Option(names = "--car-free-area", description = "Path to SHP file specifying car-free area") @@ -35,6 +35,10 @@ public class NetworkOptions { @CommandLine.Option(names = "--slow-speed-relative-change", description = "provide a value that is bigger than 0.0 and smaller than 1.0") private Double slowSpeedRelativeChange; + public static Path getDrtArea() { + return drtArea; + } + /** * Return whether a car free area is defined. */ diff --git a/src/main/java/org/matsim/run/prepare/PrepareDrtStops.java b/src/main/java/org/matsim/run/prepare/PrepareDrtStops.java index fe5f6ae9..59f8711e 100644 --- a/src/main/java/org/matsim/run/prepare/PrepareDrtStops.java +++ b/src/main/java/org/matsim/run/prepare/PrepareDrtStops.java @@ -29,8 +29,8 @@ public class PrepareDrtStops implements MATSimAppCommand { private String mode; // mode = "drt", "av" or other specific drt operator mode - @CommandLine.Option(names = "--output-folder", description = "path to output folder", required = true) - private String outputFolder; + @CommandLine.Option(names = "--output", description = "output file name", required = true) + private String outputFile; public static void main(String[] args) throws IOException { new PrepareDrtStops().execute(args); @@ -43,7 +43,7 @@ public Integer call() throws Exception { Scenario scenario = ScenarioUtils.loadScenario(config); Network network = scenario.getNetwork(); - DrtStopsWriter drtStopsWriter = new DrtStopsWriter(stopsData, network, mode, shp, outputFolder); + DrtStopsWriter drtStopsWriter = new DrtStopsWriter(stopsData, network, mode, shp, outputFile); drtStopsWriter.write(); return 0; } diff --git a/src/main/java/org/matsim/run/prepare/PrepareTransitSchedule.java b/src/main/java/org/matsim/run/prepare/PrepareTransitSchedule.java index bfc47cdb..698c6d48 100644 --- a/src/main/java/org/matsim/run/prepare/PrepareTransitSchedule.java +++ b/src/main/java/org/matsim/run/prepare/PrepareTransitSchedule.java @@ -46,6 +46,29 @@ public static void main(String[] args) { @Override public Integer call() throws Exception { + + Config config = ConfigUtils.createConfig(); + config.transit().setTransitScheduleFile(input); + config.global().setCoordinateSystem("EPSG:25832"); + Scenario scenario = ScenarioUtils.loadScenario(config); + TransitSchedule transitSchedule = scenario.getTransitSchedule(); + + prepareDrtIntermodality(transitSchedule, shp, railwaysOnly); + + TransitScheduleWriter writer = new TransitScheduleWriter(transitSchedule); + writer.writeFile(output); + + return 0; + } + + /** + * method for adapting an existing TransitSchedule such that transit stops are available for intermodality between. + * public transit and the modelled drt mode(s). + * @param transitSchedule transitSchedule loaded from scenario. + * @param shp shp file of drt service area + */ + public void prepareDrtIntermodality(TransitSchedule transitSchedule, ShpOptions shp, boolean railwaysOnly) { + Geometry intermodalArea = null; List features = shp.readFeatures(); for (SimpleFeature feature : features) { @@ -58,12 +81,6 @@ public Integer call() throws Exception { // Geometry intermodalArea = shp.getGeometry(); - Config config = ConfigUtils.createConfig(); - config.transit().setTransitScheduleFile(input); - config.global().setCoordinateSystem("EPSG:25832"); - Scenario scenario = ScenarioUtils.loadScenario(config); - TransitSchedule transitSchedule = scenario.getTransitSchedule(); - if (railwaysOnly) { filterRailboundTransitLines(transitSchedule); } else { @@ -81,10 +98,6 @@ public Integer call() throws Exception { ProjectionUtils.putCRS(transitSchedule, "EPSG:25832"); - TransitScheduleWriter writer = new TransitScheduleWriter(transitSchedule); - writer.writeFile(output); - - return 0; } private void filterRailboundTransitLines(TransitSchedule transitSchedule) { diff --git a/src/test/java/org/matsim/run/RunLeipzigIntegrationTest.java b/src/test/java/org/matsim/run/RunLeipzigIntegrationTest.java index 65c91299..6c2bf2a9 100644 --- a/src/test/java/org/matsim/run/RunLeipzigIntegrationTest.java +++ b/src/test/java/org/matsim/run/RunLeipzigIntegrationTest.java @@ -6,13 +6,11 @@ import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Network; import org.matsim.application.MATSimApplication; -import org.matsim.application.options.ShpOptions; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.controler.OutputDirectoryHierarchy; import org.matsim.core.network.NetworkUtils; import org.matsim.simwrapper.SimWrapperConfigGroup; -import picocli.CommandLine; import java.nio.file.Path; @@ -21,33 +19,27 @@ public class RunLeipzigIntegrationTest { - private static final String URL = "https://svn.vsp.tu-berlin.de/repos/public-svn/matsim/scenarios/countries/de/leipzig/leipzig-v1.1/input/"; - @CommandLine.Mixin - private ShpOptions shp; - private static final String exampleShp = "input/v1.2/drtServiceArea/preliminary-serviceArea-leipzig-utm32n.shp"; + private static final String URL = "https://svn.vsp.tu-berlin.de/repos/public-svn/matsim/scenarios/countries/de/leipzig/leipzig-v1.2/input/"; + private static final String exampleShp = "input/v1.2/drtServiceArea/Leipzig_stadt.shp"; @Test public final void runPoint1pctIntegrationTest() { Path output = Path.of("output/it-1pct"); - Config config = ConfigUtils.loadConfig("input/v1.2/leipzig-test.with-drt.config.xml"); + Config config = ConfigUtils.loadConfig("input/v1.2/leipzig-v1.2-25pct.config.xml"); config.global().setNumberOfThreads(1); config.qsim().setNumberOfThreads(1); config.controler().setLastIteration(1); config.controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); config.controler().setOutputDirectory(output.toString()); - config.network().setInputFile(URL + "drt-base-case/leipzig-v1.1-network-with-pt-drt.xml.gz"); - config.plans().setInputFile(URL + "leipzig-v1.1-0.1pct.plans.xml.gz"); - config.transit().setTransitScheduleFile(URL + "leipzig-v1.1-transitSchedule.xml.gz"); - config.transit().setVehiclesFile(URL + "leipzig-v1.1-transitVehicles.xml.gz"); - config.vehicles().setVehiclesFile(URL + "drt-base-case/leipzig-v1.1-vehicle-types-with-drt-scaledFleet.xml"); + config.plans().setInputFile(URL + "leipzig-v1.2-0.1pct.plans-initial.xml.gz"); ConfigUtils.addOrGetModule(config, SimWrapperConfigGroup.class).defaultDashboards = SimWrapperConfigGroup.Mode.disabled; MATSimApplication.execute(RunLeipzigScenario.class, config, "run", "--1pct", "--slow-speed-area", exampleShp, - "--slow-speed-relative-change", "0.5","--drt-area", exampleShp, "--drt-modes", "drtNorth,drtSoutheast", "--post-processing", "disabled", - "--parking-cost-area", exampleShp); + "--slow-speed-relative-change", "0.5","--drt-area", exampleShp, "--post-processing", "disabled", + "--parking-cost-area", exampleShp, "--intermodality", "drtAsAccessEgressForPt"); assertThat(output) .exists() diff --git a/src/test/java/org/matsim/run/prepare/NetworkOptionsTest.java b/src/test/java/org/matsim/run/prepare/NetworkOptionsTest.java index 5e1abed2..71bb00c3 100644 --- a/src/test/java/org/matsim/run/prepare/NetworkOptionsTest.java +++ b/src/test/java/org/matsim/run/prepare/NetworkOptionsTest.java @@ -22,7 +22,7 @@ public class NetworkOptionsTest { public MatsimTestUtils utils = new MatsimTestUtils(); private static final String URL = "https://svn.vsp.tu-berlin.de/repos/public-svn/matsim/scenarios/countries/de/leipzig/leipzig-v1.1/input/"; - private static final String shpPath = "./input/v1.2/drtServiceArea/preliminary-serviceArea-leipzig-utm32n.shp"; + private static final String shpPath = "./input/v1.2/drtServiceArea/leipzig_flexa_service_area_2021.shp"; private Network network; private NetworkOptions options; From be48cadb5138ca2ddd46667dadb1baa173e38815 Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 29 Aug 2023 10:53:23 +0200 Subject: [PATCH 084/106] some cleanup --- .../org/matsim/run/RunLeipzigScenario.java | 19 ------------------- .../org/matsim/run/prepare/DrtCaseSetup.java | 2 +- .../matsim/run/prepare/NetworkOptions.java | 13 ++++++++----- 3 files changed, 9 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index eb416493..e2c4f66d 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -97,9 +97,6 @@ public class RunLeipzigScenario extends MATSimApplication { @CommandLine.Option(names = "--parking-cost-time-period-end", defaultValue = "0", description = "End of time period for which parking cost will be charged.") private Double parkingCostTimePeriodEnd; - @CommandLine.Option(names = "--income-dependent", defaultValue = "true", description = "Income dependent scoring", negatable = true) - private boolean incomeDependent; - @CommandLine.Option(names = "--drt-case", defaultValue = "oneServiceArea", description = "Defines how drt is modelled. For a more detailed description see class DrtCaseSetup.") private DrtCaseSetup.DrtCase drtCase; @@ -258,19 +255,6 @@ protected Config prepareConfig(Config config) { @Override protected void prepareScenario(Scenario scenario) { - // TODO: can be removed once v1.2 is done, because this is done in the preparation phase - for (Link link : scenario.getNetwork().getLinks().values()) { - Set modes = link.getAllowedModes(); - - // allow freight traffic together with cars - if (modes.contains("car")) { - Set newModes = Sets.newHashSet(modes); - newModes.add("freight"); - - link.setAllowedModes(newModes); - } - } - //this has to be executed before DrtCaseSetup.prepareScenario() as the latter method relies on the drt mode being added to the network networkOpt.prepare(scenario.getNetwork()); // (passt das Netz an aus den mitgegebenen shape files, z.B. parking area, car-free area, ...) @@ -322,9 +306,6 @@ public void install() { install(new PersonMoneyEventsAnalysisModule()); } - - bind(new TypeLiteral>() { - }).toInstance(new ForceInnovationStrategyChooser<>(10, ForceInnovationStrategyChooser.Permute.yes)); } }); diff --git a/src/main/java/org/matsim/run/prepare/DrtCaseSetup.java b/src/main/java/org/matsim/run/prepare/DrtCaseSetup.java index e0118129..d3d66a5d 100644 --- a/src/main/java/org/matsim/run/prepare/DrtCaseSetup.java +++ b/src/main/java/org/matsim/run/prepare/DrtCaseSetup.java @@ -59,7 +59,7 @@ public final class DrtCaseSetup { "input/v1.2/drtServiceArea/leipzig_flexa_service_area_2021.shp"), null, null); - private static String errorMessage = "Unexpected value: "; + private static final String errorMessage = "Unexpected value: "; static Set drtModes = new HashSet<>(); diff --git a/src/main/java/org/matsim/run/prepare/NetworkOptions.java b/src/main/java/org/matsim/run/prepare/NetworkOptions.java index bcd47333..1f7b7591 100644 --- a/src/main/java/org/matsim/run/prepare/NetworkOptions.java +++ b/src/main/java/org/matsim/run/prepare/NetworkOptions.java @@ -17,7 +17,7 @@ public class NetworkOptions { @CommandLine.Option(names = "--drt-area", description = "Path to SHP file specifying where DRT mode is allowed") - private static Path drtArea; + private Path drtArea; @CommandLine.Option(names = "--drt-modes", description = "List of modes to add. Use comma as delimiter", defaultValue = TransportMode.drt) private String drtModes; @CommandLine.Option(names = "--car-free-area", description = "Path to SHP file specifying car-free area") @@ -35,10 +35,6 @@ public class NetworkOptions { @CommandLine.Option(names = "--slow-speed-relative-change", description = "provide a value that is bigger than 0.0 and smaller than 1.0") private Double slowSpeedRelativeChange; - public static Path getDrtArea() { - return drtArea; - } - /** * Return whether a car free area is defined. */ @@ -112,6 +108,13 @@ public void prepare(Network network) { } } + /** + * Return path to drt area. + */ + public Path getDrtArea() { + return drtArea; + } + private boolean isDefined(Path p) { return p != null; } From 04e7c7850be05b20280addc4b95a1be57c1b8572 Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 29 Aug 2023 10:54:52 +0200 Subject: [PATCH 085/106] update matsim version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0c559fb2..98dce88a 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.matsim matsim-all - 16.0-PR2714 + 16.0-PR2745 From f3016c140f579714f119523beafa3bd9ef889d81 Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 29 Aug 2023 10:58:15 +0200 Subject: [PATCH 086/106] add drt extensions for dashboards --- pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pom.xml b/pom.xml index 98dce88a..0adbfd91 100644 --- a/pom.xml +++ b/pom.xml @@ -112,6 +112,11 @@ ${matsim.version} compile + + org.matsim.contrib + drt-extensions + ${matsim.version} + org.matsim.contrib From 0c9646265e11848b2b9606cad7d9521586a12e67 Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 29 Aug 2023 12:21:22 +0200 Subject: [PATCH 087/106] fix imports --- src/main/java/org/matsim/run/RunLeipzigScenario.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index e2c4f66d..cc978dd4 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -2,7 +2,6 @@ import ch.sbb.matsim.routing.pt.raptor.SwissRailRaptorModule; import com.google.common.collect.Sets; -import com.google.inject.TypeLiteral; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.matsim.analysis.*; @@ -10,9 +9,6 @@ import org.matsim.analysis.pt.stop2stop.PtStop2StopAnalysisModule; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.TransportMode; -import org.matsim.api.core.v01.network.Link; -import org.matsim.api.core.v01.population.Person; -import org.matsim.api.core.v01.population.Plan; import org.matsim.application.MATSimApplication; import org.matsim.application.analysis.CheckPopulation; import org.matsim.application.analysis.noise.NoiseAnalysis; @@ -37,8 +33,6 @@ import org.matsim.core.controler.Controler; import org.matsim.core.population.algorithms.PermissibleModesCalculator; import org.matsim.core.population.algorithms.PermissibleModesCalculatorImpl; -import org.matsim.core.replanning.choosers.ForceInnovationStrategyChooser; -import org.matsim.core.replanning.choosers.StrategyChooser; import org.matsim.core.replanning.strategies.DefaultPlanStrategiesModule; import org.matsim.core.router.AnalysisMainModeIdentifier; import org.matsim.core.router.MultimodalLinkChooser; @@ -54,7 +48,10 @@ import javax.annotation.Nullable; import java.net.URISyntaxException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; /** * Run the Leipzig scenario. All the upstream stuff (network generation, initial demand generation) is in the Makefile. From 556c4482afd307895dec22b17e6f7fb81ff088fd Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Tue, 29 Aug 2023 15:02:32 +0200 Subject: [PATCH 088/106] added test for parking locations --- src/test/java/org/matsim/run/RunLeipzigIntegrationTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/java/org/matsim/run/RunLeipzigIntegrationTest.java b/src/test/java/org/matsim/run/RunLeipzigIntegrationTest.java index 6c2bf2a9..fb3802cf 100644 --- a/src/test/java/org/matsim/run/RunLeipzigIntegrationTest.java +++ b/src/test/java/org/matsim/run/RunLeipzigIntegrationTest.java @@ -3,6 +3,7 @@ import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; +import org.matsim.analysis.ParkingLocation; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Network; import org.matsim.application.MATSimApplication; @@ -45,6 +46,8 @@ public final void runPoint1pctIntegrationTest() { .exists() .isNotEmptyDirectory(); + new ParkingLocation().execute("--directory", output.toString()); + Network network = NetworkUtils.readNetwork(output + "/" + config.controler().getRunId() + ".output_network.xml.gz"); assertTrue(network.getLinks().get(Id.createLinkId("24232899")).getFreespeed() < 12.501000000000001); assertTrue(network.getLinks().get(Id.createLinkId("24675139")).getFreespeed() < 7.497); From fd15755a83d23e37a7132896ec2bf0a003976860 Mon Sep 17 00:00:00 2001 From: simei94 Date: Thu, 31 Aug 2023 14:01:40 +0200 Subject: [PATCH 089/106] add TODO --- src/main/java/org/matsim/run/RunLeipzigScenario.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index cc978dd4..2f18cc4a 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -236,6 +236,7 @@ protected Config prepareConfig(Config config) { BicycleConfigGroup bikeConfigGroup = ConfigUtils.addOrGetModule(config, BicycleConfigGroup.class); bikeConfigGroup.setBicycleMode(TransportMode.bike); } + //TODO we may have to implement another case for bikeTeleportedStandardMATSim default -> throw new IllegalStateException("Unexpected value: " + (bike)); } From cd06a3bf4bf43f9616eaa038aaf395ed7ffa0c4c Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 5 Sep 2023 15:03:06 +0200 Subject: [PATCH 090/106] v1.2 routes ref --- input/v1.2/leipzig-v1.2-routes-ref.csv.gz | Bin 0 -> 383738 bytes .../dashboard/LeipzigDashboardProvider.java | 7 ++++++- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 input/v1.2/leipzig-v1.2-routes-ref.csv.gz diff --git a/input/v1.2/leipzig-v1.2-routes-ref.csv.gz b/input/v1.2/leipzig-v1.2-routes-ref.csv.gz new file mode 100644 index 0000000000000000000000000000000000000000..c5f45fbcf5687f031093814b2026d94c59b95910 GIT binary patch literal 383738 zcmV(%K;pk2iwFpo?)GE=18ik!aC&KHEp{<3GA(j%b#!HOEplaME@N|c0IZ$qj$}!4 zo&OWyQ|NCX;rsG54MLNe|M72s z{mUQ!`qzK{m&bqnHU9jcfBirI_V~;H`q#%_{^|cd{_-#X^skS9|BrwE$A8$pjkRko z^-=mBeWm`;e~yo_QmS?ARrjuCJ?j4GtIS-}>ZP{)sO@8?Im$?@^xSLhkNz>{%=%9K z?M^%8^Z1kTcN#P2J@-gE&-SS0V~kl>E34%>x`r$Jqpq1(S+li0$A0K*s=g}aw(G9F zuYQ)W^q(Jj=sl+ddKR#G z1&x!}OtrMUb{|MsxfFfLXnA*i`T9s@?X2&bCC%FBBY%v&`&vD((|(TYtTl1ZZ#9*)*99 zt21#HP%;}a>wVi|U+4sB(rf*gt7bM%t~oayz$}@yb-m-#TlAtc^O3cgbQU^$t2Aaw zFI+U;Iq4y_GFsD;bfVk(SVIR@52t15`u<1P(sUhO%|RPwrt#GM zECl49c`!Yz4&CN)mxj?{&?%Xz8K4qY0<6Q?zi zkj^pVZ=`)o(Qr-5M4NyEH>Zwy$@&^C^;JOiENy8o?Q!i@{U;ARHRI{)$KbC}4^oUp z<4R*c(#O=?YBNc*YHN+#M#_FVPMTIgVv^pMWu4Ni-QyPRUVVMnMz*?RmuqP|$GXm5 zZv#&T&YBV(VXd>asn%mlTWWji0PHeG@?=d;IR{{{tdh`YM?Xq(MGamhEF`p5cFBG1 zp-Im?df_mz>2;##@B-__`jmE-ZqO=QhBVODoXx6H^_3SHI?MWYT{3#>tmo9L*GJb? z)S50DslcXh%Iqp>KHjNz4q9TDuEeD&xth{dCE>sS@Bj1f|M4&X_D5M`YV?}*Upj?@ zn^v1%8cRErvt1=p-!yd7Z9VVOG}VtjG#@ET;b|l2Ah-0fSJC;@ox8YnB~m+BJ7cXf zbd$C5W06u%+5}R6ZRF=C_CcN6ZhDw3&93`;YXitw(CkUY6sZuM%dREr`^SQ$_4-Fe z8|l*5p{_e4Ky#_xre~PC%yl$%Hj1v1;k!@Ur|3O(xoEe|AB);HXf$JsuPfS63sJgy zkih~LUAK}cU8BXVvKX|!+#@ncl5}fJGEJn1){jL>A4sN-g*0YavOlyJq|~*=WkyY@ zb?tZ^1cd5)wb`0ps9>h(JvkSePbs3Rulx<6)i27`i*YqIQNu#D@4i-(OI|a-rM7kJ zX$T#Rxb~&7Bp3T=wx7!!j3xy(xxc0ifyPTUeckx_&6ljLBezp&dVL*usU=-lGD4+m zIAl7zKbJWfaOkjp8G)Wj&7f4b)-+4gbiXBPcgt+mMI@t2LM1Zk^lCXJSDNeRyC{3d zZt16~71a@uWx>%%U1ve3b8@>crrTzS?h8GIGznW5+gFN6OCn1`6a8bM(mHxECTgHL znlBg*yxy{McCgc=f!WWReQEB&ojJXchRk`L*DgJv3pwd((GzIbYd7@A*8^lNTJ2l^ zv1O%={)beC&gCkoAH7~Jx&U>1bAkAt*H`7l$XOa~qmU{)O&eTRzXp)6(6G^Tf|m?% zef94O_WEi{*@>FhRIT-DlO6T>ix-xisuk2Vs)fL0t6yIDQMF%S5#9@Bc-=1$2*|z7`Uca#dv+RgE`tzf4(hGJ| zs@A6?wi$kg4!8DU!a|etI^?vJj%JX4W2a~@JvvH3*ZbF9|CloHqz zMCit-L}_%wWU4-fj*Z^u>qldnKOKhwr|k}=k>F%wq%b*ZH}BtyQLjz-mz zY2(T^J_sphYcxVbG68glcAcJqzLQ3jFOU*vh2@ZH zDDu*@mGLpJyH=;Z6Zx?lUFOi1Ux?6B%SX}S&>kAwuQ8-mRz~+pH{rKzOr@ejUU=uL z2yKsFPu`GoDy^!^38T<|qm3*>NTX@z?uI-3Q4lC2MLw~vPpvnOjShK}3e;{S2!LZ? zuS6Pm^0uk)8n}EY>xKIvRf)$a<)Iz1We4fiFiIo=XJ6+>k36u5FA}Vi`IoiOYs23w z?~a$nBgo5&QKp;}o*q_LhOX<(8`RObuw{1Z*)GE`Eb|&sGA)^`QgpuNQmdhhPv)NX zj~zK_5KXyM3vL3Bzci^FOB8U)m+|f5L`(oVvcz;dX;P%5tj)EX2iZP zameN}XgD#IC?L^LjLpUP+Sk=_hrRL4?0zO9dDwvZ;s=4jf>EM((}lfnPaO`6Of~(# zW~JdKBY-%=K*bQ3smwYNWvOe;G65CdKx@*ufpx+%anQ4XVF7x4siJ3GqlaxHsYN!A zb~7?{oMe|vOC&t`=~s|(mzq{cJ3t%yCSf9BNXuo6BJE3p5k~>+5-tghcngqbkou0b zCh&6>;H|R=8li8jXAmH*BsUz}K(96tpesR>u?Tp2Bj|KgVvLR0hqT{C05|imTYoz8 z_4?7VgPZ1?ZB;U^9+uETVI>Z)UiKgV!LFPP8d+#k;W9MX0E8%s4r(GfgSa;O%T1t) zSe}IHuohntI;2_vhpq_hE^So3U1oyx-u)(EhLfvjmy;>=qN#f9LhP+!F8Ommrgj2> zC4&DY-)5vCViFQYF_+%4HslLQlk2H9kp!NcOCaST^&}TU z-rh}uPp~X-eT{>f1YRUybD=l3Evgy;($DC7&4;`Y3y{C5pX>uUdRGB)nl-bs>Ir=8 zsnfhGhJ((Xj<+rXotKIA!o?%ER#PwQKoT%WbpJ?S5J0@nQk-WQDH^QUI4=7vLR+9< zTMq6%XIx~X=>=ro$YvNA7Z(ve(cC+_nKmbAHbf9|@7jq(KAY!B#$6?WBdxNXmg<_; zCfA}8{>xVZz6oS*GQT=-ItJ(Q_uxTS#pW%}BxvHw>rkp!06&a)+80JCm|g@0V+syo(a8@JJZWF z-VBAHrV9SM_AJiNLH>t8C~RVCed7(r5R(2giGDG zH`vUOPNh__9GRrEmBq4vfoIi z@`(iPri`aIm_EYKt>_7~>pHr2tgD3K2x!Oez;Pj+U~F%yo@{PG3jLey-&;ZrYF%Cxd&~2(i&Wg(!cGtkK%sT zb2zyol~lQVWhZLWY6H&r!z-H(C>u}qav?jZ zWt0$YuQx*@!gWK(Hj@L<`{8-&?BPp0s;o!($A7?o*KVoa-ETIyzHp$bQz)5(fYq)sNsp3k)qSV2Bv96Vy@;5Yc75t{?BqdYzhP)>Qh$k5 zuI{c{oGaRPQ#N-uo_%Ow)`Xs&;(TCXBlAHQHfl92xxUS-Xa zR=&@xgoT97!I74iLA^)0yn`HlX)bOWdAhuV{@{pLT$Ob@Ze$aG-Kr$fwZ8bTy1NNB zHlo6Xyzs_}tp)~Y6_Qk|Obya<3lXw!r6w8)7)jS@9w<>x&ohi#xc+4U>4qMYfU)k_ zsU?=em5)P+F3Z>GM4G0CTY^TSi4 z8(lk)98?=Iye3(yM|u*knmhJ`gbiQAimmg*kMqMfvg9&%uqXW_T)b-^&I_45VMx2v0{Ole4OCk17V&^ovgQmD9axLRi20}{M zJ(VE$l9wj?S-WN~r#$tvJSoC{77$rQJ((;SX(po>(D2f$q*_V%=uZde z%%Hi08`yxa=V=*^>bBH(HMP zaHwlxzw26^jnJc$(elPCA~jq}Hvu-+WC#}0edHrr0GhOX4J}LT$8Ugjl~KG^JBcO0 zm8{$!8K(h#a}?m|)k%3x`1HZ>IwaZ3*iup&M(N1T*D)n(OfU~r;%UqP>EK%t`;svO z-W0eRuKWD@#YLOArm~E{MjXZ2a?NDJ>8ik6Bq3w`GF1Q=x{-KK^9AHm_*=G*|626tBu(SEG5p@1jLx_tN^JR7YQ6;VryNO zJ#JR-fkNc57TIKS)A14u)Cn4S$UQOOjwc|0k!{7E$FJVonTvWoKLh4Ss1rxWF;6>0 z+_(4-+V$GcPGfQ75H(*7{NGJR1X?TcD6Kc2;Sp&i^5@;>z{vA-f|us>`{>0&2XgSDosK+{iJY7mhED07xzEPXN_+D}ZATzue0cr1P8i-ckUIEe+InL2ZS-A?*)TDhdQdwFL0+@?(p4)q?>41VZsJxR;fR_8ImGES z8M^+#4T5md`gpc5V7LX2<4^Fvy2O(`(rpz0#OoNmJLw~85Z;m~M)^;6 zOpiGC>?06AWRZzG5UlMt5umb3d&$I-7L`sOHyH#wDv>wrRJj)9#tZOoDaRe5fwPDa zX!7@;j$#fg+3Ls4%oDZ>;3Cp3!{`8TH}YC_Hgv%I^(7p}{zBV3kH}(A4$ap!>XpF5 z0LrYlLL*7S#xQN1AM%zr365iHI*Jpn(nml(bh>m2cLvrTYC0(7hBmkmO^$v{a%&5d zt6T;K3BNvgIUwz_(W(nW_$v=3ouFec?OZv`dOIK>i`*5-zRdBHk;Zi*t@4JR(`Ktc zb^Koq-Sv>32St#dF>2!M1fPKv*sRnQ3@(lczm%yTLiZS0lVyF%;RAxNOAaJ&F4nF; zlIT-FtbG8R!lxO=)5@zu+sd(MgrEG5Jd<`T0!X#X2AP;_*bp{8ek9Q43X{u;tT;G7 zmWd4To2B2;FB90ZJfp-T5K_xzT8v}KwS5Db{$~RBbN{k{Bm=pjoq8rhFROc!8MHzp zST0jpJF#>560z03swh;<)ir-AGFyH14W6QsC6-6vz%-XIL1BM78ou$}G9huUIm|RF z3h+vGZe1PlGl|t(Jv~!c{_LUhs7w0%<<}%S?)<29tqoOKoj{-D?Es>vKixXnHAWzt zUz*&}cX`8|bp-e{s1zZHsNLBes$}c1QzcSo3$LX8bnj$qJK{moY%;jyW%{baAO_tw z9IxaZ8wel_w&d6u@z{h3wR9gg@LoTD$>oXdgpcKMfvea7)1_ccrtOlKQCuKXbzkYH zcGWPzGP4p5wBgb&C_fY!f%C0PRR_M}1Z-zS^lCDuwTccG_=rx>>y)@PTpa_dbUFiq zrO7A$-wfE31N%S=VPbRIC<4$Gk^!P%FcBgfM12ZgD3u?^R~5o>SZ#9Ybg6z|q>~Z_ zbye_Zj-%UEL4$1|%%E_cWNzAjTBRXf){IKSmC>%B7*b%Lw-r876TQ)}0dyRH;T!?d z&WpG-2~L1Jjru^n6p>l#eK1L7E$R}S=tDgNxG3G|fRsC@>@xm7jXwblQw`C{(N4qm zi1%m(h!hYHdJwb%x-7?=`nszCEXx8Sh58x_k91WxL;xtGkgG5s6A>~A<;*TpE(xoP zoHMDd<;Ybcka`wjmv8CN0U|6UYo=j;vJMi!L_#J;Ko!cJv>7#N`bJ)YL#h8M!0Qse z##zFMk}7=#aY#cdngLY$-&atGb2!;RWb#5?i-} zK7u?^N&^2{#9?hjMuE^jz<$30CJ+UOS`z7!ei0DbI^%M8U_cC~H_)M#_d2MJAW8sQ zQ5T4eY5?Ti7!qLI5O^&qs(ziKPcS;xGAQMYGc>iaC^yuBBMN8eD2P}`sYlsKxQyY=vET>XV95pFK@B2!Du;k*@yh+D&?(fR&F{zf-WeikV`;XFR0)(hq zXa0X3-qp$YaAY&|!rDw>Ha3D59kk{i&r)wH3U=pB3$QjoCd@^10;sO@^UJg&j`1BG zUzRa)n+Tfru7mcSFfiou6ZPuJ>P5cFJ!U)bUim3a!uVORSkp&Ga+-35ktfryqxLfy8u85oqhNHlr*$%9FE0>4>6*<@G(gk>EH+M=E% z3}z(Q#TQcr4#e0%^p7`Pb*OAXyCOgz$z_X+k! zesfC?86P_3&8BrDIXhoJTwIZ3B zzDfg7sn{Ha zEo%83Ql`ddn=b*dcYm0EV*_v4BtgbSfP?J1v-O;*V2Mf_boQj3h7#FU%HospA)7SG zCR$L40-VPrtPWtRz!6~WS?%F47COC7m-@MSJ{gsa%j#-Dg4BhIZc5bkPyHgH23BJO znn4c|$m`U>b_(%zvEb}jK-tt!00q(o_5fZ8WJiW7J9m@*JPH_rXUP>f7KyJ&(Eu1| zFmA9Z{02IH)fF|AaXFkgL1Y0%D#Rzj0+5}zfQdH8tz?z-W)x61 zm%w5Ch^L2LJuAr_xvVnGlWt*P2!srPQaMVeM@%q^iKyr;_BzTkI6d8aD4YmQ|ZA$A1C+k>&aFer9F5~;;>SSmfnZn?#~qwoe8=?rar7=H0tLOo4V`P+~Z zOb;*m8BjKgN?ud3hfeOnuGplO zziZ>)wrV8EOx^O8ziNokU4_A>9aTLdS5c`E5~66`P+&+_eCuFl^?Y0FIMF-~re=Iz zwiDp&Z7K(X=a%7D)8FZ9C3;?DlzcjL6Z@`!KHL7&uQs7}+8`S6C3Pq&nMx~G?H2O& zus)fK=|ND2EKAL9hin=UIT!vH&@hCprQNBvBVY;ioU$x~R!-xaW@m1~Okhd*w4Vla z95XR$V580hngr&9G&LkJx(6XI>TKv-E&{dz*)j|%+LMJwDuLP16QK?g@vh`JvTiXV zEI6Q2?g$tWs3ycB?cUD6bCKk!|#Nboi?2%r*E%re2vCqfttppXI1 zvCO!J91ofB!|y=&Nl&8x-xLU6BrI=KrW1M&=*uGR!i0VOidq{P7gQ`PXEdRjDVTFl zP`GPQ4gyLLu#p(k-*Cg5vja2+Tp|gie{=Q204PjoPLru;4uUW&MrmXhP}i6x*?`j| zMdmsouvXCn)uI8V^N2(XbO!*~$=t{(qdrJn?`&WLn9)v(Vh!M`nYcpk7Xh|Uu(YT4oYO)%VOou>!+N$1>^3nT&NA)i9O(x^JYn9q=RNf+ap2JMNTqp<3Cr z)SUu(%9KR_o9#7UMmJi-oH{z10GzZT4R(E@2qy#(os=+cAWPsH!lH&pw#o+-XYh(DZgr#*JX;x%7c`pQefiL zbk}s^H{|Rxvg7#Ur;`M{+l3C3sGnwpchDfPrb$nMeZEf8JPHB7Nf=OSNw2nJny5Bw zq#84PI>R6YzJ+2H&$ncTyah*$$T8}l=mKwn8NR51pyu_d>VB2*#G*mEW!!v& zYVmLyASR;_y_utiEV68uiTyk9-==STaydzt)MH_0RBEeYpF<>0fEoMh%2e3I-GXZmIZG-UGnE8IE-!( zX&SG3lK=$f{k{Z7IkE=^9B>0aL;_ffNv$hL1YHSd0sHXyVDETxBvk zz$Mjg*p?Qk;+&j^=OkD1dX>8`J_Drs$)4t-W5Zw*GeMXKmLO&bpgB-Gcoq{GiKB#i zk)WMD;eXT&w*cBwmjMd_h%=JV#4}gJY%Iyy#pC#cA_T&fptM~7>cWo^a9Wm?`CtX4 zH;PM$IKRx0Gz901+WS^-TQtWG(xoe-G47fiz(B(4a2?6W>Rw~*pFPZE(}FUI4 z*h{zZsWB1a0T0zk&AfNkqt z0-)oovKma`h#jV*gvX3!zD*wk>W%1`Buf!Jobe!VtH0E=^_zsSu7N(Ch9wk_U60vU zC=wySlo1DikYekouTbSj6yk$QE!xj)d8v zyN!%BeRy~|HI#e|fUEpDqFM~=VsRWO*UjmJB}tw%DY`dg4b)#STfp0f0)SQJf!VaI z6CQ^DXOb|V7K>xc5LS?R9gcpOqH?hdwqsdCD?(yi>w~M_%z^w7l}<`M|LFN=*dBC#6^ceU3S=w5vb$U>9h0z3Cr!ugWEmEDoBhL_>2IuP*!G&%gk@DAsdbo~YJyi)qL*|) zEC8D!FtKIaTgEJ}lS@~7ZT*JWU>JT!+eMKDd}BZ-ClO4f>N1*OwxGMPp-I*iU=qnH z*EauHrm4{I^cQzC)WN>jo67@-t|w}HgLD{521pnt^(007?OAx9WHi~d4Hiopz4wspk{s(jlqGz0Gf}8%${vESL)a#3DE`$RMi!MT#HF?`#bLg;0$`prU*l3F;!1|Uo5f2jEcZ-DHDE0J_6GB~0>FI$ zn#0QsZrQIu66?PT4dGL6F1!Fyj8LPC3GQcWop}B^P3jii#kD`*( z?qK($%0X8ncgA*T+NiPet{xo)CoqR2K#A5i!k-*GN4S~E_becA43JKBBjQaP!DtCa zTk?`M_~_9^FxL}GdYDUz&<4n?!C60#zRcN_th&I66QN_uaxL#r3Mv#d{G>Ki0~?PH zl8JhegfX=2@f`$g<_BK03>Oo5mo`rVGFoU6@k3$maJo?vw1Y_4*p9v)B;*oEkAh{U z%}1`M+P0#p0BrM67Em`}ZomZ9oO>ulreG34F7OU5>(P^h=weX7a1L%C3Z)EscA+>C zU%3CfC1BJL-g2}vauM#ius!l_(_tv;B%)9M5o&XjEZqVlPcnt zmL8NhY-f{(5wa^?*=1k?`WOmr2+mUfp}^p>l8%5Usskplrg$K{e2MR2uAWVHbDNTAQQRW|0QH@W0S~C7ILP0F-7 zZwE~jC=LWt>UarfYYW#u$gh|hw`4zKz~MzQ;iE3D;2&L&SP~}lu}154jf7FeEWZl}C_zCM9yUfcc4tL68iR=!YVU*~Dgf=kqh!F&mLyGT3K1$T}%!ekRch z$GlKZ`EvbQzfdFp83J}O@dpJ3%!8vj9oPV^a;70p6P~>CGl_;cf)^o~EVl_Q7R?8! znwV}S5-k>FCz6GgI&9_)G#Uv{kW3ur*AFG;5*E(Js#@mz>(OLE4^43!9i5`?kLVIr zU^ICu#bgpNwv+pV=jpvR>_3%ggU9Sv+NYdpABFzP#yQ}}ViP{&IO0Bp1|yH zqy12z?{TtGgVGt!O!7iPvH`&>we;2V01|T$+siI=2(CHG;1hmna zhb%UtI>45B$Kv=7Rqhq=`VUnAI!BsY)*KwegsOrh&dynlC^Z{egi^K(Y46nBVdK^+dE7Uc~sh$+ByYiB|=TY8u{(`>9k!`uzFfW=0- zy#T_r3H9sv)7FaSX1XqA8M8wI3FNL-3Bj!^oXx zfhk<9jq4Oc$y7xL3DpvG8BrfWh>Cy+@l^tV6qNh3hX_0+{0J%!$q}-x8#VrgIi&2mCy259zW=^NOc~l?c{_&z$VvuCkj;EN`psQZjhP+{1zr20#+$7XW8Z z?+vPgn#eKB4lGcgyGToyy_d!?R39`poXb@M6d&7f2Z^ zDR`V>Dvpihr%Y=tDDpDp$LH}Eq1z4CI~#yjho5%PPovP-vbZQ19)W88NvDKn3T{S# z$+AMza2|a-)wSH1iqbY{RJG0h@6E*b3EOIM8g!l*7H(VYMH3}lhNT)(%9G4}ORz(O z-xxHFl9Gf@i1Loj_(WSF+iVUJGb;tuXK_D18Hep6pnm4)s1hET+=V&#Nu;7^LQE&kFRBJ?Y`adlFBZE zo;q*O(o{3iL!SX1fMeI>?cnv_G+f$7b5K}8C(8`08+sl-H#ag(CYU~sZ=hnPf$aC_-3d+_cGAAV_ZakaCi#Z`|lT%5pZUB1lc0Fk70mUPo0QHrZr;q+@#KtlXS#-QPEUgQlplzc>2DO!tzEbBrUF}A22j988c0miT|?QmhqB(yW(*W zY=A{3IaHpEjya7#qsfJN56zjq26DP|r&>A^czFb9@?)P!-1wN#Pw`^eBtLSR$2( z8B0%5w^|@8nCoQ?;utpqZj?Wfh|H zf$mWPVw3Jt$%-@f-XqW&QC&rVno2#{&ea_d`gI`!7+Q=ji(@G0;vy_ zfE>Ya^fIY-ywMQGWTHRyjcn6kZEw83->Ojqhf<0+9mR}Ohb54BFWIMU-3 zKD#3ikz3d!%6kXZ|DQzH*RtsbK0hM;j{U8v+_Xg69ax(a>R!=)2maC;8s+Evm2J>z z>cWNa{KNvD-~0X!Q+>g(>)UO?*k*=i2XW|nH}Y=?ou{=NBsAYLw@hrEZZt$|Gtt_5{_^iTfB0z zOo(nv_X!e)g%sGNyR%CWjxBr+AV89#h#HjY;dLI~ff9mh`?I#WxCcasQ=QIh+|Sv? z&xB#vpoLyjreS0~QK&i}ee|<{6}9D)?okwc!D#9N1-`QwVkr@Uee0HU;S!P|50H7)&R^9bjVf846hvqA~MTkkUA%hHa zrk<)=h#)crW#b~iG_wS)U198$P6a^)1+X8So1QcX4dUD-0hUPauI_db@~jW~c*8%HAc=TuAiZ4`yn#-;RSMxkY6$_%3cahy8T+ZKnMb&6vvUF@mI18}4A0Sj`ifEM|BdoC5cYJ~t?BdANMUag zO1dFbb`-z4LubZsDy{ucWDpuxSGNHfvEGb4i+x_;TDDJRVo9 zJQyt6@>8LPhOP)(p{1aHqGfNq8pJMi%cBk`)(`Rq-Lg(Et^Ly-8Z_5LY2LcH9Ynh5 zce+Cid7kQ&DsX-9%5Go#xC;qh|gJo8j+Jw_#X#2)PDQH=-!-av^9nrkAJaiH%pHc^yHP z1_-Rx=yl{?wotFrjPNGFXdz5)h>+ozPx-tt-Gj@`%C_bNLkIXC25)k|*~=D+n1b^r z(;2dy-E5-*VKLh<{raL*bwRIX8rI}JnsabTjplN38?I=bZLH}eb}19t2b)6QxoXHO;10fw(u3p-v2+r=`q_n` zrnx#vFate(#NCI4&QH}MFfQqdytU>#P-F$P2(TQO)m&@j=DK9avwQDeL<9vrp-A2d2?;$uZlp zuOb%oc-x*HSA902hFi1%^P5a{+W}?oPFZlVpow51(*o%wc?w*>+omcxVCV$2%ky)7 z7G2p@DTxf4$=XS7le2?HiI6b3xRJ~~MRk8r@<8VdAtq%ybvi7Fw@CVl!U9vp*h+vgjv3!oSX&fb61n(3=peb{AurUJj+4coE71 zT2M~7)T>9G0}eJ#IUsDMRq-bi)~^!0()F{2h%ujTp82hs#x6XOkAb_v?pY2J048Mr zPWoStn+Tg)q|d(S;BBV&^i>?SLPDW*N_GcHL0o>4s<5Cz8UyM1b($QArw~V*LHYx{ zGA{vM;w#qLBUAxHwG6R5f25W#OcQt$U{nmWz?jkTX_KB-5`(^=c<q!XDz?=`bw4r-);`LS7L?~pdX2na)nx)Pf_3lh!_g;r*!kEFp4T^u|7J~6t12|w}AN^7DE1(-8Q0`s`MhnK*vsM(Yn{M`B0?nY1F)v9a>Pz=+{IH}oOah1c zB-JEBM2kgi^+|xjQ8Vh+fb#IPCqLTdX>tiWYbh8iL@}S*|0@txvg?BXtBGi za-t6?4LAmXzvafbW;z~L9%PJ(n9|nW74*0;oHK~|Iq9on#PZ$Z|1VdGt zOA6^0O>h!hSNdz4Jb6aO`6$5p6fN+Ha!KG8G{+;HSeoc%GYkzgc$@SQgxYqZ8fb)D zce0Vr61Ec#?bYT{H|JPb4xa^~kz>(WBsUC;3{276&E<;832_*&Qxts|Aw>g51(Opr zO!Q#-R_+LmVR4I5paGq*PckbcyhhEeSa%U7Hld*<_`FJ?nj0U$KneD8PMZVdZ%w?Y ziyyDl=#l8^sl|h6r(GrZYR>(wjdevrh8VghP~zm*x{J7};Go6?5)dSSNAWq`LEv%` zF(ZQwW!H0J{1SEBdIx_pqQ?bBAGv!YOX3}XZ#NnaMu_ArV7b(nfH{$Ms=*4)6*#-( z`KeY&PID85CPW0GU@Qch+?TR*uk|J(sC23e-=Ut;BXZpZCu4s8F77~~Yuzi9()IO=d?JpEOcf|GwAxU`5Lr%8_0iOEoribW z90VAg`N8aFS2o&iANa~gR!$yZ>C?vW&1LIfMblEw<7`?_f;!ceZf5i*< z?zzGGqPILfA%Ir`YAN(D!ADz{wawl;?X79y^45gVAQLDzk%lp@+Fou_Io!ntXfEv* zH+cxyOwQvMXE|vl_m!%5%Zx!|Q_4>G*jzo-g3w#d`Q02xE5l}NtnmcmM@vb9DRe{W zlvDI4x2#;hiK|^`#2v)w^U2C;pUjroaz{~MciV~_ethVEus8s5p2we2w?B!q&_xq3 zDu`nsLud9;{;o2hMvGjtO(GzE5QG?Af-{lN`Azz_^x;3xUQ8+e6Y0khXq+R&OhsO=~9G_|@BVJV!(&!_YvuodOqz%+5B0 zaWvoSWlU34K-VEx!FiT8BY5&;dFLuL3K}k4W23qgx&T4x28_P|vG!#ZgP?+2CehOp zmX>-RA94VJw>7*PZcX+}{@o>*pIWInQ1<2sX6vx*U~|tqz8D^U@F+I z-E^NK6KYWZ`o+?1q^5%(;2!9#=!7}gjpaZ%F_Li}-(g&7FxN~bqdlIS$z*wG4o}$w zn+udFPfk#41) zT)76=mmcr5q**r!gZ?DDTxyZ`|H`AR^$cN%v1Ge#gCEWp~J^@VkmN<^;hP!04 zI2F1QVCIVw;kOW}NkqUntwfRXatmGp*tDvlaYe1Swat#tr?;LVv-(89`28ZOH#-Pz zxqFF9@fo=wx#DnvV`g3iJdf{ZJLvc{w79+lM0_NOk`8_VFft~O(P2gYjoi; z3jzez+;*Ar9WRUrqdzHKQivMkph4wN9Ilr0`1|+3E;V&sI4yE#IhsLu=CQ1ok{sceKNGohF%Z?7N2@j!FAbAO(5uivZ_+2m!z} zA6Q1?=Noi2DL#EPY{|NTkxqi=XTzmI8}@-AbQym^kI;G&0tkK=}VHYOB-?VV( z9p5;LSfMogt7H5&1OfaL7$NvQO*%vWbIKg0DJZCL(^V`|;hGZS~z=*E%& zh=ClI$u7gR2tO5SouZ`%4Lx0$v%;*WBMUU21Ey{7(*~0jU4cOsquH=%TpUs8Kl^i; zK>*P|8DKKK936rEo`ztj(Iv)Y;#9&?_%SJvFimka#l??h*0_iA8M2_NgGrWHQi-#l^^Qiz% zclLqXL6-pCms}8L?!65GCb2KG=my!uG;60jnT07s(=d0Vngk&#Z4`;XE#n;s<(|g3 zQ87)q@u^QtcnL+@*z{Y$c7Iy(notu1r04?<5fYg8$WP3(06PZovH%T+j(+!Gk{k3^ zm*)hdx*db&Mz4*0pNu1yaA9uh!jp0(4b{#P!ZYaa4OGqYX1WP#{@i5ONT5tv6>+~3 zrHoJ{Jc%DEJQ)`W;mBjx0_{!Yr;-g@~CINRAPjH0cy{cA-2%umEL zO<2XiM_=}+*;iz8ehbTREI^56pibSsaHg^`*=!|%*#VVMWkD_iA7?mca*|N;@t9%Q z0l)%MNUI4K{X$U-(+g&I4vIw*V{|E}@-VP&_<;|qXqRz+25LFPnLn-dsK2HABIrK6u~szFecR;b=-D=YTb~J9|hP$!|{D}r81AephfEjWlJE)A_?p^lZ!X=B@hE3 zbxF4ZkjTgJeTs<>efC&(hL~6-bB5o6pXX|1Y(#Jj`W;=kKVWpHprH8podwu6fI?z8 zYt-MMJj2}?0PYm0zR);4cf&OV@c}7P?#E$+K6Y7~4SMzg@^d8uDAGp1q!xX|fHu^P zGn^@8hA)%~^hV@nws9Wa-9qqyh&^a^Szfm+f3=BmD2mt)vD%iLya0N!xD#tPu*a1e zr}0f4Z4O>IdHFQ4al*layRg##*|-Q+FrmYN`E!d>{)n&CmQmH`@uTNrh8Lb&ZyEyu z-p|F|l!=ue8M}Xnf>4vfH$6PSIPL9AW4visG*Cl9K3T$b|H&}x5?c#(WltlCBdJ5maxDK7PoYEH#8DD zCkZqw2kuRvwbJJV)#ff(qq^~0ivaSk5`6#G`pz{pB2q)n1I4-Bb(a8S0rp83F(XwW zMGr$*3bC%s_|fiJOI+ux-2e+?I`3(PY4Qs_1{R8`T`7*Qfm9t_yOT~q@>|D6hC_3- zHGFrxAub{1xUgG@1$(=hrVM01=A+9N8bEw9^0GbZMZh?{1lU^osj&>SO|2yk3~?mv zVmKF~H4iLVShln|s<(-nhnFY>h!TM?%5&E8RRnw&VtdGyO=kj@6AFXsu21DMe%u01 zjAv+oY479=N1v~ez;%HJP&Ta>3hIhlG1+JphpIGLqJ3KeW#aSuK7V9)}V z4nxlI!L0)*b;=xup?_Y6kE(m397H4OqaC~;^kbc1&rs3P3z(eDA|8NNf5;CiZWPMO zPi(G>gk?tpIuyu-NGzGA$4w0aS+jReziCk|w0 zvf&D>@g9i{dTtXEQ#R{62fCPKc5W*EXSxZ9UTu@N-E^*C=~)#CBlo_eefJZ;Yceqk zIR-Jh##o>(00p*jTZOom<-Nagz1SFnQSMta1U)*Wv$6!hW;({@1{aNu@Qmc2MGRBg zFDM5I1dxD?dzg*1-I{f+ATbBPPQMfPpE377H8F1MocC2gColX&)@Fyyhxyd)hB+*afbFycR!qBbp=l%=(z!_dGvJb zEhu0BfVdkoICkf6sY|h@bYvk=)F5F2K;{+TS|Y44HDU(1&(LZ72*fO)K6D-~?&mb! z>l9uBjg5T*<+JJ1o;TK6p1MzvS+O zyB{hv3}xE2P*|LAv*XAyEV22s8{CKv&-5m$SSv0yvnfOK92FMRHM_^* zqeW_?55?z)SAZLYDl&?PQgRT?6f(vENSi#|o)1jziW}oHLD5>pWpuQ9lWK0Rmi5hr zL$!%N`IYb;fX!2uVY)td*mkoB%DH8-(4-h3dzX&Dmngs-jUxmE65Sc0I0_&+@y2It z#YY$zJaw^i1L7njx{2u%^P!w&-2@o5J75`tGiWuZ1Id2Em>CR1MmZU^p zku}wMQ9?%n)}o;5NEc?XigAqTPebJ_F#P@O_XlC5jskSBeg*Pptzk`(IqDY) z`^h6_+H*{a6f=%*Au0A8qUQ<;vJ4X+UqD|X&x_#ix`z#I1e0!`ltJ;Zi^ zId!&pe3&dNw<;F~&)?lG1GX(4j@t?Z_Cg4fK1=#nKM8Y#g<<+G*ae&WYPi@5%l9l|2KSft zAMP}e^=Q-cY5`Q@F5ny|cZnhWZbk#xPEBM_(*3r`aT^oiF#`#XwfBAg8?r-=iy3nL zuC7d?k4SQSl|r0hM%AN?pk;ua;H?JTsQxd!8I>I<5Sn~Jf|y-`FZR6_YIn5Jcq6ezKsImVuQnd;vp8H73Hpfm&dVR58KNZtUfzcOzksW zs=IrJJLkeBLpIs1(YYL92Q=d!?aco^j}!PD7_fKBw(C2>;%nadMzlSh!rQ3{Jph85-=Oy;oa(U45H)w+!D@CNY& zxH=?bmniqOZJa-wEIX_`={O7}rh8-=xdjO=NgncNw<&V7OKN1eZJ<<&R`D*n)a_Hj zWaekqAW?oo1zGVMEc5UH2GO+5%lO$n5OII#whduHcJX0(D*#QVpE16tk#dFq9V&Jj zK1|28tNVqV1yqNCfRlj|nb_*bDe6FEP~OId!IG~{xtnAeRqk7K!s2{M{3Fw&Zo@KNzcJ2BjKV2T(_#i@Df4N_3! zJ5Bl7g4a#ionD6yx4};UYIE9Rf&MjZ*X0=ye;9uG<(^@C24=oeSX*9PwNmwTd?mkxt^uWeYO|AhWQU*5HB~-d(0B}fb2n6J9 zI=@2bCW>(n0xC2OkQu?X;JCoNy#j=?s35Fzuqb4w35ZMm^&So0PZGxY6==u&Y8_w+ zM8P3dH4(qPVwl}Qwa}Bb^r+}Qh?pl4x@bXJ0vjP`nw%3UC=eLYh}61iA)YfldeJ>J z-R8+#i7+SZeZ~ENob8a$NJO-ym=n4wko}s_%7x!;_6V*Wtvs~9X8|N5q?3zd&eW#4 zIaW45=+}=OT?bJB1ELv9ZOqa@Lpt3H)Z-h|4ztrHT7}I4x~W8GS-$Jp5rzF8;l3z4 z1=>ID+uFWP(8y#MEx>K-|KM0d<&!}>3YY=!z%2l$2b|7K#u-Es!4$jn{mFpL?$2Na zy4(GAc=|v{Q2ySJ<40pk)1z74yI&F=A^?;bNe{h;K`SKCCQ+m8Or2I8X5~evYxsDM z!$)Ef`fWIrC?tsXNG^Cc(E$i#Yr8=zgi4zQsR*e8su4T=2hmf<;h%tiK)|@2aPQyzuw#u}AiGs2f zu_tz8al6daZsR*0ncNOY7YEtlfZc)c|D^YxX4j$OYe5J^?ryp0g4PrboI)ZCd=_B6 zBVB?s?jm$6A-|Ecx%mdN&R$iSsR!{ zh%`F{sOj;+r8uC{{;N6;aU3&RP|o+7jp9K5=x^7sQG3k|^=NcMI)H6Cy>|)7kzHG^ zJOr+z<#+%b!i?z=x1flRnk0}1Ppci{ii?b+6g`jeeCRF(76h+mQhE;yD+rx(9{scR z>U*CBkguFEsRqEXgn1#=sUTBjs${y)J4yqIZFiO<`nnv(H%t=U@xm=zDSYLbbo94z z6qFphF1H(SK=5=Ro!WqMn;hdUXqHA61Cpv@0-(harmUzLMQ1$$Ii?RWjq{8KYs9Lo zJlejDACMlYva~a$_=$uki}`H4RY0@2-UCD+5OdC3;esc90;?EtBrVoWKBPzCB1*(0DN%(o9}cXmXIh2gzbr* zrI@=A_Ks*#Nk6)afWQvYUh&mS!!r(@cBO~cY_6Jv1daxLmqK`2yY`joKv~Ex>aqg2$G0U@RnlsB zQ`2`6up%#y_Wb;9Odfr^Uca0*#ytT?n{69K>pMD+Vb;LgS}!8Pqf75K4sJpMnN~N+ zI7K`5P3y)_0uGj`8-`U1l*eh*KmceCp*(7k%9)IUxX6U;#GeMuEv_CAL+}U?^BBvf z27RD)?_P$tJ4|d`Ta#Q%KaWp}%48^*S;e+3P4gO202uW8Wm3Wy9zYz2dFtU3)%et? zLVi_YLH0b6&RgIpV6kv}I2=!|O5(Qz!}BO$9#?j{|H2D}@rf*1WNpBWU=-_yG3hD< ziLz@5dK^Wp^E;4bCO{*DD8m^ZSsIsqk_5Nu06<>%1gLu0O)s+)y_+DouY4t_4*SBR z=J)D+-pZ^Aa&N$?^yslm&lqy*Wk+g~D8R7IOxb01sUFS))ZWWzUpHw)0aXe@O}q`I zC#C?muc1EZ1Wp&>;%$;0s0lCQhc8SDzw{`1dux9hC~0a?vO%5IeMyL7cH&v)VmB!A zH1eVuaQn_%;4)B1cU;q+!j-|%@6n*Fx-l=Dnv~;Wb~t2q9Dg&uyPtpw3371X`Lqt>mDD=!16sfmZb2Lg~*?C;S8*Eq>t zp14anHlRO?TT9e-6H(71Xe;`a5Q~!2N{c-DQ9v6-G7L6sZUZ=0&5(DNFfP+H-+BLx z(c`9pE`r+IY!+McZGhVo@Ru^LZ!s@2eqC!=LmP+%hzJb)mK8IIKhbmri~AX?;O~>hH@o6J*-ofMhh-qACSCP(B0rH8EzeeyTZO z`iC3pfh&>uK~5Kz%N`WlX^D3JzjPY}{8$x}7C2`VP@LmUNmEkDzp= zHNn*C&iNB_9&ID)UM>yG4m=>dc5)TvX;k>NX&ZqPOqFl+Uu>q0B#cfO>z@!lB0mqb zCgE%42M^MDDHYeRhXn#WZ^mZGN4dIZ$N(Kp8u{A=jP-l&x zLhJ6v39m!KODZlW{u2VtqCSlEWh~boyGH}H<}83){28%Zonr<4K4HfAaBDJb=#949 zzc0KPDM-b0b>~P_lza0)Ow-L~+syDcO+;LxI2BAM*ep8H5SybKDg~k6#7iKkL>CcR zv?)?=PFs;R%(lhlU(|OFf@yVv0M}k=b*BZ;Jcs>9_RnzdlK|7SWS60Sez<&t#|Kzc zsTr^dPjVGVXf`RbDT9|4%O;e38-E4Ak`x*)bRp*E@kv?r<()t(5-xvQdxDN}qhG*Z z-9=UuV1StJ7YW<-GyF?}x793@)VEOnNr{#@EZncujDK*vvn{;c-TslTe8gBT;|G0K za!0QeD$>bLE@N42pQQVikcfos={gjr=+P=bzNwGO*H;J*Eo$k$NRPd!`77kAg|Vxt z?F~-Yipd~RPAu(QZ0y$C_@->apnvu}?)%xEwV`1l{h@u$2nh{$iXyG24A;DY!h;#} z*>d(E!<{0qaxMc?R|wB2V`P^X(FqJnO<5PC-8N7#XaVGFdMJp!vl|k#9rM?(9v#qz z2_SUS81(vKpjCHcjO^6sQ9^KNHHeg@{7B!Eup1aQWUMHzbAh+Ba0Ft5uy#cMG!_k7 zYgpyIhls49sf(IO(4P4+ z{ERM^-!cl_O$R{6QIDZxYTWL0V9zypQJb9%S|D>dJU@rgjb^HUgHi`w8N3YUXUM^) zEsZeDKSC6o(d7^%04&9N=sf<8js_4p<3xgLy$EnP_K8am2=g#F2BaS@^LhN>r3GOH zvB*dUgPJ}09kCYW*33FWfB~Z+GOYP48&C42jyedaPk6F488C-JZLcc>zN$4Sb!jn2 z%Oq+>UOmj6v|U32;mB=$v%$CDh;mHf>MkUOu8DjwJaD(|b_8RXtDM^nfOLav$kEo* z_`ym`YeeS#1*;h=hwTbqJREDFUyWPV8pE6hn^3;&n_xm1uRg7_<>R~)yfR0Z9uNX) z7#R%WLsL*H)8|D*kkOJ`0u%{v;oLUr+?##_s)Lr<{NdyU=!L!sSTQxw;%BPqsZ$I; zc+<^$k~*Ga8%AtuL3)DB;W}d==pFu!1xsKz=gg#yWKzO;wH5JJ23d^+9uWPTkvQ7e)`4Bf~F(1hVni%%kY=Wuh zFY{q0*L9(ThZ~L8;4Q>yAW8v80SGu~F|p*Ai7FxU9!c8GF(JN{e`7+cGI-j^S&7L)p(hUv1g9K^6scUdHqp)Yc-MQfG z!JEHBWAy?XbqTW|74D!8KAc+vP(aAhO7k?n%QCo`!O-$);?Tb#Lhaj}&v2iB`KEVg zCJgBAu+8-+c=bmCD|*Y2A)pxrym+QZ_O0p|hD>yui9NeJE=_YGWzN_QaJ_E2o|5If zNC;jiN^R)%#*SN1w;J|B6(Of|Az_-+WdN*^0ZBbRq^iRPK{jgbIDRyDrWYU4U3Lit zQ8WNVGeJ8_R~QouPb!_<`7pUJ;)ioKy3L412Pgkgg1Nuk28TLAI-L|GKq&onT^g;} zoOX9Wh5Q{N&>l2QYIM`tHwhz17I=$6lS2&vgR8II4Sg2`;f@YHA`L)?_F@K=dI<4RCaflBqL3OJF_~$1P4Dr8PfLiGHygy zzq~Bmqo|0=%U#6@&jia) z6E79S%bP-^uB8SDM;Sc8o1#+7vBK1^yw?WK)PGg@(4|1;RzL8KKIOdEZBK$g^pHU9j3?qlDm);AI z9pfFiK?S|MF9Mt*+}J@BjL?SMnFBCy_=+%`Za+UY&@ed_{Of2>;l@0W!qC00oX6j; zSbZyCa=uYu;c0AgY8Pv|Q#tif#w0h%-O~ism^=!237d zI*hx1*6&4p06*ANKOkX<+7!?o?j3a!QKMlO#2x4jO6VMMvU3Sw!$ou!;l50nmh^53 zMLWIXPRKG#0%d#>;7w0Ufnq#0spaTKVJ3axFKM~bjc&T!?|M0)c-cJC3k-LrTW?wy z0Y)6@MA6&`MstiKXnBB}kfLs`bA8rY7jTx%$RtZ3z)h2AT2$_v1S{#i-N>Kc4j zfr6o#8vZ!AV-oba-X#(SQ5Q}t_7^|2n^nS_Pq2}!dJX_H+EMGhdlZHrhBpTWvFEQ} z4zldwQg93}UoS)@j8|CTzldaP3UbzQ%pL6^Y3T(unmI0sPhd9D!-B%U2o-TDW0#NZ zMq^U7)X5OPCxmnyoG=zPCL~-$bl?#;D?R`e?KA+01U<83!ihw~KpTPR1Mx@Cb^2VH zUhFh}bsy15i-isatvhK_bVe!^YDEI&js+IiKJ{UuJY!yTnwxbkC%2r(H^nF=j}E_7 zxHW*UU>5*PFjINKbimyt6>kt0U7{wm7t;Xc)4*) zL;_9=8^z5)ni8Eu2L-n0trAtW^}RQ{JL3T>#idF9Ga^yi8iT?CdS$u(X+h;o)pe(f z#&P_psD-JMO+)adI&0E;1oh@JPZk+?BOD<=s_t<}#)4=ub|~9?cy9z7vQ@@Mvk`Y;jh@*>*Y)A6)Pz6M}3-S8Z4!CHo{ocGP>I zsyq1)2Z6c|r&~ulvka8D9fvnbP_k_{43B`}k%}+Zk-$lU`GRqarFIeKKf9E|s_KZVwWwL+ zC}4x=S6VrEGBH)Ta~D*0-h;!)0F7NJH9e0XElM4dBymg1fg}e#`^;Al<3Esa+6);Z z1EC4jSR0JUvrXl%!@7E6xvLDsEcpG;>Q~ZLc!BgV0OdpI73N}U*`jkaEUXX+EkoGP z)6E30VS7>Mg7bU?LWC|7t&>Yc9`ywEA3JxrA4d?s}{stz3 z&ZUt;2{K7DDp7hz0r+wYV<(;SXLSHP|XRyEEh(eP{(w#H0 zHF?>JS5;eCw~i6Rb= z+>A5ma*gVpgLbdmiGXZh8KfH+1AyA_{8wK{QU~KazAv0XhYS#=aN#-R4+%Q`Mms&9 zwhLas*1$sqKO^m1C{uJt2lyKKn+S0dqo)NV_Et?uss@R3sW0(A?k&8;!z@D89J~@C_@Y0$^gts^E6M2%sG= zN`Xi=9k6Ul0BQx&S}y`t^sh%>W!D-lH(G(sD4C2N&@h__G-Go?pFQ;tV=|rItfhv( z`Rnk}-wc8jC-QWk^3Bn7>m8-n@EbYU0G^~vDAF;C5u(pZkD3xZSaeAyH%{k7imRL1 zS!v5LsGlXuqQmCq@CyoQ3?2BTqBTST*{R>u2$_?@xenF($ zQQou2APOr_Sj-L{H&wq|A(eUnU+KE>V*fYPR&Lo9X$_?}lvnI{fF95KY3F z(@jet$c0=s0ZIuj-HHE_vwN-e?E^eeQ!Tmsh_gq3_Aw%L(%ilZ=O(!#7<@1GJIBuy zy;1V7?}E39awT`PK^X$)=d`px{|r%QD)r}99|RD?Uo7)2R&Muhnv?_@1^iNMjKbUeqjPIYwlbAFn4PI*0l<>3AN`$v;!O*D0Dgz z(%0GQpPa(H7pxLCPp35G>>wX{KQBi1jGo_nE(%rzf zMCYz2x2zGRT(p`qNMd?6qBNr?V4edgxGn;sIu2rHhXVXX4c;V3LWo( z-lWhg%jhOB=tj|r z8KWM?TZ0DHm0|Lyi-c$I=CD~myK)aK{Z-8B3D3;&)W zkW1r_;zoIc*WiU=C?B5@3n+OymoyIBv&8lF)5oJjK#MU)e;U6N=z_xWhhMkQBCrVN4P53!h^zoVVm{Kt#8?f z5WUhWAD3M_ts$RAqN_0q>Dn}OFpWgjcK~dE;#xsPA((De=y2#m0)gS;( zSeO7bcd0)Xnw>_>^LwX>< zQqDp*>j*0vI3y<;@^F7w;AG~_sd;fKB5EP zqJv|d?!5L>p)Q80Z&PcT%i8w;adxlCk>ggkK>vRco{6G@Aou`B<1fiPFY)y0_veU-JG6BI@&{@VwM9r*tg%u9A)JmkKjWM!ozb! zxHO(0SIJx>-HEf}{AoT0Uh(^dQ!@+mL8PhE$g((Wn*C(S)uU@c_c{E>W`D5Z;Y_z+K!IgN2)vx(@Q1(}8Kw@=WWQ?bI zj&13c#^f+|CL>}!zl>Ne#=uT66jX346WAE>Y|6FJz;|SrEv+g5dCLZz$n02iX%=0_ zuR|8Jo4y1h+J$eQul*1GFNv%B&e~}nMP~{)3_CtH7X^f0M=bHW9>H}ZSa0IrxQZRzdLBxDQNS=`#| z&H#`yBkcY6F+=IMr85niw-GZ%P%^k&k5GAzaQdNu!){%$+l$mc|BF_*1oB@z%(B+L z`J%lhW>i03(e+IJD_S zZAbBVGtuhNu8*bxjS?32a4ZWpYm2qUW)++u{UwgK2fzic;MmPL=J-=eEO>8o)g)gF zf-2&87qoc|+zfhlDEl)Kg*VsF$i5qk%rgaGwTzv92n`io;i4-(GBu8z2VYr~E|9YJjF zng^HMP+g(|7LN7x@OWFs^hor|OU=HsOx|aGb)A_O(c8=Q9lwXmd3B4E!XcD%UN)(x z5U*)eCk-1I6GqD(Q^_~!kc!cnFP$oJ9#ABrhb>qmYI}SN?S2up{+_O;f>m8_&}PGI zH+a$&Wf9yb=)Bv@k065jo4sf`Zp|h1ohNQZiiDje4QF~5>_A+jfl-jJuloqzVpBuR zE?Sdxq{=}jIt{O1DOwEt^z7F?N2Cfmh`3DA>0l$^@Ao16}Krrq~8N4{|)93YID+ z7rmXi!2^fkb$qPYjQSCKlxT8L-$ocU%2|VLfYDgm$0@n_>Yy#hQE45u#Us6r7?p#F z45fK;|jlO4~PC%FFw(sB`pUeigqLULa<)q8=BjTJgImw3WHeutW^r0O;vSG$Gk(QWph z%+%d5f94&O09BNcvs=9P;A!~uxO)#FbQPCi7jaf|SQSQ`Ut!l z9>^z_N5J2nHHM^-zZGi0?~{ZI4_~~81b1CmI8|EoT4CwXhEv2EY=Mf5Zn{YzVP70K zS2hpaSxyX7T_ofSVlD9TW6&=*uT-drY16z1D%`7D=0K>4BMyP$`JE3)SyaL3kek0?Me}36Oqb#0!A=;lj7`;Rdy|0QT@c ziQ&cFZw5FOjp0L>U%lk2AcMiqX51e1zY%z-x5qgi?oj(QF49j}6cv>^cEcYqLpL}= z1a3w6HHTi4P8lT{jwqZU({{KG5CZ(lCWZ6S0k|5gDtVBJB^Tas;dfG|WS%y^dkqgG zoys{J5epRW`@vW;6^b_&0BTUsn|6`$~y)Z@Ks?&1&@2-Cp z$_SlXnX*XU1ke!#NH}QlmqE-C9>n5L(p!WEr-KK{N@U~y`eo8XWTOR;Hj~-<26)nB z?gvPG0k~vH)H)U#-O+RmkcwV|o7u<1cc=`zwV$T#k;#)?b2ja?W^m6g9a0bgd5oK+ zs_O_C%a)D$Ho$TBkg<%u4QWQiT$}4}WWT9cG3xOJE~GY^=`zQ0J$-@nme+Ub)J!#$;WlM25%*x0bKVJ%ix?lz zji|{bJ3%IZq=(1+>dkc~qby7q$%R0I^1GxQU5G! z19b*!k=aTMeFPr zDq>`(6c3R+CK(sbTwaYhkhddp*{cY|93r_KSMP@aM@bTQurc?A@M>8nz-I(pNUt>$ zia0%nCNs|;ir(_5rC2Ox;kN-o5cPDmRyuNJd=tixj%~r;Lj2?ni3HUQ@LKIe# zfM2X+%&!mrmCDU|pwYIM65U?^jO@hq=K6YmoF}NXVrmQAsNri<1=KC4ycdRl`MN~* zF1WJ0tB>eTfRxXrj*LDpWy-UJtGr~klOku+=pkg>ucW-YkF?-GF@=1%`b^M|IEcL& zA)mo6LXy2;WvTej@O?PFuvwU|wEbHXkTX*FM|an!bCFkPrZb2OkHLGLj2rM z&$vU3rvQfl^EtlHL~p-=p~%TxL0iEsQZ=X~Ivb1rv^?otm=wM#LiGCpG~B#AgtJqX zY1WVMQ)6fH6kwPupr~{dor0!JrGLuyrgzx;GuT`0w>hrKGO0brWgM!Dx*kq3nR)k z6ckxMbM*N)1#?QybK73L=4TjP?IA{b6R;Ej_$OH8NOSE(*!=nx;OpiVa`t$2-4&pw zK;buP$3K0K*g)WXE^29R)&aMS!`4)SW>D8;$Y9JELKanw#>P2GhF2D=xx4)x~ z3*x9--U1RaM-9Xc@YJj#Ae(qJ+0QQr(1v%kJ_YFoC|uuC#j?hL;raa4ix9L%HFWR> zje9x=UsE+m%lSq0!^vSU3R_- zD8+~vKik$Xvky0hX~$kU(xDg+F&!?k^zijXnx$X5p)lf|mH?rDDc#-m(`UTU(xJ7v ztgEf#qn!F=RXmc$%k6@pS#jQp5S}5&8&7IIKOoM9~xmx+zZ?CZkFS@g7kVJ zqg#6T%z;`o!Zjd!B!&-2R~qxW0~jsnvqYtf7BpX)hO&Xs*ZWpZCa8l(5%S<7%0{k< z$pbp&N2bDNz!XL0ws)70zPvg95v@fbpO=-OrLz=ZHn6P>=PV&F-eu607c#n|f zy}S6xe1YHnd?n4(7`V@$2wsGpk%yEG9I zbS|?i$S9;xvj}pQw@R0UQ|3JCAcLow34QsD{42{ws!n}DA}ug zUv4AJm3sX-{^8dUmdiU{FWNkkM{d!_c7^QphoMt8Y#KxR!&f+sceJ6>DTd-1+1apx zG^RmnOK=AEe(1I_jE=bHqV?!NwWinG1r)dFKWCllKf$4%s%#1-4Av(Y0m{GhdHEj9 zcl-9HcwtYPK=XTnA_>M8lbftVJJw4PbF(H$_6w4djDy>{9fl zWyY0*fJl3|d{{Ks8)CXZ+6Pef4h&Atc|(p}9JEm%a+)0sUI(cJ0A0)eAwa3q?4KG1 zX(6Xqxh6e%Ngw4a9MG@f5pC`k7CY_la!t-@J-lbYb zx_uYlgwrg(X7yieH`Tod&AoFqhSmu)5%o|q%GO=k`csH>NoSV6F)uV+T%%L%CcOYb zK)%1TW?6Bs8$l`%3;TL}YrzBL2w>CV6Kt=xO z;Ol{qzFRCMQdet)ygp07KfCcbAU{Cq(dkqO`0@qq&*^*h-j5;+gT;%!U$y4#R3IioMd{;JJG+51&8dY_@gU z3N?U?P$@X7{vsKrH66SwYPUPX+W^8r?2TLlmCv;7=JFA8U7TAnI;ZF4^89H|db3oB zg33v{7z>G`bXGS$B>AuN9DexlE#q6KI`%hMkoV`HX7ZIz?CDT7V3YYVThz>adI~y( z=xx_}AGgkIpl_^xRjb&yv-sEUWvH^3ZPnfNkGs!5%cnPx z)sp)&zY2A3xT+;*`)SUS5s=o(!|Evw*UtK&V1EpY?gCUtGgR)Kgr<|M57g=HvNHJ}~>WJ17OCoCu`PvJH7AloleiBTS4*4g-9&us?> zI_3e|g>&0IXQ>q2#BG4=p%7c^*WXcp7^WA;S*FW538em9()K8BxJhh6D1PB_N5u6u z!j&lsSc6X%ModV@YdC!}j4+SFDq?I@+FN_zB!sMPFKYK35z{rEuotQp``F9d(lc5YNc)grEI~tlAM*Oxfq-P0H%gD8eT7q(X1w$}nH% ze!lKrTCzTS+(?*er$i!$VocGi~`7c8zr6)PQGJEtE#cC(c3NoOBHSExppSSe7k5K;J3*QeM4;}YHo0W4Y zpHLrtJqzMAP9&m;in6o2yJEC2jxg0{E(`pFGtxrGZ87egH!tj`>xgK8H*RmrXo}$X z7rnkON^!tEi{|ipl{YPEYr6dN)rn-S{&vt_7sK3bzrScpRjeCNfD8XFJp`SAo^BC8 zqn2(=q)r;ZiZdp@Fm?dwqeH)K-5x^J6I_h$=Zb20S!s;WRX@3B(u#a(o0>IaOgAvn z^cD^pno9)j$MF>6C_$4g`mXxbP#Tijcde0v*?PWA6&n*n@E6GZaeQ$WPewqqH?&LiL**Dzww`YM(tqd& zkXljjZRN5IL z^+=t(kxZ(?aKe#gZn{=OaHM%k%B!FIzmA-4sl;`Ke^_4*#0$guufNj3Iz~V)m*TCA z&`HD0y-9B(#s~j0av`;I+ve@GV*owHO}Jb;>+k*Ll}Hx>LxEr^`o|gIQ})Wf9{)ai z%{~4(#G27NCyj`fDEnB1JEfNt#x^?HJU;On)sz8M^zWk|8eF`|0UqmT>BPL9;XRF9 zRjZ76Wjh?R12U;r`nu+_{oh9~`JDwjiL3G%ZK}E6x^ptNR@+_!4Hn>1l^#bdS&H3a z)e?gJ`uCwnX%-S|L^a$92hrwp_-T|6VAL%2F@hyQ7A@gS5>|r^BtTv3|NeW|C~ZVk zS~r5+h)LIlWzWIs%KS9Mp)2H~`BIoM;x>+z+EV1auyAyWh+}@7cpftc`sn2VhPunZ z&2m+SY_m3$ki5KxkMLDO4P_8d4&)Cg09K`OciiD`uKu#tO@$pf>OHlt0S9FfmYc2Q z7BYZXlA>g9>PK1{#J+k`o13c#Do*xg1RAMBT`*;|uT=CvG~eXx|+GxNlA7;xfo}&J>v^Is%a-jU>kb>ezK3;6?0|M}GU#&Hy@6 zBw`iPS1-rj5kK#h&Pr-RNJvA2j(#)LJ9hzI#>{vv4bG>He1q!^{ka#fbJN}-cNFeW zK*UtKqFSKLdiVyhZejpqw_!ndg}<`|xP6i{=zDZeTMqJ#dq%L^IHeCCI&CdfwOC-9 z{b#{M=VqfZ7>?MH(F8X2k|fz-#*XaTLn$2OKJKnQ?$)D&gPG)(N$b)|{)65ry8@~Y z$#S%Ww?MmfL~gJDB3&-uf{|O{#_Uc&!^9Y4d1FA$mL<(mOA?Ha7J~9+zylys1PV7d zw4Fi>t=lM4npok8Xtx{@TR?Im8ZwfGD@h=t#}+72Pp0YKctXZIeS5losW*b9#J^!n z>4BPWp7Cj_8(?@wAR4^nLyYZZP7r1zmB3SgqFv6r5EV?##u9+1ZNrv?(P616V|p4< z?uNG!2)cj^ksq_C+y*NnSL>m z*d8LhgfL@y{57W8GUDx;*Z@mCxM!(pbdIAR6b7-;scw}|*PjBEA&<@*<}VUbfZJrz z7VR^oOwjGbs%bzn3#F6(N3*DtV^V}hH~&6DX+&xce63Xa0ay!p**{@FR@!_Sl?$4iX}#$M@B2I{P-oZm_{tl zPMD4$lTcUqLXe@zKMGS1C_7g$eTDXt!|a)Gts^Gi4>8I&4OutFK_fbk;mG|1M6Zay zJo{h*D6RHC_97{eom|IHbAO*Va-<`;WU0itG<6VDcx^K3hrx`015K30gDEo~uhc`Y zY&K@@0khETf4KgcL3cn9CMQ}3HCUgOz}Jst5Hq8N5ntD6Jz{$pR&&XY1z*2URN`v_xC>r+buP{r=uks|1Xcbx`R# zX$i1UMz-y(^J^}DqJ?{f!r>sQS=vp2vc`!Q!9sZ_PGr8qEHt4DS+yj96xWN70gmip zt6;2Y5KpV*>H1O7)5Dut2~_Wh>>`2QofPoA&s@7I0wV^Rl#7>!#PMO;PJ#@z(ktSI&&p6&oBlN0g79vb7Z5{CIgMHG$8b=TJrKt*J7&DpF(7%%o2IU+_>mGMSc#6t}W#^jFMBt~f6ppED*#QB)nJRlROu`Whq6|MB4qymr zF!P8rM(kOCTF8AZP+q?e5CYfwV-tP#YPTOYNytgZ9{&{W-;qse3AuNpAh(s=_TK?Y zA7Oo)ztYvhQqip3*ciw7&y6&Yyp%r>sYqymf5eWQd-GsfA4244iEJLN?u9;5aWLk# zJl5FVvJ@Azbvot8%)RqMQ&hR&FXw7qmWPK>W~QrLzngiNNl5g1-|O%AE6BZp-{C^y z!m%#lWzC0o?B&OL7a@5NZLqkFGhjT52593R3*P7}1fp=%WymR?iEwBUhZ``@m4}sD z5(%5vSW0rf9E;9Bmfy)tTqRaU?2LrwtG4K%O886_=*}bgVM^Xda6)qzoONQOX(y%I zVpB|k2F+`3w*^`-K53bt$Lz_5g#E0GN=xzuMCnSQS@Ulal`?OE%Hg)%V7OBP2PjTp z8D&2|_Y$;?QyJx7XgV*nX&lfQ7+7oDZG_VPUo~&LlDnF>5@i$e1%sNb8vcGxC|Rbi z>y{jVC?Z{nkqPt7(%=eRWCQ*Hg#`hr#YHEf_kzN6Nu7UAccf67;K>RH zykIH7sqrtbrOXiVg1Z1|;!_mxbV-gE_ExW+-gD*{^`Hm>I9}($krzRo*sr|Oy%2CW zm(Lp7@?2+ON?*jfyPSvt+j*7T&s!a?z|2Qv3( z&0&o}A7+oTg=ySc5V*NLeFS*8o6Y)?yXok7jlLFpGWmMi#WcnZswvkmTtGlxz*KLp zd8P+?etJp!k-11DCg-MrBneP^fR+Y7nm0~sIw0QLiw^L@AAK2j^@!Te+zxwEp-W`8 z9|&JPDqm%~LKVMhdS&c>8^D0%&%4r2M8tK*=AJa~pczv8G%uX5g#b2{!Hrr)@RLY6gg zL`T;0gx)o%;=0m2admCX6fDWBFr@QqYTK#UA3i_T04YgP2bg7gWl!xEosT67zd`!u zd~Y!&h(RSC?76b|IIg_C9s=+&`?HbM`tq9G3UQ19f|q+l(WOO;j4T1%L{KL40MiAK zkr|EOU%wWS*6PA3o!?!yTnjoXVh)%rU(Io?CmKI<*1)-z`Q9mN#oT0$=rW zlc=-v>AjpsdvG{gFzsB+>FnKKKZK3BM}ehpp@IdYsWjBTj739g*%8RZrk1$ST&b-~ z)ETPV=R?@}`}(2PSlEzTaPxZ@E&xmX?(8=DK*61ZJj*kqVybhqrV3oZ$J8^o0j}3r z>4%JhiRSroBQh3mVYIH5j%zpVkrl2<#qDKl0`81|d8{58#>Q6U&3Uz~AyDxVM%etG zN%$|B^hgm^4#zhUUW(){R+J7)pLm7w}(0K{pIy$zAI|K}sITkQoS4 z5n}1o#_uDPn)yj_g2kYYQ(VoE{D?rAPX&z&U>5kyNVwY5!cQ;?l;WmAi4Mq#z`$6NLJ?k=15m9Yy192)W&uD3t* z#hBK?8j{gCnNIgxtBUgj&~wT0jz3HUiT5&TF4u+YJ_EVnOn>g7roQtef z@yKas9ggWkXD?P7QsB~y(8CH)c?+Ro4Jr1xyZ#GurXIWEmmMw+4#C?Ta;u&~qibl*mQS##m&j^dRt$HK9=I`)2-9?c(;CHT=*Ch-Ip5aUR?%jV@+gE( z&W*vdZ`#U(gYGbhmN`Us_aei1^!v+4_(f}S?ir0`mCqWe;fB(yuW$t=5?^_Gfeiz< zXm3M5da%AVfVmHF-F_>A^`3*@uhzm$v^jR zR1t}t{Vb|iKEH_tg@2aGEtHxS_aT~Tb{lop8G#sK z7lhu25OJBgvL$|bfzAkZQ}`rbQCXg+VFsnZx`lRR6eP5%~xO`RObP~Nqc9-u#h_4ZZDh&3Tf|@Ann%ct--Lvpf z!uipC(0?}cESM;{G{Ch?z>poxLHy?LZ6{|VpEsh@JK-fH(vT(fzOKOE9wL+?fda5a z_MjgxbN0|9mJ}j&E{^2$M9$;`>QBa}@iT^3aO&PhNQtWxGz$T$MB5RcE`p_Q(Oli+ zC~dE1)R*IJ3DwQP$SdNcTfow9eTY!96R0=2K^OFPz5_RgrAGLLdGzkaX~8=yyWzFH znudb~Aqw0EXfJ0#ex%?$CeexD#3oISi*v-y78Yc~#AI6TzS+}Ev7H$+x<|0f6dlI+TL7->F#lD_zlNZOHyuWdt&pL5E9s*Qx!IsG2 z#m4KirRjO~5MW?lx)@llENWiKTJj!BPX z4j6w_1@1$<(q)~lNFKFbc7z5saPjos&rcUNCOn;BbB~GycPglGyeQ1hhZwJEFCTb~ zjeR9LRS*k|ELEuCTFO2e+~#&>P`E`fJER9XGxQ}2&U8VcCL~ln?Vzaq9T96_%5v}~ zUL!wd2OyfyqaY@%T^iKg)K`)>9`LN%M~6Z@AfmOTe_2tnf1ILber+J z!RXE(0sU&e<+|URZp(5O1N86f#|>pxsE0l*Od`}ck=pjweJD;D`d-@HfLJ}<$&&({ zhW8SJ`(1#HF3&J7R!-8A5&?`%Hc18gK$!~2-WAw@D3GSgEnl48ZI5}zU4XWhoWA}z zhyhr5MC{_%Lc^E#VMP9;OT+G{w@5qyie?4sf^WxDfF2y#XBFz_T$duSF#57`2!JsP zoh-mfnSMk86X0SUrQ7`wAahXShR5gM$HOy#hTQbf7JC-rj@iqIhedxeO>Y96cL8>g zZb((+1PA6$%mVCX#d_DTrD6b9YbL*j0{9SM+`b2p15tVFPPL_QyVlso4cfCR--8}X z`8-w0tKLT_7cYRPb9wy;VAGser@np$=6TU^QtA``^%eFo;7^B!UfqBGFU7pdc#RZx zJJ+gyx9B^M*H1?@w%0*igcyp`;^KLgDG>*Z^V9S1jBxAH=EuIje(7}q`0%;jBJm20 zWpK*)y%DlW4`qo(cP6{@sC7a|lhv4Eo6D3|R#%qd! zH3EWLnlvOP_MO^B&tUa7!1WM7*LGV<)OgAH%$8+7VdLK|QXcd9VXCBGM39>=RS-Qu?j z34+E~-j*DY>D%dd-(UVPAxcF74(E524i$f+Z|Hc*t0+O`d@U|IwD?xmZIJ*Up*r0~ zILl6%`*P;cgKvC~n4&rL5qY;4=ZB9VrO1x#qJr915^ejE$;m^2mjKe2Tl2^ZY7(Z- zfEfB>APJhW4$ZVWW4T{(8@C7%_jp?C07%yQuES*K$u+wVoP~+N$wf|n1g`X zZ8!%`g8S>AMIyWQ2oSYGPhN~}*6;a7UOGJwVq<{Q(eK}=4H;I>r2w&dAE0J0r2;kY zUsRv-irJ?zVVB4k<&kfc1+*hFD^chu0^#sF_3XRLUuA~ABRt3dYROCuSqu~dQ0GcN zXGMpEtms=@OJ+(7GX|Sr=wl1IyMDdGhGj1@@%qx2)%t+z=3t(V{X*#ET*>o&8FTHm zu~2u6_t0H{>@P{8zkVgaT-e`Yd07Rk`bH@sX$rVl1DqJkVFPYY5sq?m?^K_l$(L#X z@^)~qnSh+tz64kgvwycX!zQjX_vqbnl2YIPt%ZU5SyOB4Q>gwRdY#4La zVCzpTzPZ-4i0?i`s7kMAc!JsP4NQ`Y+5xWGufBcq5(f!N|J$Bsw1cfh*{kP7xDQdn zB>#NZj2*oUQ4VQq-j2bfMA3^q8b`hPuLt5-xtQyA6O5LyS53YaHu?;TFB2$)gHjat zeY%G^h&WnwvBz;gte3l!JCeZUb-ISw_2u`Wei zz+*MDMiY)a(Q$wMY|Uzklfafa(_Vr+@94)olzH@n%NX#}_k#SJ+w{ufjUw#B_0#D$ zzAQvRv9JSBhm$2Qw;h?G;43j7Z1BZFYSJX%Sv)I9_lN73$PicKK&qrpJsOWxYp0NTZ=!$@E zrpUaDkk_hZZZY7LAl@Q1uu~``^?5Eeq}+oO+lM^< z#HE3k5t)js=JThkoym2Bu)hm1N}+)j6Owt$_+f*nhC7KBblk11(|J6ydEq8flAXZ< zbgfTd^Zg-4y~_I!%6HmRkuCoGk!O!AO%!9%E#0bZ-J2JM)x)x9{_L?th?2C{e7Nl; zg-I7S)nT)O@zuIDD$VA=Arbjhvwx)x1fTA|IvGaTgaAvbKXZ~U<72Pl& z$^>F9A2z9(%omc3TBPyeOzWUwXnYq4;P30_T`roQjtEM+J8)FVhnum)tCU`{WW^2* zX^o%I?=N}P+qF0<_}2C%&3(1wOtuhIW-3z7h-ol77US>OjO6V7bH9+*xBKgt`u(=P z!w$L85MsRO-cxanE805mF4F|1$k|;V3ubPj0P5Ja4*`j8|S+I~dJBT&X9(_e1fzidj6162NSiJ4;xir7a)R%ZJl+FLEps# zF}}t4tY6;7I$?%n@#=FuXjYazBgzm?xQ83m*4r;I(oz6AQ)QJuGa-U#jc}cn`SNv$ z9Tmx)!U)|2NRta{;oZHPZNg8!+o_ScAaBJbE1ZKR3{$kxt-T~!ZUP*4&loSTF0N+vLy%nc>Pgq z&!5_Uf3MP@Nx`8^hqj)ai$p@2*mK+49#`oas$|dMt`RM5^j1aV&#h`vve_NGQL%%Y zCR}u>M>F4w=h{3?J|fCK7hTZMd(aQCBMQ2_nMS3^n6d9nKiB0y}cfmDrGvZ1LE*4{!x9?%h&)S#704-$P*NFCJYmyK`Cc zhbzu=q_*hED8~+DwEOGBNNtdMET9d>yNCBJFp|U|v`-J<478z41r7``&GLr`5doQ595^iD%u!+|M!8PO z>~v?ef?Gj@sLK)${fNH2><_SN{?=uC2oaDna2KV;^9pz;Ed@T3pFN5!=k0=zi(U~N zyAFZ|HFW@Er|oH}wydROBI&d({C`#anjkx!Ds=H+@acyyoou(0+P7FpI{VnTfar0YCTsVa*& zuwGam+DpAeBEQ~_x!bqQC(N{TMO?YxC@ymRi# z4*}YG7a^RiHx2xJgZZj!K~dgOxy}e!7-j_-Xg@~i)meOlx-$3Wkcm|?ZyIoDil#=G z?|?(opV|{cS85IV%HaJeMx^~@A4;yly<(4I=xRoE*-P4%L=Tk!0av!pQ)CIPJeIBW z-k%ndSKlZ!;9ACnd%;(39t3@&k+%LWnKFQHh6QWpoKJ5;%SswAvM|lpnG)VD)$Nag zS1upWx8fj49(+?+>aC2BWalkuX>g}as-=k*xJG430dqWs2yqxGY+>k_o?i&1 zJk?Ga?>udKSpqsh^Ci~I^2-hwSlt?b3=rb5ck)N_ML5*N75YE0{w+4}%+i208?$bG zRfM$Sg$|wH`w&GPUcP~zy}ksymbey1^EDx;+aUeKj8(^LEJ5~c`CZ53w|FUh$O^yd z=Bx-K)jtnl{JLU4&$1f%kZr-$^C;%wk9|Hb#-;0VHMOHeBftY&P!YXQ3Jz`AQGVuj z(r$rvT8my|1z*=iEmLZ^Ki{d+FUP0&N`|ZSVA8X{e!81_sRQT)wma#}LFjLmZ~;p1t~v^8>&BRm>tHOni^2TQ91t<_u5Jf;e7o|_*qb*7br`mndqr$nP<3V znq!~UV>22mj8Z&hmb64QzG>;Ig<51(ul0BFiu1M3+}Pm5lV4vXq{w5Ams>NvIIfphc=gpP zX9V0Aa~NH0!@NBM?rF+%*vCHCr~;D-q3Q z?=$n4R>Hr(iWeIVXP+^j@-HT-gtosai-cjgg`}g}0{DRd18zn+nkQ9sv3?ox6w!dd zVgq=-YT2OGIB?icgh)>+EqEQ1<+4t(`)Tc>i&2;UWyma4MYRO;h&F5{aohGHs`t_k zj}EON)y2B2hzY|WabD96SKM7U4m9z6IUY2-->GQc&Kj*y_ zH;C(H#5&k-VQOw&I{XwO%Sd0CK8le~%VE2ciH1MwlaSwn!*-O2}2<{T8kL&))w}_^7;0)n9&{~ zOXC{5g%r^MgG%4$uj@x&Y{$M|Klw~aZ@%Ql*t(Q+H<`g$ACuw?eG0_|t5EVb)_xmc zq;&vHICoe>t1UhCjDPqYs+OW3G%pXhPb=q0BgrJm<2~gN*Y)rPu1x$H)2}q8W|{H& z1_Yiqv=Ki`8)uJY166oH=HLR>m;BEe8;9EnCGw|3Hex10@gakP4d!6ppt&ZH6U#MP zr*4TMpIgLe<%HDmkk^OnrviZAfyA1xLErLXjW3WPPomOr@@@eMYdGZ*Tv6mlDyPh} z-Umqfzw19)->`B8&O}d+X`4UpUGY-bg3J%VyE$St7)}&vpX2`e$^DUCn3!aJx#(jWVL~GeIUlC5o_U3K)eCv8GA~zAQXA2a8 z7qMJYNh!&@y?mwpHvD&;><#STji?w*akn8VG)F{R{#-0HXuVR>*!6CjQ51CY2Zf24 zu!jKvyw<*;FjdNoe}Dp1WH25Bi91-{qPXnl!Sn6hZ!aIU9*sF$&Jy$9Qb}pinj8}1 zu_RqQ%%5rbfwf(c zz3&?yEvJpz)7Q~o_h>FCSYhrrb6+W8LT6i#LhHu9;Vwi}gTjeXcdlRVqp3vZmB=zu z^2ND`^ClZEC)xRM!Bi|1NlCDDA~;dVF&$wd@2zhyU6P&YEJ;^6awt3tJA{5Qmh2Ia zyNeJRZ3Ekgxf;u+f8SqnyGyuJXSp$Qsk+$ztmW{l^UnVnJ8IsU<32(u%N$IT`#|eu zX0Du#^-{6hmu0QH7An_>wQV{m+T+Wa_Z?pBHbO~alRmX2mP}|EBIe{wFzaVf&e`Zn z`LfsAblxa5*w~t zeR!kiJ@eAVt?=QFIB=JkZLU%4NA6qZf=hW}t{hc;gZU-XSzvAI6c?<)Ae^>h*0V9V zy?nq&dpcnnlLqYvhK}Vv3l?Fw9FPG5Y#B7j*o<4*e_pWQW-p^Z&q6B%@9 zHR)ga_%qx096SRZ8k-~Rw9RPl?^}FPyAjpRB~vid*ma|EP-H86xY?N{#Q00nhj0cG zj%LMVqq~PCdx}Dmyi2$eS`jSoVj-*fJqo7_M?yStusOJK=|W_0aWUVi!9jwN~(%JxO>v zQpTk>m#1An|Kz)$YnFW!cbNAM(0cbwl!bjmcgjXp1gbd}?^LfMUA4eN>azqf61nEm zJo#p;_P9fUrc=z8F&<^4wW@R2$VDdi)ViKe0E{4l6Ze-7(qw#kiszThOXu|Cx)XH- z&wAoE%``cs^=f+oron8(KIv`04>0ZmL@|KSKJ;j#wz#Fhr0uF9JGaN!pA+$*n|tmh z!S(m$uX4VctDb3%fqqb>gc>?jb%Y`^tpOJ0pqZyTFCTRiGfg^?*TdyoC{0JqDqnSZ zSaKSF73WyEuDzj4Y*!Dsvlnks{_*L z104#IBjN>;LlE+LVo<%DOXD1W)H7uvy0@5oay#XOg^9n}86(uGe*sciZY6gC$|BHo z&Pt;Se|?;&6A-w#yH;*|YGKx4=#^2s00YnnPj!!G!b5p($uqmsRbktam7_4NxKW$h_KFDJPs=U=m?9wHP3T6RFIZxbO* zIZ_LFwQYL}QD$`(>mal!4ZD8`x<;mQy69_e(@yhZZi#A`K&|d0l*$EXuTbug749N* zMdN&^6au9%c@R1@^iueXoKFwBW%VHA0pCWIH4Etq%8H~E;e8SMaEwG!KzBS}A|{`F zFZB+eo^`#%#%Nfaa`$9`L;a)Kp2rseN?gmHXvp~}BKYI$=4H&X{xSW-;LHGTNNV~$ahDT>$Tp)ct ziZ~ZaCu^N3K%ci)4^C{*ys|9@qZ1JOOws54Y&44@=gq$kifV)BP{iZ^&B@Dy7 zaeMhXdc7cNd+t)dI48+(E{h3Ix^OMTPZcF6l$c4{EQm|XQ;@jxiu?_0BkHy}x`7BN#G9l8D-Q&i8e%-fptm#f!X!A`OBAF-`yd{)z!11;ID} z21z93Ayu1If0p&dFf)rW^_mKR3RlE3q%El1F&@9Ye$}zM{`OUs!x@xf9M;!#qTv9x z8f+{56`jabo&cLBBW8`YP~s$oI%GJ|OWcWdpW?=C8vgkgrqq0Hx*t0q@+*>DCoXv$ zddqW`Iw$q=Bbp!M;qqq+v^Kt{`Y}h>0^F;082u-8DnTIHSVK){#gt(D$?{++9Q93r zR3QL%RK~P_wynr67cxct>S@2AK9QpY1>fV5#GpgZoQu}7KSj7iFiAoxkKtsFD4&kW zcwktm`y=YpV&Pp%$KY&pRdrd~6 zA2lbguoc$6SV_7Ldb}#S(#*fR{`o0Y_N9rE9FRr*%r56y&ZQ%*O{7aGKx|Z988Cz` zYH{WjPuDNaFA|)~OQADH^~Voi4^Q4pNWqowhK_b8W)pHv9PN*%hfvP!@Jc)wUj`~( zEdnd;z^>l+QyaU=ku{~Is5`H=n_$COSzA3c38 z&+8=zHolce*No%V@8z)Uu`CCxi8DFUo^6{@M^u>o&UgIM?*a$ujuHL!SI*a^q-iZJ zG|3)N+&+sqx?%hvg7Trc#lPz)-Kc*Txx{uqXLm0pjFPGS7P>DxA(Q_#*5XIRJtBN! zI57y_FaP>WUpHSmnid%Z)D*L9G_~9=%nmIGk<(p)~uQHoG7&)05^c410luqSxdfWI)ANUS}fo_%l5BB zXLZm26AbN0;$1S-Vdi!-Fa3cMo;}P>QJv0s)TSN_cVbA2{&nb(d33K-6uiD$ko8Lh zME7WO*~$`u}=ym$dg1yPlan(lD{jwjkiYTxv=rd%b*T2TECe zo>ZC`86TOWNO`bXl=>g~D}>CmS*;XQK3zKS@59}W%59S+j{>K!(TMKB@{JTHr1lZt z<#E@;^=nU}=x1xY@GyHaoWr-e@Jg@3rjMoc3ZQ%gA^c-tHctYkDSIqxZX;w$8(PmS z1lA?oauHnL>+gUGoPdN*hhsF}0Ce~x;mUdG|2iM8U&HH&D@*y5;`s)_KwvpsJ@WCu z(k_Zmq%}px>s%i$+A<~O*<$vo)(IAG(XOHfLM;3&D8sD(lG;RO+R^++^05q<^bY(V_jb2FvD>8`J^2PJZ_ zz-Uja=k;zc-%_KqWIMI|6}$M*wv8g5a*2pdpb9!|5-7cfBDier=_b`s8?AGT6ROpA z_lli#sMa+aPUw^xRo-w$&<1Hq2FF@(wGZu+Bhu)FNMVB=_d>#y;k1}8;VcXXfAS3? zU_>yN^xkA^vl<#Q_9ZO0hwJAVd=1B=g7oTpN@R}Eo6Czb?M;g_{%B5EZ~PW-ZKvKt zfV{zO^ehV1JJHlUW|{sbd_KyMe109n_lyiiiM0@TK)TYy-X9|59p-_8{8c~81@J8= zO8~nWBUd>&5j~0A8t2QIrP9yc9wL-~JiHoUB24UczN{f*2}`!37u%7&b5KcXYe%|h zUs44qh4gv4OoXMWs)3{}85Yx&lY7fEk;A^nO>O=>J!lRkOaXvln!v4J2VK0_g$uykpO$Kv| z+dX=3U_EwtUu1-vTYjF0?&p1sPpz|S`T9}nSY9Mr*-MgzM)z@;(NQFf)|P*6Ih#GP z#|ZUWmjM^&XjMn48WDX73K5!FYkKaCM49;BMr;=BkZvD!CB4)C{2zH?9-7z3XMV6! z=ODZK`nRA!L;8FhCsWwIIDgD4N=37j3pJV=4ZKDFqkYU5e~q=Wf_;zR$M520_Q_6N zD+O=hNdIE)9q7L}cxH(@(O&F$9#yMXkMLMi0HUDY)0;ja=qGOa5O zR^&8PQeP1`2$zLgQt!WBE7d7Hwa!pS{Okc}%spbaG0*_*SIx=w-bO|XnxXSxP(-b3 zxDmg88_E!!vL@T{-68ps*x7;e7Ho|4tIjaYFheNbec*2E17g&MZ@K?E;t0fm&c>I5 zRi0#jr}4%sUI!*J-F9mIHNq-(_h2p~5cs>8#`k6qG=&#tG&3uVdU-qFs`?`>F#xhm zMB?9dkkL8A-q7cYf!wu5HlO-I)6=0C8f}`B>hsKOXPKc=#1giNtDyPpY0C%GiQ+X= z#Cg*KjOix&1X}=f_xk|FPC27+y3q-iC-2vvP$wk&Am;|N+CR|&A-{-Zt5$~g?W8JK}suus%tt0 znWw{CEIr9oD?Ta-P~$3E0LRl;7^N{qB?vSlo*;kSAUSWe1n8YE(OhJnVUiEqAEB}P z>$@TBfnO?h^JXr4IT{dk=8>>%?bVo86cN&0q8ux!%WF8O3ac@$Fs)=Ci>bib%9gsk z5x!J1(}KRThv(;3_n+67%-OrH-7NiYZI8#$6~O-Amk+gPz;~RV-IoXjX}NsyWX_TS zU#}RDt7PI83Ie}}Y(Z7zy1jl);Gm9}(O<+2OQ;E(gcJht)_jH}9$SPO&)~)LssMw~ zb6W22PH19^>@PA^=9SUcdSf>qu-FI9h8ba|m+=H|T=)2n@1@sqbNNzk+TjT>8!2lB zjbR*h#IL3W#)9Ov5tQsPDT=`?`A9`sjC$k0b{C*nLrze0gZwkqA`oGcZ$FB-B#A}$ zkH8pNMF`~iTq6O#-d{ckx7mMySypz6QFO*Qj}9~N6u|Q{&Ua~#Bt%Gb^!eAIxTrkd zU%ouegJC=Cl*!teTMB$r#BYiQ7?5nxz(nNh`ks#T$nYzMNH+mW`s-^bG2fiBX-B{D z>^PJrrVlXp2dS03PXJ}k0n#fCdRRYYL`|leAJWXiYWR}Za(n%3l~Do{%Pfu~-PWj5 zrV(_;-2U_O^9Fw%`HBGWnb*IK?d6t<|B9Kl^g;j8>VKO9>v=?Hpc?LUP(v_Lvm78h zq2vED-V0&3zKvcoqNUG+t;ayFCJAFHb&U(&&@c&F3hIgb)OPH@3^>XWkC-Dzk@Xta>lx;^OQtY- zf8A0w##?81ukXm%YpyKN4UKutUwjGb>gV~}MN>8<=k`)zfIAKI4HaHmES*}aL(kH( zQH`*DpXxT$%)o%dw)l0(;Q5X(lD?}%r-FIJ@`)pkt`3mR;)B&8&2?wQZCwj;{cXJQ z@EDo)Dsd>}TzQIm)MwPtcG5f+>Jh8rpoRK2ei`!RFm}A!*o(p`$}4lvC(~-gc0|_4 z*|cvs$w+h_<3a0%F7}A0+*jKIPEB4jck-)rmjRhD6qg`gLZY>E(N&IcUf&96f#sua z0}WGc&$lrMam|FcZsCsr^^DRD@rfx`x!Cx}XmI?QPw*O{+=C3oWyptgf z`A0bw4_)QCO;EazKw0FiwD6#po*sa`;pPjpC?j07LX*-SrT*-6(xmN?%k_{IO_|H5 z4fPPC3;=t)SO`PcaRDx6QctX6D(xw;NlaOxhEh5xB#|SkUoMSTq{r5bNZUh%YA0VWO>N2yvGocRkF2+h9)lDtshYGZxDk2R zKAt{z-e)tnx%(90W({)%YXLOnQeEWXlD0)t2IcW=d15XRNeYMS;n|i{t8O?|H6MB} z=a=A{=PQk)f8;AUqCsz{Sb;1N+o3kJlMmR9jWAHoEV2)OUM+Ff>$|^MHy>qzV4qn{ zw+R{q(YuFh=z_X*A-9^L4H=1X&G``Fd<_#wDs1H&o}spB4fO=RqrDuMsc)fI9T~9y zk$bw?MrYo|g$E)P8=g<@0$YS(s<{{Now?5<6&d zlt4dz3K6ntq}6C4H{j7ym_OhCs#k^-OYB4G3TmJs5fCo1_iazlq5AwVeV#X8g5@Yt zX|jmrW*Gtr8{zp(@{ZGEM^%Reveejp;pW^&NSFU8k>$B#xe1}iug4qRzOWDrMS?>| ztX(CB)S|=9D0G4Fzm4!3z{gWI0AE0$zirA3OIOknn{Ly!(aJ<6KCVa0S8?2Iv4u7; z#FWjR(RA==L29Q^y4qj1ypgoDgAv;({dc%Yz2Bvkff+ZLxpXCK{N%t$bjm_(InO!^ z?yg@p+dUQt>VmZ=)m`I-cYwf0{BmaozWW^)sg#I%9@12bn~*u<;rbMe2d|eF6cW@Z^JLgA4G!Yt z#gAI-s)rM5Tu%||Ojg4>mJIr@a+aG1@!aIX^p2)(G4jns^Y41-l zvd(ADc76Y6uW`|v9$l-YN}UhBw8;%tQ^i5#e`c599#0XHk#qf&v_(xhBJ}#A3}Nyk z%UOOg7iN4-;M0R%ZTUU3N{D60ZGfB&3%l&)oA_o^(y;Yv7(!;-v;Qn))=n09u^o{a z@#C4N*SS6|rkZEcF$3lw`bsVSEXc#k_98VU$5c4!me0L4YKex}6Owf-XWVtnaQi>(kU1W}f>{02*?VG_PY(qb=OF>{l{M-icIf&52mIT{qV z5mFVXj11IFz4nZAY`W2aRl;6d3B7d4%z3*xc8G9<(sQOI8Ks)r2yML!arYfoLSX4V zQjN=Z(1+ZVC%_U<0m8h19C@AiNk;D@P}Pd#6^kCPG*+R_n8w57V`!nj5!b##^o(y1 zIZ1s)l7|ud3i(_I@rYN)h-L)gJx)R+BFp;H_YopkFbEA%iXr1#T<|^l-KXTpQ8DP? zO7PY=E?OaE8iF`fi$2AOKFX3lr|o%(6%Xg2%)c7HjU^3CFM`A+Joo6I$M7=V1&Yt7 zZ&Ap7&A8Ja*@`y|PA zPBX*i(prcIpgd&Wzp7zG+9y)!!r_65FlL;c07->*^5PSM9j(Kss+ zxQ&qZ$FaV1N`eRLg_pGx(bq<6$W=#xeo*y$Nb2`jO&y4o(Z0B+cTe|{bY-RJx5OM_ zC563VJp}Q3JVYp-+zIXD6kjCgrDOZnJ<3;^Nh_6tqwaG8H}O&wj&fqXI(T z2G3??#GSaUf=fq?3CEo4*C4&fTm<$jO}V z*D=e{46YQmb?z@=3O3a1A7h1+Vd?w~Lmo=p_cY%FFSxKnts@II?u9UZnAgfwaRY%uGk;`S(#% z)`8c>`y13E4ZEN1wmYlr?NHTi-&Fw}w$pI1p|ioC$uV#3Ny@aY>Ah4ZbsT#^6OLlj^q zGm&TXg7e6|1|}<4(-fI^moHIQ!m)e%)L1Q*&(}`%5^ooRW3tB4CC5u=ia{Q&!LBva zpB{2e%*Pu(Wxq;DRB8YwbH%c5(Zvjd;!&5fcqB&=Xi>fP>fLHNssU$LYa!Mk>)Ld-Vs;zTQxaMipA7YneZjF;d5-h9}yOMY%ufh6pDpbOg{ zLR&|!q215gfn>xQMV$Ta^16?IqM)_JN|DkqNc8;j^HW0S;eEz=XfMi8)TGOF-PU*l zeXujX?k*p_Y*JjbwtLi8+2WA-Wf;NMol@zV=?CKuo>eW9PcSr+7awf7ZtqQ0+<$JG z(Nxr$qX8?=4Gj-rnf}CLd`E6Ca=oWL{;J;2?aiTg8JFFqWD9vm#~OS7t8V-Jc$9Q< zd&uf)^-}0OVi+IoqZ68#8_@0byW2x1U^PWF{5Z$M_2QDwWiegxR96NQGch6dJwe);m> zQS9aV-ad^dwrU~RI^QGXfE?BCFQ3KJfBYw^oXgnWb9GM_7mra?2lI3^p9sUT6t#Z0 zyX))0uOtiM1Zdv_u$D+E@7dLCm!okBu&-EMY)8RlLn%Fdr$TO-F}4!T+R_T7K~BV` z4q{59?=oI{ZTT}#igjQF9cC?a8nb9%)Z68*a$cu}qvd-aAke3s`MkE?>NU!azmhx0 z^5th)T`ved@}Y-BB$KEZ{*h!KsU`0tl=3W&>({QFJ^vuQit;g+V5tKeVZf_;2$>!P zbgL(3BZ`viZG=#l+qYEjOP(B5;H_K<_Rq)#by-uhT*pjI;@*LV`V^sTSxCJfssb&H zpe5g->ecod=AB0@^1|wzZvaHA9{&q^c^{xEtT!`Lp>5u2bth6tE$rc^JWUj9E=Y5%~X#R$vi8<7uTH)GkaIK{hEPD`UeiH=7QC`iL?wB}^094fPj!3OCwJzW1(9*meWQ_jM4q(Sns<}~3g@l2?E``j6Ibn_er(LHVfx|-+1 z^+(ZY-g~GKh(3ak|C}y_dLW~8KFG)8_8z4~{0+Ho(A>8n!juDZElDSR_Q(lS+SZ0? zI_&}w*CH5Ic$b*u2J`|*3HS6ApcHiQt~{ycpNU{jM2QrdYOXB;rl8fUy<~yCNUIry zSr$SrW9d&J!gK`mCm#_hoM_od{Gu!gupNfXyc}08$r9xP+fz{XAvv3oK1De0BcO6z z9(0SkgAnrY-3MN`Ez^)$3jyt2HjYE}a3A6Htl3d~+bxDHwnZ0ddHpOZR4O3N>!`~5 z8pjo~l9Ewl`YA}HqUFf*4FzRsXDKo*3|Ky=@@p+I5LQZD}%IYm6%W-=bT81#?2>?+ST^(fPcwA)la;eCp-QTVQT`!dub*az>5 zn4wL5XyQ4^_q{!R^br>Mk7T&UI|c19;Uo#7Ey8M?`tHK1+ILHebbHk_&uOsXP4&Dw z25XUVBmxS5`@$RzO&n}gEF>CW8Ej%+UZJ5ig? z$ri(nH6a*g#8EE)*q`or3(|{;+jj?GE77AL5>IZ^{J&cIRcbr82@f!YS9%`%(`|Zj z`2W89Y({~Z_#!b0>MyDDPS8oWjO$vEl+f8oAj@HIapRw3Hby1&f+h|t?n=f7MJ*DnSdG}Nc*6Sn0V5m#h@@N;5; zBl(4jawxU>2F#t=g|tsssoMx;L$|-F)uu7a!<56WR%+k`R|(ReL`Am313^d*=pcpq z@$`_ZpZ#|PT!WzjN3ktW0iaouf(==z7j!IDCPuN_<5wtxVo4gDZ;pMLBrG}PbxidX zl!4?DfUsQY472I>swubkc>0dAi%gGP{Sn1h1H~F7;13bU+pg;HkRXjAIbHDnv^I-; zmOdrCa8qIwxAt8wCG(v9%*YWgh%94=k~d%Ek3>;$rtRt%C+2!cdA{>>V4Q`Z$Et5jL$6T`>&VIyqcRFnDjOq9uDa|PD1xCI~tHs%4BO4celIi zmwc>I3@rS5#+7P5+i#Wz&S$Q#9}xn_O5D(r+(H%E0xG<{dW&!x9hl2-iR0e-5{q3; ziQ@gXmpO>{SPF>%7+QS91)MXp4m!$8>hA!(WQQBPOpFfbKT;FZSR_3Ip&U%nMo$Wp z5n@C~svR+6p(5fg-d?`M?ye)|9rN&uFWcXjcMmBrkqKI&=}u$_#Nr$$oC0Aub8aGQ`?cb9(&cl~^?!ocT-`w4wNiqj0Wx^dN6qi=J@O?~rfj6Kbd z%yz0(h*S&{R+i%SJN$)(dU1^0Z4pk&c-UQ^q*P#N_$5Bk)GlY@d*I(*@n^ofe7QM- zC-$9L1H+tXENI%`(am%sc+E}HGILTEDMyy3*)>M|6*H#eAwZFKFL2|}Uv)q&8R=r& zA2}1}Ho+reE-q*79wj3)S}H7q)4=`pbLP&9haKHu$rBPS44-+ULcT>}Xp4$N z;Stvene4?U4o!0mZj#oj$J*jcXkBHaasHoY+PzDD|HVU2ll37$xg3UJA)%+AR}R_y zWKiXHcAv5?23|?Xyz;VuIN}qOD<#NC?3+s`QR%ey)U2Nc*xGT_cbjV z8e2-o6>@o-d(Bab6ibH*q+;gaj{s(%n^%S{PXWrSV>!a_=u2%6dz_CW2F=gAn4osP zs!^ji&gStnH#js|m6Z1Z@*GU2sYw9SjYzJT>_rgH(N(fU2Fpm;Kw74;Folwxqo1#7 z@@;@jq5|1Lt<$_nt4kYNH?w}^D=2S(W@Dgu!p-PZ0L=t;*qr+0oi~QN1Mx3UUQZ}aF!Pc#^7$B{7a4XqwCViwEZ!BdS6t=Dj5ASD%yju}^9s{5 z=0eO{EF@g&;Ey2!nVc_w2OETy))-ThG4A_BT&Hl$40Q`^O@sDB-%t`6N?5Cc<32_x z2sX|ykZD|(hQSS0XEeJ)pU7FAYwJqZK@g_zxl~N zI|}jA>F5c1{pgQE5PZ2p>GZa=rwD6CTR(i9w!M_cE*xRC*L6fn2vzTbS~|n4{}YNV zP>KTc$RTH6;IQ`*tuE+@rL4>TF)BT1J2+TA$IR(5&Of3y#-!L1KyHjFXd(`eczgW} zd-rbc4iFxVGJkEGz)MeXp!(6j2q*npv;nSuu3z| zm=B^`&+O;v`lYNCfg!&ekfMZe5S@D3k}JxuA^*X06bfFZv8M*H_}B|G=gjGF8>8e# z{-IsC$FezXG@3`FuwF)UAUe7YIpg*b*(z_fhSXDt^3pcw%XI0g^QY5=BO$kkwdi%j zlS5`0vDm`-j!g) zA{!?aSF|eVzCEQfvIaaBOh=5!Y+fzbKlTz!ojD&>IX`-3gwERvwF&?}c%c%$zCxqwMoBP0vXkq7`vCp!1MGjW@xT6uPnn_xGgG#2I<9e) znd=cGkh|=<=rr7?c@Go-Ln@D1o zUmLF(=wjQ=F3LxFA!SR24ibPq7PGb0tSWNa|3mz7yZoSaI6Ucnb0|+=IfmFsupn4P zLF2X=hc13N|4aNLz+Kz-cdgA;p84_xNV$F_*H-Ns1j{o<$akiMK~u+4J&GMT+td=V zU7{p@=@897zEJBcmi_)i@(=Hqd)ygR)=c*!u&7lwu>R(=v9#~5UxL6ZBAV3h0_Y6i zfR-ZkwT9%mm(cqK12gb7?J-V4y!gJw-*au-eTV`5m@bR5oYvwM@W}#rqMc^2d+JeO3K74+@jK`v|c;pOuZSuKJc{R+N}g|H23h`YFDA8zLnR`L<>ho53&s zUi$z2J^)Bf3}xjSfDTBIAQwL}z~EJs>ZT!XQ3mf+dmzxFC0kkdU4%e%QS2EkL2#4C zi_9@hTn`TTDmm~o^$A(y-B27JLiW11fZ}hjULNRYkU7^lilhQd81aTv=_4zm)V9>P zB!q%B7O>sa1vU))>v)I|5A=hi0Moo~y#@;grK0PCE*#y9^NeM(r;aNiLR7ID!=hQl zTn_F4>$G2S(^k4yAD#!5N$>f7wZY>}a!TQG0Pz#3 z-vYBS#{_gfxb9JDqoovs>p`i?E}=7^oC{0xv`DoKfh7=i#^IE?Ojjv017 z1UPi2cqTXRj(=VPAUDL$SE+dlK%+LwY@{-RYK}2Pe7B?<57$2tEdi!#I@A~gW;_}4v_yAkyF&GvKs^xLS8PE0}=mjC!`Gi3S_vIz1G&dlP49p7@mjTl4 zpxa)y1HTJ+hLvE^d|{oBl3d~k)Oi85-7J7y5lS&-A`=10W(2wj0bHbZj^D=Ag~i4w z%^(!;M+Ra@&TDXg=uWn^)cUHydJ_mCUfXZuUHAvF-T3@FkX&Z&XRwiLV9hHJ zR}ND)YtX7pc6F%eA}6sB z6VG^mj}`APL-t~r{6rdq1`i=1Kjyu&yRj`ar3&%OOOr9)70`qgJXq=eGAKRdKVL=B zT%k!}!{YpQ5iIVAp1BuA@E*lz%<_w^uXjlCS z@-yjz-yB@`4jag(!B8aj9TbHOd^uYAU&sKktkK%Z*lwR0E30raWAoi1^hPZ{cCg<^ zRmoz<*(~6U`7Rg~EX4bCdpU6)eF;%aXW{m$K?=m`{1XM3J`fb^o?>NV#anGQ)ZHEA zaE}$%A3~4Vj&M?+=Vh4eQ0U;%`y}f)1EPSzi+WNxB z%$h`6d*yy85xZeE79cm>Et_ygr`(Lpj-Eb$3zNMQ&HSujw)+Sh%^JvaDmi8C$;Ww< zNUVM3G=3kUe1sJ$yyQz(^fm$)BcsJ0aHo+nI?6~TY25u3poGoZyUu00nD1!lH4GW$ z6SF5rhwd29YW3GLGF*BJkYxmF)W~>KU#!OviUdoDE!J%&ZK_DLH?o(b40CamE-75* zH}??&8!*jX&g{QeA+1<_MoWr({JPhupUR??$m)+-36;J1$zlc9uBSI_jjG1pt2es6 zaHoH`nxc2!F zAg{2wHm#EUEXR>JJI{y_A|s zBYoI2)Pl%1x5(GtM#yVCOZT7H>AMgfatAM@5g^3e`SagQA$&yH!oFtcG zuo^7ZzOg`D!%i9{?0plWc*snU(Y}qvmvJA0m%PbvQC0K|?~nn_RBFBW)4&`iFx z-1c-5K}f!LlMuYqyt5us*O6}_-GT`>Lpj$h zi5**A?l2RJEqK!9ty1d~+gA)XKxiG@Gj}W1W`3<83HH!qgVRyGEid7>D{9_{vQ>=8A zV_LxHcE5&NT0a{2$Cq?_UcBqw&oj>eT88bny>%~~G@}r=mA2UV@R-+Z{j6zuO?FaN zwSCXRe~n6m!UgH~qMPeD;_1lMExc z1me;B-T)l0FaaZyH&@)I*W5E#aSv4*B?5YX{ZkdLvq1(Uzk_m{lxFS)0G zc+t>jmbt?px)Wk1mf0 zk51U)T~!@@s=~r?u)|uauU>*sc}n1~`;oS#y(GXhq%G+~V^2F;LT}Z56n-OHRq^7| zvqPVQ`cjqjbV?3v`_hmth1S#aCT{|T9)ADQ=3KBo_#9sK9RAxb8dO8@gmd7ctWIPdU$(zT)0@8%MNxVtk~p_S)aN~8Bfy!17{?ol8l=~xN& zSs1NUr{a&C-)fvaEW(;8JGmOS_V#f7S9R_;G?jFJefzUg@2LHiX$NAM0p{{bo;}wg z%CDyp#=~7AkDYUGhevGDD>@8ZY9dr@Hl$!=1tvG-Pc|=ju;QN7B_0BB#O*Osn2(3 z!Mg!oW;a%wpfmcim0%2J0F0Y!DA!*SJ*_}Lm)$=RZw|ZZAe#18@_=NpDy?~wj`bz7 zUw@_4WQy&m!4%239cAKBb!JZzUXZ4ass=!BDf{+^5sG51{7YH@$P{7apk@7@#q$*i zZJu|C<2k#C1=XC|(ONjR`^(2QGoSV&0I;D`5s9w-&72qJ-?r_Q&4~Q7a>rV|AvMt{4p@(RN*^)XdV6H%TEC^2~>PF58psB7nSZTTo(B)rDg?L$LWiEcg-|` z9G}zCl|xMmI>~d6DS5sJbLYkNNLBvF|FZ>J6Y3(4lI&Z8{Bx^gcgZQhbVm;RiwS7t zJTo`go6r}8vBnd-M@f8l{TzlegEFkn52q+V$fa3unFGueImZGPaOL|QB-!9Pw(#nD zB7wKN>*rAH5cCsh3tIX2+eW!7a^>i$T-`PByGb)zK?}?lw63mbzZ>8(!1dPZ+Dr5F z-8v^?xN>L>&p$d`n38N*c1{<83HuRH*uU3L?SqLLcIe`}YF7>5*;boR;!y@@w547S z$NOwp@Ki%wo$u#z`vuf#(R_HA)WsMq{r>tyy_;0CBsTUq+U*H6ihRen50FD~I@}bw zyrZ#=j=yiqBu{1@13TVjMFqJq#FQ!KBuyhe59YyZ-CRFMV)|k-S8D&g*pBKOQULS_ zt`uKZP&BK69ZqBTeLq#-rJ(mP~oKY-a@nu zeGwOq>VrJwynR4@QA{Kb00YyqA0bqUlHkye0;!I}k(nKVRJ1UV88s80NAkP93lUb; z$vFrAhl0;hVE*%#Qn8*n&(AlN{zxjzezxMLoXk{0XahTEVWqI;_By$k%cX2 z&%J##UQtI;!)mVQ$^;3+iyVY1LM}>dc?MxT$2+w|RPGiTt0L@#EuZ6~0nZ3KB&B@+ z^~I>;wpfDryh<^%p*|gPI|OMwmv-(>M3yrT+y~?3l*Yp8)H$($B98N`UeVqeD6i z7yhO0v0a}HSEX?M2$h*NYID3aBGu!ppC#8}2$fZ<1Y#6Tq2|O_<(0*Y*CCy^ z+W~6FuaLoGiki?CEi(ir~IIH$$yWB~Zd=W?4t{NeTFQ@g;y63|t z60Kj+_-yOr2-)lADXC;+R_z7Swg_H!h!{#Hzl`8zFtxKqsZ(YbngzfcG(o@%mS0&-PoLKLw5592#< zh)ZSCE_%JaWVTH@WXkK7;C9x!6*+4l{E2i7RwN~Vr`9tho7)R!)xo)4?PWl#TS%U1 z4t1R3(#h^&x^^tslKv*{mfdyRc|K|TR?&Y zxeMiZ&e>XO>$n|ZEoE)DF`=ZGfA5vRy>F&{PhzBSgp-$l&DPDy3?+zD8)K>jF(EGSzv>S^hTo!#;X;hs58;UlicqI$YBI?W^le9wmL~i=d+sM3-Wl~ zq50?%Hi4sD@eBTGtI%VyrLpv7OR$dsA7BM zF;tiKy??~LI0$45N*{20^(cf)G(Q^UJzAZH?L|wtw89ZpwBOdj>7r&Moj4ty(X$e9 z{qN-~>gt`U9#B@Vn2w}okBG(82JD@2P+HE1Erq8gwDfbizc&@j85T(+T)C8eEvaBi zoay#JL13aUZl+YQGPBxTrR=F0nOiI%HavtKy#jv26edgIicx0WTtNrVE9NAbFR+XR5f4wjJ#BjJJ>{}= z3eNZDV7IZtq+}aivjypPsN`#62n2dZ&Ej@NE}me*3OE$H{NnkB>sy&s>$^s(XwK3a z7Qsl81frJd2}@Z;49EO;GeC9E5%-*smcZa6On(6o<5Y@1K4FI%2u^N$+ObIE^J!TL z=HmUk86XFkInnu^09T2LBe&m@kF52$9knvJA@hHQIxy9e%kk^(I<0B3-(CNT*6zE{ zF_*ql!f|4J|5=OkNNu0PZmb1M65lO;eRoJW?g#Me?!X)jInI+r=b7VWvR%}JJGn<< zFcNJmLkN~SJ!>xF=x61ooex7qx%C#`JO9i|e*SV}1l=z14#TjyFGVbrOmxxw*xx;T z4KH&?atgot?x`MS6QY_jv@=_2;WW+_bBf&hH~HG|Aqsmjs&Qn0M#K)lA z&vHL3saX>a9YreC+}aa#r4I0L*%WEsAD`vQ6UwHl9Z2qemTTnP(`(Y0#%wSzAMq?w zl#pEA4^f2lCC{=J9J--bDiUwK8vXQEgL1naDu|HIo}o3QKMI$9jo!-B%HQ7qd`Fvc zK&VFihZX3eSFS7sLd|(f!4ge+HktFDMXm|sVhEn^vewZ~ig`sYeMvnzJ{>1pX?*TE zj%c>+sAxR8Qz*C|($Mfd3DhFN1Eow^Bi$1eM0(OI3doPfjMR(a_&q@i!5-4gVYbKb zlj6F<_jN?zbfgl*X+3|_#;lp)pFsnT;7Ou_-M3NNJ(}JYTw!mO_J!_7UQCYrKMY#FLJZ}h zS0r$>*Wt+Qu-V^Jf6JvxU6g-*i&i<1p*(Fg| zVYW~h^#t`04u3po701=`(!gn2TCqs-M;;k0psXP9mf*~tgXIt&?!NUpFPv4-=YNbXZ*@J z+hR@I2ucD}!_^j00j6TN>+iOxIf5MTS(*hb?PkaGSn@Lwv*6P6Egylu+w>75Q>GH0 zZP)$f^XguiYDW@?A<2RO^-L{b>PO4gIdmWVfWfbx_iCtBDT?VX9kJNfRpB>y;>3gZ3^YE)dTU7XVoA?@7PyE`e}B&vsZz1?5F`Vm); zid=8y82kO*StA5y!a0s`vUUZQVd+-Ay8m+POfI_U`c5F*?ygVXw}WwFJB=hJ@oKC4 z2w>56zaOAB8R^UY2xrqodfU^+3xOLdnwI+`1@jSuGAC5Xb^U&T^WhHtb7t;;aVr@C zNU28NFU_Bbs)S)K_ahWS<@gnQRlN51gGxd40WtLf)&$D_iEVh}Z8DQ&>$C!&mr0p? zZVhD8^vyWh3=v{&-UREm!g^V-ezs|%?}XhB}F zqU4r~mLIWk1rIgO zkRzw_mH`A@_jEAPaJJj)U)2(O)mL)<&ueF&qTD|3ETE)W`wA`}r8eOSo+_=AD;?K4 z^!)&}lAkZf(JO{C^U|+;<}9(Ke%b>A-!s*(;8u>D>p8K<{SaBViP)$H>R*KmQU6MA z!F)PEirZ1A6!Cq!1Wgo^_7Ikt$anjE@pJ3LJvxgO zAYN%CtN1LN)SM{aYy37k3NF~B>>uGL&zng3#GN{4+1u+^d2Q~rseR-grEmsxmaabj z2!68l4kp`)Lw7N`;t_*y_L6fh3T2|DeL|h{$E3!k;<2pG)%KqAOZ3_b%_EhdSAfkf zAw|rmo@+9~E>+Gg;9lcD4N(EGLv|_CRg0>89#?*Z_)FPHk1JW)D$RR;vGlZ90(srn z>v4n+MUHAK`vR=@RE8*o*}Oce24^Yhi)&X-x8UOFj{DmjOm zc`{=k$H=o35rJKLXjge!-;kE08eC%AF2Ibl(pnu0InBj|U6o3GH^LUU>RL&=l|xeT zO>Cy<)X#bnH1VID1A1qtCup=q8UygvP5gmT}#18A$002i?Alwrq9+(SoyY@09VD< z{z8-#Sgp_HK*SgyVdOLn7|yU7s@te8p2IkV3Aon72<80Rqe0VAh{G}-#U)2v`}da@ zY^9Kx?*pjF^$I?n4-TKrAr0_+8X~s0t4{MA1fGt7!#2t?kMCH{Iy^Gel7V;_DfgTh zB)mC;;blDyF(}1Eg6r2Z4Zv5ch6G)3h|8Hfi8>2A{$UXB$(F)QSZj;vS$cYyN~hv% z;e~tsjLv7>`!DCKL*QBl6_Txg~G$ zGRZngDrJ+^Fs01b*fD`?cS&Gmvnx>T{0!^;^((6Q4ItlY5?_Lp z5~TydZiclOuoKt`bBD{9Vz{{?rAy}X-2ic1Y$k9dm;6kq#eezbSq@0_ve3{P!*DY=R3Nu7G3(*gd=DXj^Q@nUTeBQB4M1yyl&|9ihDh;yS;5 zW$1qR2zz+OSJL6K?_eTKhCRXNbv=EAsKF(=Ts*s%GG=`XDKwa#{Cjb_p$A-a{ytkk zqkFc`K~nDHX@nAg+ZG3N;#I20@(r%~ve)UyBOrv)mfmw>w2nQm+{5+r9MS)t(Z(&X zn|JpJvu=AG*V3GvPv}tWgiqWa-m+~i#xLP8N`%_ypV0z9SerHqRZudNjwmvRDpE@j z^N z%TCmEd->xfHSeO{0Ci3)@0?KC{5J~=(>|51m&X`o0FSB6Zji+vGrkqv_RO5=2bgT$w`(=50#s)hk9C9 z=sNOFkwY%oALM1ZziJ*E)uj+Fmx%MCGJy8|;(Atdgv8*|Ox%}}q&VQQHrN-HN&0St zLJu5o0%Gj$xiuYcP5)G|{NLi2qsv&+}LH zi&VU?*olj-L}q`xZ(D1es5O0nL(9oY;}2)##<|f;5h|6I2q!U(!w2|n5x#)DR%y{A zhgZ=Xr@icf`bf?2bomHErRSPNY&qte{(C+)CLY7M<~Q z{j*{N%RoMN9QePIf>EYe>hm*GX@b3}&$f}7!Iyz!GY^rYz?-)NM8ilnF^{PT#&h$R z%o#b<(pqFF*MbQaq3a|$)zr*z6i2TpzVq=DthGH@s{TH|_;szM;IBQ`R`JLEuZqqQ zB1o4HFIOz}(9}ZZvOW%xXp?jMRaV|%QUZOaT27ULoQKLF^AbYAd~mleY)B7HHU9B2 zsyK#2tj`(9nEPDzVzmskYx~sPC_y1hfQ8qU2>UrzTjM!RbQvDUh+HbE#&8BF!f+nW zl#1vIZu416$z)FeLCl5auY9S=j4N>mhDZxLV;AJ3vMenr0L;7zzJDK{Hz&kF| zA#>o<5P6FBx5vJ<^zfDRaeCB{_F~+p+3rnCaBK41_Qd?!zIO(!r$JKHC+1|h(K}c& zhJS?aZa(?=O4*<}hlwG;lPs22AeR^N;$D$PcSBVFhS(a;g5z^5p$DL1^ZVsTHmEt9 zs6Ho8DdJRsW;+4M*H(G6wh43P_@ge98hgWb*4Ua`B2{H#-E-cRSBM84#N1$GDqqRO z!s_>qtIq64fGzXT~|tZPqK|kydm3IfY=zilXM>R0>d>#ORW!J=QaoeN^bebr;QQu2tvG_w->)$+?v4v8ENF^ z;_)7ga^-TMdohZ!jgz(isaPLz7jv1?f4#jpEO}Gw??IqBOYV19PlsC^obAed+>#8x zgwm=ES#gj5UcI?`oU}A?Ea>^&e}yIuS3eT%AcZ%8j!Q93Qf!K=YpGZMy?m1KT0(%n z{&M*qo#d;hwxWLfN+np5wUGm_Vt9dM=l`5)-Tn2CY~vi(#T8~b|MzCb@cu>JX8+6&dnVHhmexi+T7Jjxxu=*b#Fq>Ooa?f& z@t;U2uL#bFn`u``*jh4dx%G6DeUAytjlS1l`mCxE*;Nb}!+noSdsLO^lOc1iiJ63c zWp^?qlx2=Z_`;5B(XQ(5tIMcewY2^n7?tng)!O;#fW_ToaPbh{+9XjF z1vV4G$77a*Gr&+^>Y(d&gg=Q_EkWgC*g`}pOu&`D=Wpy<=tn3V>*r5UX~t1PFHYt| zX)n_IwWV`^aNDd&l6I!l%v^P|09rt$zq2Eu0oH!eA>Z~l=WKB&=Bo7dpUmX~Bb38l zA#BS#=B2Wc(U@e1DwtI!yGiOuvG&T#xl=P9P`lbz*QX;MMLPbi0}2OcCCd^H?g<4V z8A-WmSnZ)}!hbBp@_(}qc7$E6Bv|?~Vmc%Qw(H_YHQsvjt zjNN^^)=$SQkH;Q#Hat%oEdx$q54q1Lutda9_FB*f6$i;^)0bCZ!5=#oNunXqX_N( z8)P01OaMKeA%^S&ak%wQhkV&me=z4%wu(?)4W6K3Un||*cw9F*kVf@}|1=^1;B|{} zBhfO}*FP-itPxiUV@Zw*-qF;F%fPe>9e({4&lI(k*aWJpq@|-auSsW34%@>Br4>5t zl;P!$CG!38H!S3yemQ9meeOZsXvTpUG>ht9I*e$dETcV)5W9au+j(r_ys?j)JKSbZ zv+PydojbLS?12?r+KH0z6+1F<+*t{4ch|p4eLHUum-dwm#Eigs@P(32$8}E5>pZz^ zYe3#96yUz7P!Sbh4+E^#Q;^X1^+);|ZU3eJsC|QP#@P-PXqC3L#nCK@v^{--wcXqz z`>!*RL*wOQrH|BnNq>E-U;juaz1jkgmmhb(f5R7@+Zq0TFx(ugHy)KkVEGEviVCqL zuqus_Dj?quQC?)r1Kzlv^ICdO_RcSETm|`wPQ5S(xR!H|#HP>L;xy*^^q{WQ>uIWa z^G@N6jyy@6U&l9x*57n`@ONzI6Vhvb3?(RQm9#m3_tUdln~Fsa?RTV@w`*=tyx(&t z!C91DquAp4?al$_oSYa`R@8>ZOO@P7um!6NHv+o-onvgajR>o2A&2bklFc1XW4O61y@ey!~MC^=1KN7 z7C@-S8Q@d~tYFbrAHb2Hv;pL^|4Bq>cmBr^siec3>jx-?^RdShg=&`cVE4M`#CRlL zVgv6+1lkCUS`>!4l>dGy%M1R{R(uktZGSnm~+Yja&FE(6kq%P@EzC7d=Y%N`Ow&C zrSqX10X`#9dyhv}_NeZzdemG>WRfa#?&>y`Eq-_TJVPqVY_4jQSK3dTMl^i~Xp)SF zE3?}cm5DE4y+#7FWQ$+uGj~_FC=1qT;ZPFQvwpX~5seVx&$T4+3x3<2DwDOa9MQs1 zNi6ZSUs@*Yax<8EikVCu-fC4>f~o%EOTB`O>I(JN^v zafKbXmtW)SIIkb0$C`T+>UFpMpZ9sm1_@7QuzKLS208Bk8kJSLRtDtV2<`q8kn(>2 zwdI&e=5tAlJS#v}Z`U%`)N$MWm%GMxB+G>F_5zE@URiIlSrF#_zA9M9$rzy6ZW0aG zKaEoEtxa?B{`xUVIgzpT_pa-bGY0?}nEQNw&aIv>b`FjT85flc=2RR)OO99f1DtVT znY$=8+Idw%%dY(kv%AqN_B=r9tifTokg0J{V0-71Rt|%k%a;v(@O&??A6YQHxZfg) zOb%<}q%ef%q@6zWsXZWRnzh|tXs!C_rGPUO{b8bV1heW|Lq`rf>A1EfQ-Zx4FPX)$ zh3b%Mu@j&q>Hc)tqbLiahhRmv1U5KpY_Zw;qBUiF)mSH+lc63hLTz zdBZ?ms0elY=Z<1-sP=5PsXZ2lp@#w1kPf65NjX?UT6H~{iFWI_7oi_F;8)dJN&pyr zA7X?LwAxk8d2o9gBUfC0!FOv~Shjb|g>NVmsBXPfr$0*;c_(k0OQqtcAx6bZX|{v* z^tC?5nLE_Ub9;e29Q-ycbIM4?0vqgL_BQ*6r|T!aiUQ{6RFsEh`IXG;6A+Tkh43T` zKcHWE2N$KMxse$A(*x>w+~ZXaW97;ru_rpLn-f*gDwOEj6Wu_MiTSiGpjwp z{xF1amL?*8g$=OJbOXeV;kOVc+fOsNl=g0`X9tOxsI^i2T z0L5GYdaTPZLoKN@ZOxO#6MdNZM;vCv&G^Pt418PL!}aU237nK|S;acXhe5?%fJPi{ zyA~X@wM+yN5}E)NZP?5(ckf5o)+!&(*K-qs&KsBMPT5?_7xr5uZaA%IK?)G;<8f7F z4|+ihM?t8uPW-veU)|ibMJu05{WY?gIg%frh&6$&IhF-%vRzo4*W>B6o)Oy`s8HHU0B{|a=?d+o>lf5@7v`Ve-BcS1yVMG z)5x;*Sn)=YAFW**0=rjPyg`2$;d*&`f6Ahf`Y*;CNIl!>8Q|3Y2*nTE;vT;0 zZnp%9d}(Sb*p7PfN(E8k>eI!>-3>WoBD;h(%?$(P31k0@!Uby~J;} z=blVdubS~PblNB)dUSfdq-ey# z-Io3eh>;)g{`ztKiy!KN;q^5AwWWUHR7WvfH8;b!zS6-EirJ}&_02u;cLM|t1qeQ0 z*#qWG&5t(eBJT1nN9dHW2TV+qJu6`fQ&)l=#2Fw2?e`-TK*F&kgirYFz1ga%c{>rF1OZsbUv?7?6}%RMzw{dfb6(skVk>ybi~7hsh?Xu6Iu!@NS<`4an+4QoA-$HL=~#Lt%^x zflVzH#52#{b1&%sF%fPstmpmZ(?nwI5z%_g0|v4P1B$fa?`T7wQ~2rt@KyWQ7)u3mv&8>+MY z2pu;Ry~uU_zg1R};teUcrR@It8_09;9!Df{9FG!w5&xs_;qY!w>?-xc>lhUu0}Z zCh2F<0)8;NAe8v?GjL$|$8x>5dbMFO&3l?zQ z4^RjQ2+UQJjhOz3a9V|xU!TZwOVnVaXkdBXaMcPCneF|ihJl!`6joZscyN{|*OdQo z1e^m`f!Hz75CBt?Z=44{BMJSP2G`u-9?8*=9)vE^V|=~4d^%DXQ9r8as;Bq)a+w?K zkgAj{WSu}qH!cj*+AeU(NDI3E_0MWay%V@nz8tBwXz2B{ey$~n_Du9CC-a&`UGwiH zLs`~Q{Hj$JNB4ZQ*sTS6-i6fq=Afl3m{nQ<*#fY~ar+xr<+~^4>E%w1nryBgZ?tol z=Nw9=+nK$%JS#^Wc3BA1c6;^qY5|Js9jand3+H63nz6f~IK5SnUK3;`c>Hyq1&kp% zS6o|e?@dvX9ZLKUDMsF2p7P$V;1#SrLcBebqc(!eKJS0~6!}cDW=n)+ZW8J~cCnvL zU)6{%aX3rTdK4Dd(6V9;usywf9KEeARK-#&I{qZP?#C>7mbsg3Bf00Gd~0y1Hn1_o z=9G4K`RXxSHowJek!+;2_LbP38Jp|e=QM>~B|Js@Iy!O&Ufx1_W6m-aBvJTJEAQO5 z;f2*x>22=a)laDEx|tzWaONJoSl##VD*}sAU%$V2=}C`7s)47$I}T9Tp1Sn>My{_c zSXD0ky!3ZDSsSrise!4-)cX8fBjb; z=r=h!C8(jLmI{TbYyMxZKZKiz2A?~RQG1;h*Y!w7rlQ~N02K)8sN}hnRxsb3LCWd` z`TEzHPhh*wtVlF`I_!vv;NK1h=IIlpD}o}vSkbi=<{p^OWPd}1Y?RAaYU(ySww**M zV?6_{4o+6j?RJE8t?dJtyy~aNC<;D{%g)CFRz&;N_DW3~o>7reoH$ZPv<+`Bf8Kos zp`l2-IQcBEICH>7c{&((Sxx>P$*I?$Np|qVL%O{(b&t3Ba!#4mVf>128i&FyTeo-s z=GI}fU%W+` zb)@(@cSWnH2n+5Z?{a>E<6(%{LMT-jGA!7c6eyM9A+4?J8e!0ojvfK!p(eS~4OU3e zE`#KisQK>zsbm0VZDBZ{^$nxznKKb%O4@oN?evhz4_9AJnC>|e^?5#A{Z-DJ|4Uk$ z#GkI8^p&qZMADJxHtO_^7VSG5e)J*c{O&t#BjEkyH}Vro zvQ=_qZAZCi#sJ$sMtb5$e;gv^(I6G#uhnNu`uh52LWsa1GlMBhZS=~AkkWXuX)NqY zPa~9eQwdmVDn{#;cNx_3$F~|^!VZB>KloC*(HZK3UUm~>JUvJy_d2L8;E%S@MsB|A zvzEriJBY>IGlZU5@@QVD>)R2|Dmr4glI_qMXVtN3-dZy1uO$!bk#yzj3blCD!wN7p z_q7TBc^ILZSTNt;M$Xgg(2h6uWjV&hWjW08+1590Ee75hYX9%mT*o#P=lV}U`n+6l ztyOe4rGnmXPw1%;Nv(0*v?n?3Q&NGVQ*1?aAF&vW;Gi9yu1^Q7FF62VS{^E$oN)*! zwEuT{*5G+G`JhbE{aO1>JG#ji3UD9e(-Dh%hlkqrm6u@L=k{TH2hs$J!w-MP#ygf^ zCqnodPCoNX`S_k@ZI=TF^SZ|9;SS=5*fX=m)$3H~OEL6wni$xbJ}^^HO!*%VxNZei z3U|r*;R{h{%Y39yQ}N~4M@@?XXe=yHvn=nu?bpZOVOc=9+wh)jU}T=%enb!DrQ`rL zL%xFgNes+KFei=n@t7sY-F~U&cI3bLg?dOtJrv<1TeMp;}Iig z8Sz-{3ELRiFkNr4u04-4!2pu}9*OuLE3g{cZuc|?-aj5O?ho-H-`Iq(9|C*aQfQ4z zZ_Y5(|2rLq&23T%ly{V4oAvs9d=TcL#F_`MNqEF3@l$z~=f}gKUIaQ6#E_4-n zc6<4V61H#lr*Fck%+6ao9|>YF?K>}y`0eDq?y+F1&2SU1M{e2e0GagXwqwM~s%KXk z0%ei!zp800VCN*5688gbAwk>xVcXH#!vGa_^u?&1-_@ROXybXAzjXafH&lDjoW-6I z%Ar^giEcJt=lV3jt4T1YZUs;O@KOy|UxwZ!)7es@9jgC%h+KpIir};(?@+$!v$atD zZwD!J4H@Ql1XrccF~2W8|5iRmwKlue1QS)H@+I??vbkO3X@J^7aMVKe@-1J!2%P_h z$+%;P6lej@oeEJf*dC-zpvaauWSpmGsZ=m8^(Gho5f1ApAIs2vkTk%Am8e^h><~ZB z-ql*XzihUkEgXq?u3PhkrEGJ?nTt2q>KneID-nqZj9J98t~5=b4#xg4#1=X7ksLJv zuUAji|@WyFQ>CD2{F4>W96dY|5(z@lG++WG`Y$8aM^N{r76~{L)L#?mY7Y6P-aj#rGMk^ zns4vQ>KF~o?S%_6$5tfoq&df@8@jRgi@Y=I9&wS+y;)##CO|LCUx|i*n>^!DBHoVh z(yTSg;MdPQg9jFj>^1bltMY>`6nay02n>H?Phw<|cJuJe)OO#ZKYuO#P8KUaw zVf&rQP&fjAx~c}CN8B$Igcng2x@w28+UIsK^fNnqIyV^pCBB{(IwVQWQaP0gAX9YC zCknsn=K86hfD4~?kj~C6w(cPqSdZ84B)`N-;wY90XN9lZ)%HSg;CDO>kTTrimlg4L zaQ(!@E!J&IpraF%S|D(If?la0sy=)a!yQ#=^oKiCQgp%gQs=pAGJs6JAMZgg`LF-1 zM7C-z6_3$Uxuxb5Bh1|hFNd~Vtz&)t%*GgVd6OnHqz_e{)!~r9aIF&hzQ*$(oM>Bu zrMnxUx+3G9FO)vviY8Lj;+6p#OaeyuF8a8d7G7b(eoD{dVSrKO@Hh|ErOx=EF90kq zM8egWh|LP~?L%w8&G!k^22S^GzAjZpxf`G!B%U%gm?IHkDQF=19O!Yv5qnr#I?l?+ zbLbcw@TK{fn0~^ixf>uG>p04GpoMcTM>f8z?Nr{}GfLw#*0RLR&TqQq6KHGr?l}P~ z9q`}lZ_$JA%P+&tPL33&p!|h%&4`Ptfv4e2=ulAA1y}_iu6S^`19d1Tg2ZNFHvh@K zJQ-bA7zy+JRP0Nb#Pa&r3W2n@*hCw}-FajqU9x@sY^t=U5niOh-_7>1Ce%+Fh7&d>`T!x z{~dr}0eS1!v>m>nF0(IhyaEQUoG`VF_fb*dP^$-{8o^9o?>_;l7$3D+C|iEI)Qm8f ztZPojyW?yxF%{M}I~|iiX6?@4Qc3Y`Uoml7}&m#@Y7?i%wYe%>XYYnfgkADb#4;T+Dg; zUBO2LOJ*}y4U%m@lD|29LqBXwwY^xC4%zqwAasky;oe#L^X5X zT`GhWX?Si{=d81Q5$&4_lJK_qKAiUhl;)EN<4C;4K_C3^8Q>$%;PmEnU%4|*wx!t6 z0lFWd-F=4p0i4L#Cg(F%)9Gb4~b zSrA?Iq^0G3KSW;S>pdyym3wvuTf&SGzUo{MFn{E({a?AJcr$X1U!j~XQ%sFBT@{@J3D`EV%Dt5 z4uO1rdOl4V=XQj=%;&GXs+15ov;Hesg^JX(RtigV3%gUplU<)KpGWYp4ctRL8qv!R zt~y#|g015?t5doLx3ko#8e1}Q7vwt*+^1V~3jfW>>*ABL#px?dWnv;0nTr?3fPwNlm#7&Zx5A9-Y4td1#1^_CzKp2n!{*F5=# z!Pe!QRSDbqV)rG;MKA`Ax~2HLX|*wJ47_xm*eYI_2}iyty(H+RgP>g z1K&uOe=V9iBe;kJB>U(^LO4NhE4~Uz<8bS&ALUvxLr@rrbDQ zH>3w@$Du_LmQBHx61~0gple-$He|i4LC3Sn+ym|RCU%}mlqX1j)FCQcRkx5?$le)>%-6y;2HbI?5)Wc~*4RW<4 z2<3Kw0tLckC!RrHq~%f9^|OMR3x+GsS>9WF2q&s5bO9t7+`73}J^?@W zR%B9@f3|r!$_#c#G}-f$OW*C$KrV7p&0kTZ&;N&rC#GVmIl$t~o;M?;S`4$qpE{$+aT8T^$4BX$m^+ewATzyG1e#jZ zY;zdcET$fcE6&j}2>g5bC`(kPDbj8E_Na9n!UR_ZjIrhkN-!N}q%$z4RZE0mI;UHb zk8UnsIuZ(!Wn&6YVCM*RiYs4diGWU7O2o_=h7ph()$6X|9@4MY&cgte{>7*U{3J=r z_5GG6Y!mTScW2O%FvSX2BIsPU(5rt5)$MrMnA3cW%Q|1Rb z{r>B23x*fm{CF6k5?{APw@lQ8bKTfNU|gxAF+h*Z5OebaJn8HgMe5dqWgD>hiPE^e zexkp2L6zO@mIBjXSo^oAi_(k|09%(S>VWBN+5ry1AJ&IEw57e#U#%0x_W8oX%4?OG zMFZ&ek+#cDq4^YG^jTn96y@{u)Dmz_JSya?|2g%D0;10T6Fp6Fsm(pu61l|u8(Sg) z?Z}UcTnJ?CK+YZ`po++vC8d&d-e13pJu1VRhS=+Cc2w&-7BXe1C5{52z6d>fX3l+7 z6`R)Aw)guHQr60GdjHGrG{B24u3!aXw&BP4-ScLW1aSG($C zTOye+@PL6d3xlMfZ5F-uN~cL5nZ$00nm!(GekE{%guWjp-2U}Fjys-7W_aw_)kKrT zBSuMb3f7J7e7a4kU{Q{89XS52v2eEJoofZ8&$?x0NS3wE$(FPuc1-W=;ecF`u{R^s zwpsSxoC3nKt1}mt;Wy|fU-%+$Y2>|K(&`TE2*h9CL1X~nlhrJCLf(H ztjLncBQ|OiCY&I^j)xC$mevT47ve&Js)C_uFF~(HjL(diR|?P}X32$Wj3CGTRfA!= zL?z9+wSLB2wRvG>+wQ+{dZ?-e~3E|RWG ziT7p5i&$SKIcIPZxX2IYZCWCKe7Zw*;u<_Yr-MhVh;(qB3rRuj8YOmRa8v})w<|xo z5(e=Rd+fspsK)Sry;-Xc=t@g}#aR{8mG-IP93WXwgF#{mClLq4ailsw1nnvbZ_=-s z4KhQw_dugPo*SEQq&hOD-isrrF*dj9`D4ynJ>py_L*}UC?Dr#79`@ee!M>tL=TbT+ zmW}?c=U_Q^%)U>{#e6hl0kO$C(|J3Nhs&q@j3u?_gaY|jC|g@|X4WmI5Z@A!*Aj*3 z8K*yzfU2-_2t)P#09UOZzrF!j5Q26~*a$R{lYs<6E0B69CyJisrKN{=6dC;A>sx%O z*F2JB&JnNe@|e-Ue#KDF8`M<^uJUxASBZkH zJ`(iy^qR?>B!$oeyF=Uk02O^L=bCVa$Zn74DOy=UFKr2)q3iD)(S@mfY`H_quq|PU z8Y`)rYzdCsvMV?D?}aBye8LEhLik+xapDtX7wy^4*|^Um9P)>GCUtGBISX?0$?vZo zHvUev!ceSv+DeG`j?djof|*`j)ayN$XO_-ZEQ>(DAKS)g(xtud2lJJt^9G zVUyJ|t|T}s3A^u(jnbyihXF#w*kidtzuP7KMt5h?0cHr=C?nXS&Lttn&2*4ttU4{D zw~R!d-3+jXnnHVer4745A0U9mG%E>;>9RforSiO$q&*Q~&x=86f@f^qM1L*0O%`_BlW^Tq9^Mi(XmUfj_!Chh z@x6T8t4yW01B6q;Bi9wNBGd_(QGSrl`0HnBVX3BpOG2QcDex^^>H7vKprm=>iq5?ks-~a0s6%a~MyvS=Pff8*cPG#BO^m&zo z^<<;Q?X7w;ZZzMlc!X@ivEQI+P2i4#`~3(cA$J-nNy{^ThIL391#>Ater&7}V~% zzy4JrjH-9@%)dl7o9mmR+Q^1~8}&vCuC|MqXQ>>BlL%mM(Cv`d`vEF7>N!)gBkxsMZ z*7zQ55{?SQd>CS+!ohdx$bV`BRB(q1-j?RXdX&-zW$XY*4$dla2+4l(w^ky6hugna z5e0f`*b>^QzHt1+HX+)d(J`NYqmVkCuT8o+PVNkNxT)flA4bR%v}+ys2s%0~_ld3| zTYi|^+@}y@h%h!p_R^%p<+Nn+wduMWq0(75-MhI2Bx^B9%fzFD7qGOyNLyXGPBe^q z3cQ{wREPThl8dg?R&hlEUys6!gt?*Yu5LX)JDkDl{aU$vKyKr>rKKgQb)fPJJ5A~! zFX#m4{q=JkmMyPz=i547TozT(=uCs99pM^l`JRvhrKs5xH-83hwp{nuuTDVD*q0PR ze4`D_lBnMSa|9WSkT+Tmdw6Qx5a29heYh>zvBr|wV1KN3HEZ zo`hMsKX^6tXZ>l2JUs*JV*Z`k^V>hmx-2R!ZBb#dS&P%31>@SXhMD)%ta11`#={V2 z;n=g6XpYcFe1eET_%_{ZkzN5uFxLv3KumjqG*M}haDOYx|9gN!j|5Lcx{{x}Q$a*C zFMMy$r$X0;i{vIKN*hUNTg(vc38P?&EzgcdyIzP&tDlH>BXQ0D#j=-?PZ?Lq{J-wdx&(YlXbm59I>z_rzEm@vU6xyD@YJR zN#~@eSX-L}<51K<%QuhS5kB=t%v)c0Q9eL&wf?<7kb{mVVPmV+M*slTuF%IPx$m9t(F&AET+H_7smW_ieu8-=V{RkaEOKJ`~rBbx=#>m z#Dw$LdvS&A?csg)5NJ*rXEAJP3yELnhPdLaO+`OWiYEEiu11|rxewBKzrTFZzlp%S zpWHiJIb5X`tw|qO&UP-{>h5K`?1;EQmnWr49Px2~{hZ8tUel$rEpvK%>w=ue3l^@x z&uU02ADR?7V@z1{Z#C!MUH_`1)$$`>Nz#o?TN85}(`G)S{Av*QM6_}75ZH~>jQu|k zx4%4)v||ph=qXz~%K~M%(3B`+;T~Q-{`#mO+vX zKtm4z71HAjK-z!BH8ozSwgV&4wk39uhXF<%4c_#BXGOaq=>K}4zX&{FT=Bwa=6{tlY}C3 z50QEIlOfaA_C^Zs(l_rTlIuAgoj>B)y@b@eT>v{PXNN=gu_s2_ZuVF|{gkn9sGw<* zqxR)S`E5si1e94JZ%#q5Ci67}qI68iKrRvWwtR#W*UK6t5B)gYm${LGLv z+HTUV-fqj`iDA?t}P`P6>*F$Rdhs~{$mTiCDhgElr(;$e z_gfKvVfvUa;kz4Z?Nz;KF`uX&?LEi8pl%iaHbvY|pP(x3vzM&FUX9!~(KTepE4qAg zNf>lg|0T$4Lgu90jsmr=&@E}Nd^F}3rN^9!a1y$(0g$;?J4v}ZT;wpgTV?Qe8dl}i ze4*9E<&amHoQEIJf$jGnLP5KWLZY<4v7;-&N=wbQdDB4t`YT&Ai7*hDEflp#6T2BP-=n0v`H$WOTV2A^MO%rr4P^T-YaFdxv&WdQ* zbBY#Iti}!m8)J4NSswqkOG^t!*ysqso2;Sms*Y<|#~?qH!j>wW*A`>v z(@k0{TIh{C-V;^UC?!0a7q39IPEVVymJF_hRtK)ImS#WU-1j4lq~Ng%#-J<^>*+&q z%TXW=1{oh>z-h@jPxJfnKILI@A|DirJ^ys71F-YzMffEJ`JvCJe_l`wji5{w8pB+; zpTCKetN3y>%MM!8UX0z5oaTB`>BYj~C(q3-${w&Kz+yh<>%-Lx=b~Jsm~U^=E8*){ z-w5t0_=V1mn4&xzb=Q2b8gMyRSB~Kvb5JLEuGYy2&h2O&9lnyu3!x!&2veQN@T!gK ze26LTBCU(I<(0cusWAJ)040|rmVmX>{z(5WyhUV*=d*rQySuAbSWk`AqRNtVZX>DSXQUfZY`dDL$@d5Z%Scs5NQ1YV`! z03INafj#BU^i;Jjah@`X9tNmLYbs&g62%0WtdZgb%miKgz zD$=?U!|(Zfl>s!o;JDO1q{wdh2v!}NDW2%m`8GZf+7pc^N`N!6l-svsl#Q0@2eI);eW)BvcxQ^J*kRe6(mu1#v1zY4nO%`*tyiMj zyWY|57}?~LwE?@*o^4m!T_hTIC05TfAC?QE2gYJQvsJ6#8t9xZ$FDsMaT$U#_6*tk zXlaRMSGyr?`x!|O->Hu8iYZg)SNWG<+}at((|xjuXP~DTVptxLqme6!>kt6$i78Q1 zK>rp|-QARi6B{=Y3`vdkG=>J!-#v!L>-Dp2*@7@6KVdA{<2feJ)hr>^S|%J+NT0A; ziGDx((-={<%JBAt?)t%dP6osSzx?JF8e3{oCeHz1uzl40{*@AY@H_Wo9LDfO#=+AP zQc(xV25>rfx6-O@st$?Ej3jl43?P)a!V=e*KcB|1CB|l%uhv1%huf|7yM0B}#fn)c zTIaX?Z~K<-oK_C;M9XTvgHQLW6gGk|ZAp@waimczQb_{a8Nh5Lv=p}6d_NA7nx*Ec zkxU}-ZvCo6^RRX_7^wiSRPa5ar}KcAWQ+Cw>3pHgg5|9A&KL13`Guc|g=ik&KmAJot5eVBCe?z@5o_ttp3w^0$A&O zdR_KJdO(>qQoGAsFz;}@O6qC!W#(elsF2aOo^Kmgc|`e+%n*;hIVOFUi@f-;}| zHFkN<*}V8&PyQ=}aoIi}G%Xo>uPu6im0X{TYg3gv`d_--c?IQ`1=q+7V_L3Befu9i zVv(`X72hp*b7!?%z+V5V=IKb8jZI|aBPxYoMUtfl`X4^x8t@&=ZQ`kA6n2Bg13Uj` z``EqGWqK-VT#jOue4EN1Jw7v7!~Q>v8fk5kW6sx)Jjcu1#6I7|Ip~oVqXe2Q=tvDW zG&{wiC4#~KVbnai@U_&C`H=@#*lTOApLud2b|RH4^L%mi=9(cA#6IqS`Bu-!lvO>0 zMK=8R(LV3tFFGZ6mZ;t2bgVNsY8 zi{6$J&Pl6wnOy%m_RfnYu8iDs?p7dp04V;5A?*>ien-b?GLHlqx3X^iuvR0dnrquZ zz@Mz8?N3cs5)9(&xb83iiul`W9wxNQ5m!V`i$oycmASCZk!bA5U1K>BX)UlOLeKs7 z+A;O7>sJ6A?Q*g_IgqUUIY^ax&h%vsIr(;dwmVpu)Vs#}F7ehDAcVh_8xpCm5qGHE6+@2$Mb7;#6~h-t4&8_j0EvjqH zo0t;%-_+ksa#ud%c|Sm&p~kM(mG^p&5YMcP={%C2o>NX^3SXC$2-Pak;(#K(_|-xF z_v&SHBMY_tI~{*U=>~rybuHmO5hg`kQlq*bHh(=@RJQdq+w_qG&wS4QSnSc*j>;uk zw_t^Ek|Q2I80XcGU%C~RTiI}-e1pgTIZeN@59m6!$IBnVKAwtJO&srF*FAtZ-p_n& z`3#TTnEDnR+Swqej*C8tGVAX8XTdDKbLKHz7xFNK|N$DRY=s)P&(9`_lmHcJ~F0`H%C; zfBmb)f(+~x?UKM7Sbb!C^IM{*p9W}^2sh53)QoO`i>`+J9;qYTzUIB|{u^FxI1cty zPR@yOd-@XAad($Mz;ap9cWlQBpNBXAPX8f~OLgMN5%2c0w-3*dX7m;!lv^Xk;@CGrHS#Mv&;r%;=gS{9b+=g*;z zVkq1*O2)nwk@4d3i+Lb+wA&F!OiQ!985n_Su?X5o6DwrgZ z&HQ*okhyQs(_cXoq#Cp&)_bmvj9*L1>TX7eXP1i&A!D%`kI3};57>Jd<-4l{)PIsZjwbZz6zC9~> zSn2zfqZLwh*8jBHd-4{8(ainBR=zZyz~!RxY-@@tjnqy+LEuAN`Q$KrP|92gx0gSv zLBGGjltPsnNkuyd>cutl%&VyQ{MQw(pEmn^=#Vd*@nXuq86X}9?%KH*A)z|$1kuY%w*)7J*p6#e!To!QPf-Ap; z(2YSM67>_ILxM!GWb~fahg+1VNY>9!7FzsW^h@M~j(jP&>ySyqo#(M5FLHK|OC%(g z@9qBju}&f^nFzKhBg*U}w1=!-Tuok-F(NO*5})@jb>5ds?!nx?Aj7yONkHy#xa)YVxkXSzvF!DEKufZR>)Fb4pzE)PuXfgty_f$) zFYcJbIyFXvKn+D6iT-w8#oM08kgOhRj+M-EGe)ZG&2eHAm$IVqM)`mQ`~6oevIC1p z)xaxorAyj^ITU=oJbsF^+R%KMgEld2$vDM$x?qF@gjFFW$~oh32q6B7)?3U6U1Lh5 zcLS6p^0yKeck6v)o%T3h$JQL;>%;}=Jd+7y1M~*24w1?tKY3AadbNjtfGwhYCqIJ~xBpAJ}IB(NH?V?0|$A93#3 zzs&TpwdjFzU{Z_L9Dzyf*V4#5uH)k&(^cKxvAux?a&H|1b|r6=fo)BgbB|zk?ZrSJ za0RF(Ub~NP?D}H1yoZnF*rsPs$7Ul?u(g)L>Kc0Tx@IzLNfQgVK~(35L*{|KDmA;} zH23>k>qrMzM&c9|OiNqTiR9QKul{hv940x&R~zpI9KkC%S5iqoJRXFYd4&1qv0>+w zztoTrkff9+lgGz5ws$WD+tJfTm=Y_jtPM+FAMQw@RJH&bFmO>D#1KUl6hy8#5 zPp4&s1Nh1`?kUgh78Hd5Z$Oa0FqQLmB{v)a+<#XyP633LmE8B(UvZ>~aY>&P>cQGuWH97@WwGVAhyc6jn6l2+`G%CIA@34V?>?%zvP|# zS3IF6lcEO!y5NoohfOw)ON0r@61uy773b!2`u%4zMkuO>Fxk1UK2A5VT!-1V&95*s zl=*CgNEof>-Q}w^*WZ2Ny+S%hP`^n0>qyY&GUrFfGSQd`Ly*&au< z%1#AR=K&t4F1%=D-|{fTQR`j{GUia=DxM^gKt$6je?FJRO9I8EV7i`>8aSv+)}$a? zqqw;nqQ1fFU00v`bX{*HkxC*(z&nzSA&nWNHx7Vqt$n;r)gl|OxVQAOFZI#wO^UH5 zOhqtpQ@2+;O6~}>Fx~Co+l+Uhr30)h=AEiUv}^&7<5yJc(e$h+FGda`n*oGj^CE+4 zKsrh4p1)#%i`-8AVT7Z|DTJj+^=mky2Z>-;2xOiF0kVvVYKBe`0)u%4c}tZH+Oj=< zgy?Pf4g4M?5}dg>oC8Izr9Rw4DxJ5HId49yj8sM-m>{*%uz7icZbrE37&ej%w9?pg z;WBCYzC}F`SftY#W5xQ}4ZxKuvq+EhigxvGfOWecfrDMfhHQ-*0dJI-Vi0S$jIeoV zuCN35<#B{|dzbEpAZ9;#`!?Rme2Rmun)GoH@E=cOjE8T+EACm%&rBPlJ@aM}uk0BE zt*M1pAQt0sj2JI%ZGL8^VGJh_x%R4lXR8>a9pgJ$QfF=zSQ8+*=F2jkzU`L+XOmxy z)gf2p?oQcUE~YM7xD=vm_7q1s#-loX7@=%)3J&`rD1H@leXt|H*-C;=UXH|%7bF{iH#q>W!G;F1>q;UMJfYkG|R?4rFA;7T>d#va5sQ6}LUs1yyFgSY zx~dC8fdwF@8vsE|b8cgDf`y#YFCslz-R`bmd-;eHz!v+9jeR0`NZH{}S$Y+k_XzWG zdQ}^vzZ~R|a}izo!}ZsA6yWKhbY&s!are_2Qm9hTTvxFRw?LBS1d(W=uvrsc*h6i8 zxPJEOvhjZY%m;w_*!J&6ntl3_phbS^>nhZ%$U-Z$2FrLDU{oaSq7;*u#=RISY#Jjn zMxwb&LbRgUM5l$B0>Vi*8&dswlDXXuk-|H-*!;TaJ$f(MkjOXRj3)0_nOwo~f}_fJ ztT8d~qI2WpS6mxzSgRHPU;JS;GlT1)3KYqeO~1pa#6u>4G^dAZB($EioAL0BzO3qa zQ<4c3bC0`sOh&+Z6sSizrboY>E&1G=&I|WZ2zo_&W(;2Q1mDDC)G>{8j*ACK5rCl5g9H5K_ z9sRu%O43RTmG|!e6%9eoKKJio;k1-G~X$F{kzu68d!gxpBpuKGsab4+G>8I^WXJ*W>O3L@+Q+9FR97 zPY{dJNV4o4Xtaj`Qghc%SFq98+hcL-zgQok+MP*J=m|l|{pk>SOH?e!!#D6s8{>Z1 zzJ65GS|-h2C6wGBQ6Tm}^InWZ0JIl_XeC0q-h`=5Em7U>KgGgY#Ro1@>W&IcaAX$9 zHa9?mFqWi9n2jlPP^oJ%b*RpUj-GUtS+pkB5ifqevE$PbLm<-O5{gZwsb7%9*<@vt zKEHB#2@6?p4r;GnSl%Yf@)Eyw9q%f}|Nk)~Sf+>)&Y=x*Wj1EM`8f>-bUTjZlu#;! zPxu$CQJ`yCG1gB z_VJ@adZAImW#xneEej-jZcX+hvp2tsXdK(#^$OoqbBU6_3Q9V-jXBp+}N%VYK^9VKTMmfnTgfIE0@Kxvh7vq(jlGR35iZehcSHJ-CJH zz_f4LxDt)k$}aTr&J9Et)V}0LYkTm;81kZgssms+x%T4AjX+=M+Y;kyAJ6m9GZtM4r-tSUb9DJ~rDz9q_K zZafoOzS|k+sN44W*`7v-)PVkfzA$soMwy-fw)SH!JmdRxJYz}Wz$jEuAn2~6C;s$J z6s(qEaQptN!u)JN_5!V4Y37>}By-K(QW;(psCGMeD9y(KvW=i6oAV}!H}zfdl|;J= zb`@S3@#>K7pnI5y`6>^Awrh))BLLXB1$(+Lm}_TwIR$NZ}L z+-OyIoqbJw=~{xT`w`OoKbAM|W*7y!T4*kFN@m{6EX=T$`rtfeouQwc;X4cQ?15X| zj__LltvzI-7dTUOB*&Y)J*;|~52Yy7Ib|qV&^ZMC<0i1-@jks4{Qm7eF*(Hc_?11W zV3s>2n9VtVf4SQz9ncCGok9QObiN;C_%_yLg$OazDyWPGs@14r&ONM8EqG_X^jUlv z6}Jno<%xQ}RYwsPMAE(fm7EN*u_}x!*k(Z~95dCv$U&mLa(SLJ$s_yze5Zmq;b}Z0 zHP=E*i~>Fc#zp@91yMogPa$$$f>S&^As5vAl7i0t5Jh<}hAlVOa+G54^!09N73%z| zGWKI?NL}qQ?HnWwEyg`-;~c@Fp9stjltGrI5I>IOD1Kw!=RTU#YCh-WFxmb5IZ|Sr zOZH|(x`)gd;cU+i-7}aj4RoIJFDbxn0Tt zc{O#>W@2Ye=i_=pQ-3@YXR-5an6nQtwGAgXZkSW+OT_6e zr}XJt-qLo`GW^oH&3Wh$U)A0mTX_W-SQd#mdKu!*=A~H|2|p4SO#QI?rv4!O&2@5Y zwdr2IK~&0USz+b`!}5;hp3XZ&JicX z`&wUd!aqg;D)!uS=ds-n5Wamhxqf zx0w180@AoXwVzJ5;yJelTKDiERWO9Jh2U7{qpiMZJu6fgtK~6r^vAsdl~&@?qw*=_ z7}${Os`ZDjy0RgZM&9KEw8JMI7PQiwlCtw4TV0pbL)t6n#dda0;>@-`yll1Gj(FqB z-^&=EFLu5iZqiBu9UbRW{i8(^L&HOCe4R{jjX6Y2dvcOBsw%)4(%-lbW< z@c<6Mx}@!^>ZdHW%6TPlA&y3C6mm*=uYHcuQda!_I6xW(9h^)8{YY zAf({G@Kf|04d*8^k*B0O9!GfjdXGfygwmPots8~$*Uu3VgT9D%R~F-N9mP`Ffve;Y z);<%y=aqsQYIWz8Sb7hC zafl$@r$TPG8YJtvv=}{Q+K5tk9O8^4ox7U-GVlB2L$#&9PIUW&;`gSZo$7lpNr8{>#mRq< z5m7*3i2~UqqRxo7ROW%o`uqjD6hQwG82)rjpY@U$>g$F)hLjIQD1=#F2pfh1$KXPa~v#>drgZ zg7)u0lVLRwO4&0VRkKm{r8y%eJ#y%2>O`^rA8`rXk5Jz7@zwd#2~DjqWxX>c$bMGU zO12-SYWiY9%`}x^j-)xaxBfK5Q3m-pwnxg5^JZv~DvEa|k-%Rt)D>0MD}{^$K#4Qu zicREcgfottIiF?^Y>@lk=TcHBi>RfEPRI(%YVR88OvBah<=#Eux zqaKSBx`#*^ELgF-sE(qL^CFJ>%ZJEeXZ~YPOHlav?Lb^vxlF0fLJ1OLFiqLbIp7Mg zelEiM{Q$*{Q6@d-ex6S_ioSVKme?Z(Q&TQy&lb5pn->Xnr7Mez`4 zo6AceP~PAqQbwZXUHKAJgNf!1FqHD<(B4I2zBX>QcE*cK~TO5d@wl8nR zntgG57$FKBTi=%M)57XzSyvG#%%|w`^rzR^2z9g4**`B_JvWy>|w;1RmjpKlO`(sq%=t&f-^g1-q|CXUB z=7vy+?*}L-3=N-&NN;7$s(kWfX7}Knn2kPaMpWR&u`ZxHo;p8*-m-fUCbOjxfwGTy zzZ9Sq!e+k4t6K89R`n3Zkw+gvH4Xoyg^l@g@o_}-ttR6Lvz@_^-w%*w3-P)K7%3yR zSX~WgMfB9N7uDacX31%YTt0kCo;G@#Y(bw!$b^~*C6=IsJlCgs@uM;EM!c2hL4hR> zT3b5H!iZchaLzM1+_yvIByB4D(Xds3R&dv@7y&~%GRcoo$n0@Z*mNI=a83_6K98TH zPSZm5xN~04;w(OPbtG`oMS8VmBFNaCCwZuVl-boRgcw?nLu3=C-q{2(WUW3c-R#|> z81!a`xj$2qfpAF!F52nzOxpGQIkHTPtn7@$tG#<|XB;mNCulabcu{?6#4)G_73>?) zH3BLvQmO*?gA_;wqGGQ4tFl1tXP9om09JLy+6(AD`OeT{Vh>WV!GKgZdVDiV3PVGx zxyBJPiek6QDXDCO@d~A6O6+ISh@*;(&`AKDee3r8l~>>*`el^f{wj6Ncc7O)qw-Cb zI2jA_j^m;fNo_i6%7E8dMITb&h>pHtIlHu#DqHpj6JuO z@p{W=%RA1Nvjwt4(UMi=+j4&r-p=J=D4+E3~75M|q5@*F1;ryeOA%%MUq5M`#Zt$o#ZPBE?CQ;fVHoPY5ZlQC#=9OyWCMiifAKq2BEPC%KOZ z1#LM4kfYs=rgyz&ZO6XEHF@c=k+D zz||W*kg{cE!NyGP%vAK$rxDJQ5GF>HpQr6v^Q>?rep}2HDIBZpnTxI@^&wG5S1R8~ zQ*?b8VO#5qjbB`aJ*WWLcfJ~#altU9hZZcmG|uEGTBGRxwP>8h&vyc&_Ams=e>UX{ zafp8|d6R3X>M5St+lw&0WDB?8YvjWAUO#rieb`;|Ynsif;i(5S0a~H~J{~ZIl(Ss* zjg?f37raSZaqIPS&$jf6#9s5rCL{Uqw$Yo89aqlBV`kGo*0=O(+ftj_;oMabAF1gP zA3~KZg(+k5(#MPJu&o~t7#oWw`_4KAq4UYTX#8JbaG*^C8!m z?DF^zC(b$P*;F21koCB`Qv=^Z?b&eZgNXyKp{;ARF>Id?nFC##*}7mB<{MLT=^FgyS-j+ALY>K-LxP4n*!HL=2iiMm@cDM9;(GL~< zWhBIAt|Z6jV^#%Nd3$cKQKh-yf~9)vJo?Qv9gblBxA2bRo!%8)l=1!VpAULw7axyA z1IuI9cYD|HLKX59eU_AB2t3kB6>6lA(sWlo9;@njj7=&8tIgW!WZn~grd`Qr7D^V4 zJ)CjN$t%V^nOls3Jgoos|0snUc=8`W*j1h2Pu7@&FGK*CP>yPW{iH?koml8uXoVs< zcWpJZ%_qsJN51ru9Mziv>bx*dla3xjFt3mm^w+v_2jgv$$Smk+7u@=&+kg3n)Tu2? zBM;);^mh2+_3Ok5CG?WQ+P$zFB@Cxb8WVx7wGo z8}+f|N&o*?ySF6CktA8u?}zB@Bq#`~xG7ds``^Xr#{ze^r~qc7hq@}ulR7&C>IMV! zBEtOhR#KSxSPeV*gJI!igo5d?&h!M{Gzo!O)91gU=bs$7B<3yE@}CddwTG@l4$$|d zVsb1ek0TUZ^D|bgw!({>qGGzulUn6;Bko{Shw^_!l_Z>ce%aR24ltX%gD)fGbrfgH;YmqWy|mIJ+DCmPrVjEC8+@flHH=?Lc2vV}5$G;;9()+0 z62I`61M^U3xZk&*BhFFX%g8&!xRb}eC|rmt@L9%qxqMFlA?1DlCyy#&*bm-3ASG2K z%`tN2^|+H%=JpgTGm%*@LsTR?=imp&McVgeKC}Bzq&zzqM365Ez|H`T@jgoA%SQ2K zfR=zV4JwQGnd3_;A~fvZY{R&R6iZ?}m2#Id%$x&y+~MO$CG2^GaGtJ24$S?+E()a1 zbjh~DUTzLaU9ud@+ZRzs16?7^1m^Kk4-STt z4oPhH8i&8u*-prypSiw%h&uDzW_O!@n4LSVQCuF$+T3TJiB4Y=lPynS>`Bu5P4q+y zyyroF{j%%W&r80G*>>REV}V8X-H5qTmCnpG|0oxdSnaAT?AUzbdk9{xx6k3d<_YSe zM+6>>oGs_Z)||=cNCdDPRr|S`8@lCN5A|cwQbGc0MPFM%M~u(Ijz?0&KD4?n8PyV+ zT}HTh?flyO#Cn|b&z#>6#S5kvdLuo{liZ{~UA}yTTWk=hUgFJA17ZPDI3PTqE6kiX zQjq7>b(9&w!986u_S4O=XYOX^)IY8i=wkvUED$;KMM1d~ZsuwDOw+~R&awk;#S#1K z;_;r&t;ElEJs8e;9Cja0N}4U+S?se=;S`2^omX^)k`OP7!sF@cG^}R@45W)fw|k_J5?M|B^V%MDKSH(%1H@I3F2-qdYnJ|a`N~8~@E%9Fw$S=TcmLT;-$(+!KxZYM$Cw5VEvT43htB20 z_3vQWD7u+0nO9xj?B@Ws)V9`4f!~+hwJkgIW|bFzfIZib=gYSn9tg{Oclz?{i#+xu z&v!F^XX^=3p$$;G;~Pe{(9yN7g=c*lpv*A~P5mZN=t|^Fmi2kQ%kj?DtqxbZKK*uu z0H?P=IOePRa`Ehid%TFE+(>NFMmfb(`ja9EYDHh8@Il7Qx$Bbr($e)!7Vz`+3*8u? z2_2D6Lmr+F>}#tnLp2W(dB`ebI-wchIaw>T^6B~&gr-330gB%#DKQTFgGK6yrDYG^ zQ3*@oIhy6B<{bi4L7N`0-}7g;J$gTP)0QA7Q@n@>Ucz%$`%H|w27_qwqXO?zHeO4? z66UDt=A5j5?YknCkcm8dhkm;pJL`KTsC-V80fwpfkUuXZV{nwq@1)BdN^+OI;qw6X z^=+{<+Ct9zkjpvbtrS9YSCaWu!zpU9?jd51-GejevL4Gp|8)JkhHhS&Q_0C$QiNJl z!A4ZS=JGVP8aJDU4Yc@tSlJ=q%u$e^uU>~{Z7%O_8t|d+_CGRk;|Xwl)9?0V0SpCG zD$G0EoEv}q_Xrhne*GD&y4z2F2ceutmdiZ}B_6eyt>yr%{~BtWEXd{M3#g3qImg=@ zs3bRL&N&_bk?dRXo4`Ff#5-!35!7Azam-dD0+Xi^j_2>-+tZrh@%SyV3KEiNM*<BFh>xrq^ndJ^U+>u~-JecTwo(33CuYp4L zcciNO62zR*4xKV9qQ`Vg4Azwxnf3Ylm1l^**LB!}rc(3`j}nvnx;K_5HH-sOi|g{& zuwhX4)Wf2ww7&0jZ7KI7dUuG7b5NXzDJa<8uH2TzkVkpL$v@e+ZUW9vCCeQ zk9<5|{!G~vVC*2=xsp-JF25WF!GYYJSBF_zyaSN97gvdW)TS~6w$h)kKB}0GVRC&5 z_i6Tz^N-R`oC$NVl?X^rD-e7K6C9V6wVtmZ1dbYl{64vbW+gS+_AQtlXIW1q(ySyH zST3;bt5fB{7qubo1|96@@P0yuN>5J}TZ+F5%+MmDxYAK3_>LU)3}AL}IXTSE+bSVR)@D&XA?X^X0Fn-=3bD zanNDrU?#kM?}{kI$yfIOwz@R?5(&OCi~qWQC6OsasvP@z*PM;6pD2+~*_>7>r>LJ< z*@z0+i533IwWHsCxO`%_T1d#_{&~cRK_j%*y9x5<25DnpP*EdosdqN;PA(tw;PcZ{ zVsNuC^8;S{u|3_OBd99)ku^TQm@g@MP7ZbV>fD|OI3GU()kE0Hb9asD5ZU|^PGB@a zEoTf9^&Y0pe5I4$|cppPOda56dk2@E28`k@^~|(X~B4K`J9Q z;O2dD>*k>j6^)%w#r!DQKcV@WJYnFA=*+QY)TjoZ)gF@Zm4^TFYm4#ch-GU#>|PTP zflse>@GEuI!VR>-qY3waUq1UWt-=)FwRv zzC8@Oh&A;1Ff}AaHXL-~+pYqR?0+NmA4e#nvC6D>z7wM4BjAmK7w^k+H$0dKtA?C1P1&+!sEyLbSk0_SJ=Y$R@M9z0?zV7{e z9Uw;_%8R)@P@(2aX89)-RvH~NRQV1(kK}SGCge-F=JrG=P)JP6+X#7x&S=)uitPl8 zrQQEXY7>UDBoY97SaT`&h^4F4G2?t0q2~r)R74gdd%n`zdferWUO~nynz7ux31Nb=F-W!EJV46ec5gZck-?EStuNQSa!s2Pz--LtZT~8Vjr1ct^07&P z(uV(UW77TB91-Ywwujr>Z{2!^5WQrBpz~JeCEU#G{wr=0gyU|4vw_P7G1&}zx>cLX z{__Cq%YFIp&mc?L?O_Q)LHre+GA~k2*sF5$^9aY|2uiBomQj-iP?)-3qPs+}X$5FXhaU7^_ zxeQ$ZCZ<&TYRO)j*@EC;O2gcpjLxoOFOfVjh=wMN4Pa{FRUao(y zQk5iKotE0j+EvbXc&?Li%!=nzygcJbXi}%KR2Zv}IetVN|2@FC(VC*-wx~4A1>}|V4Wvkz&-}PkA2pP z@iszWcm7|O#F@r+^?aAe^%6-5|DxAEcPWr0?v#}Iw*iV0(?1ISXMLx!^+;DmDc@qx zK730(NhH*tW_uAc9lfT#J*WjKMQdFgGDd9K3I1Efc^v^(RzyN>Vfey3M-sV!1(km( zX#cknQZif+CDy3^8)b5d{JvxP&YTo-I+`c(P=ge@kq1?BhIGeD5_}q>r$e;=l>Jp9 z=F279Y+$K!&KVbcM!-M9(pJc`V_YqGcDu0ge3UAza=Vxy( z8B|>q8{4JgMEpCjRRRFY8-H`+jbcRjDx4D_+<+b08I1q}5ebh+Au~1jXNlj%YvVXp>#2`4)3;KfH z!G4w_!-)}B|I}K#5|yh&sit+5eoAWt?x%SQ=C}LLL+0t>oq1~KFSz2}{KqkYL8Y^CS*P6FOX1>(?HpsnRe^`%abw!i1I4{#zTB@; z+Boz4^O$G8O=daisYs>OR%0x1pff}D3Rw&W%>$*P664~&hm_7wwg2<;Lr%i4M(BkG zl3rQf!{ehXB2kQrOY>xCdv>Ug|hCW#0JIe4`d_smOFSu9S{?#@Er66DN}P6`|d)$dlx zyUs*Hi=S_~PfNi>`AEfdVt0cOj?ZY<)d+!Ut-L*>Jnp2l#KkK^NZ+2Xzq&r>zZ`gF z<-TT~n}3i>$@yxv@OGJ*6sH~=5!!~Z+ta9NF%hoMBjk1OUm>^od;C?$=Vl@^DU;#c zj)bS+!A0hkE4q4a_x0ryD7 zGGAB2+E*btAvu!NwBYTYuAg%N&ubQlXq&=Anfqxa*Cl_Cw97o>8!)-WBuPS`X)dWd z8_dWkt5`88GN9o+wCAg=HcCtc*4h8!pogDq;6SOpAuZo18N4Y+7jv4$EEZ_N= z4Juh7O=^t7VXP9lISpsx<(YXLAmTsCebD}8zT8s1tt>Nh51Q_xAC|iLNazM0^n9n^ zu6k5}155{WscEg^gj6l!A%pi;QIecTW3?u}!~|FAjhR)e^YS=C%5Gbd+obZcPo^?F zpM5!h0+vQjfht_2eIJ*-cWhABVEJI+pm8~`kabX(Qo zd4MeA(#86jz0a0J>+?43+T!uCtzPkXugv#Hp-An>WLSey?8nRXD@RmcWoz{#^wT(8 z=?iUky+TL zTyMW<#RH|4J=b*WLMiXTx>UR3ON<*7E10Zj;bKm>@M@!IMV}j;?d9?%CHijI@GZtg zdEMZ^a5{tR;5q5x%wD~cHqH4auqL(R<@%*E*$62^yoqrj46>hCtC5?&hpODbIn~rM zk)hB8O7A^8UWO3j_90%L zmaFfd;?d-hGRDeViTf?xI`kT#%aaSRKaWtFv7<7>7SZVE{*|V{)^IAH=R@&$=+9C& zB?ob3U}{M*ZcB{OE$xfB;wkN2*>d!!oX;qVBX8KVcNyO51M65UDi&w6@Ow) zMMgs@WYU-GSB^K?jxJ4pjw(rhAKL^v`z2UZM8xR0J1QP}r4&VUNMT(+4Up}OsF|Kd z%DxwvC@qKY7`%i$Z=7RFZ5u>_jiH_+H?dS`)aV}4Ye4BO zrTy(=6#aC#gG7?Sono)B-ZGk zZK01OG#6C!afobRlkE$VU~*meq6T*D1Ahb_t&Zhu$g)~RJzyCEaoa}Fl-}RQxcwE* z#XrH4RxQv^?*R5|rbq_z1;|G8+=mc08qog-aVce)s*0rMCG}HV zrwI*oU|!C>_WF4FRB=$>ggmiB%_-)_j?DRNP@!#^Rpwcg$9-%QHA) zX8Qa3mtTJ_2s*v`9P?Mo;oZ*PqUD))aMHOWsB1xz@zW>hvxShqH+|Oqae({aA2IO# zqhjrak?vnV>iW_2!+tc5HK>NPnD#6j<9rz)Z}PHzDb)NdnEmD1=!mduyjOECn%Iv; z-5C8S-3H5q?Xacc`2q4K@3<7idE7Gd$Z+0=)wyj(2|1&oblR@N^_;VI8d)DgP3l@h zqKb>*X@oN)uvTc0Z|icD<1+PFxPKc9zOTi|VQvC3f{pdpi-vo1pkD#0)bF-+AU0-#7CK!4Bnc>Q{9mk9j6{;Dm&hk|}9b6-MVK%lyCMjKE`ACldi z)Iu)rHd9S%^q%#W_@m;0sI%3JQJ)F;B-f0j($PeXasvSmA4goMXEai0;y6{T4@JE7 zWrQ=T_T9fMGfZsAaZ5q~GUv(#BYV>zu!l$tY`!!}n%Lp<2+yvy3Tl5CBU?id2~nW+ zBbdw>gcby(G3vxNzmwCt4_`t?sb$|a;P^VXx4U%PMz}ogzoJG6wQ;`K;Sf}&%6!sc zI2Vh5FSnODB{4NB5-$U^UP17BkAm8_9AzGF(%ePhriv;fblkLERiL))?I`V@M~Js$ z|HIY)_#s$j*=xffJ_0J)sP6D+Xu12_O=`zI$1fl0TWM9vs9bJ;T(Wc6Yb`lTq85H7 z$C3wVPS|r-I*zvyvd7jU$GbRk$}OI0(<9HbvmD8r@w&q*P9d6a$l7+#)be>DS&+DJ?fpprWPAGo_cq6!3#Y|Cf zAxipH5Rs$?c!AdQ`SRtqNW!S1*W6Oa>KKD$d?z`-i)r-1=dYkyepbIJ4ozD3V~Zu} z>H4FD`18b+6A5DOUe3oaq!?6rynR=Wtb~cUpMHlfCrrkzf7G7`h?QUm@1S`)mOH46 z&3kc3!_kQ44Hs%8WaUh^V9vuAaS)!GgXYTzXh{-DV8M#3FK$tP8R+i(0$v=YpQ5=) zPdMs;^>?d+nL|(1JDy+**I;r7sY4@XeF}Im+Oyu zJ6$XIHwr{{j8kI8yk7u%iQVM?a*_iXS*l^q48jT@n&QhhTyOBE<1NH5L;!)k(N0WEHq*o|N|7odWFZ+MYZN!<|ukT1NEUo!UZ2wu= znAN+A_|L>99~NeIy93kagAq2qrKAEVVc~cQ$5{eoh4aL7wSf5k7k-(uNLaF_CC0$p z#y)gLhRK{;EtI$gY$TM4&J=Q;`&L@fHaTcm;Dfey^Z0=_z6UFGL60Am7f`!-TR^k2s`j@0Z#*>xVz9r*S3m(w zR$bE~78i&R(27A2NOH?rw@S;y4SZTgjfOJJ?<9c{%(NZZ&^ zUes-4A=%>pS_7>O*!c?GUVCL3FIPX~+5<8J8A?EMe&X}~%!uu~R zRjjdv;qWrRQQE`gR(EFnvMyY6Op$-mM#_nbcD4C>satZemdXpuvL~eN?GwaUp%S3e z+IULSN$yAr-~Y`gVCxK%5KW}j2+Wde&wO>QFC$a}C~6G%*dc@~l2vzBOfU7=ShGFU z=dxK3w)zS`GyldlVv#;x?ouy}>RRXJjR_@b#EHU8>F=m@IJac)9*r z4U9{AP&V&t+NutVXwdWMU$qDIU>O394`Tb`vz;6jsnl$5A0Q1(WqqDu0daAt*iAA7 zndSVvPt`<9J6L$D=HC3h6c6TP%Xstl8LmR&E`rvw39h_730Dgp+D@FG$^cWnxMY^E z-S*@Jgs>M#sq$@rmcHq|v8Xh?{Kg;Z^F`p#rZA?c)UY$vlIIwgF_P6mqtaf-2-sIM zPkO2jXHFXazaRVwA%AFtj)`f)C4i5?ve~%G!C2o0IKn5T%u~oiL(KPj355a&*m0^- zd|0r{=|Y?)4Y5RQ8AG>Y_}d5xD&$&`rG+eh#GgcJ#AQep-V;=qj61{>)y%xnwsLBK z>aSO1zgoe|j%%qy#!{t7%RhWfvL=m(I|?WFNESHXZN>bQ&azuXm{@plkee zZQGixsQu68ZoV1r(4pcRi-vBj62cm`^b%pZK7@MsuRVbV`e;d~0zq4?t}QUrxi=)X z{C3C}mwHK?PHg&RL@zzxfz?7j)ARMSl`@^e>Z+QI>M6~mKngm$=KEe^wrdHN(CoUT zHJ08u+vr(2b`RH215t*O(r^8|k=YKNLw!p72%OCRVW(8*@Ee zpbRxwVHa2RC&RmDB_(hSq+87cpgVP}{(OI`c{pF5r3%)n*99ontW@zbg`^(?9IPfM zLI;s#KF1b5;P!;9KVQF6Q||n7Zs<_}e7Ss>4C;-T%AIOv z=ju00t;up>_>iY(t3G*}bWn4tYGgyLHR_>IOT~s7F zFqc&r@+p$x)#ufwuo~OT02Mj9v07wq6>$7G0J%bF(mlJK-_a>u&JGCF?d|%=6MuaH zzz|@gb1H9r@jS{jg!4k4k5NUA?q9MIK|>>SlrKfFzbA53igK3*?{Jdmh-Eb8 zUFNH|_kgX9jB5M%k5J!5!&_pLR!U-2drvsLuo?B~IlHuU#2AmRXfUplyqGIt7~0+V zHwQr~wqnC1fp|Y++R=JDt0GB?GjEM*9Oz|CsRwB1Mw1b#%HP+|Jeq8;um5EGVjnE? zd7-$;;z`7^kaM3sZYaAAjoyT;_JqCsef265UT)TqMKL0t+5|<;dcW@zcIQElg0xNgE!|_rBI) zY>JaQU6p?;4bO)vVl4=3fUmxu`TzPA$~Ix5baGkg&zD~x?!C*8 z%W&f&&NH^aa6Np%b}C_XuB1AYx@HK#T3QggQRm*h4=z{def0nib>SLaZRZ{C0}X!^Niv!yGFCPTqOrQ6eZ-s+>z z1#M4x>#$9oljAR}1KU^`2_(SR7ZrHk;>4C6VV_ViIK2jVF(2100RlSczeey=s(Z5Ms;m6E7VWW8pe_$7r{pvC+O&|ma=W$D3VPL#HMUaO9#^V5>+tBp$ zNEs(;kR#j_Ksob1^#nhJatGbrH7D@Ho<_K;fU^DoMfMPV%m5s#(A>#HxJ-8!FCAn9(oMNLLfPEnj2VQ^i(lcAk=jP+%NLSYOB&WD z$CoVSLyW<9vc*W_fo>7DP_;OZ1nHLAhc#c#YCg0{Wb+cQNy9jQ{BsE)O2Yaf{iSDO zHhbHR0`UOOr9-eVID(9HQ8bU&uf|?lX!e)a4}v@NYE-XyB~$>CMvi!JME1Q7;0v~J z&YROhH&WhdBXm(Vh4wf=Ey3LUo3hEGSYA%X)_xoaN+@U;1pyF%1o=%SGiqqD4YUY$Mq!23D08Y2Tmw`eUc_pdNx+QC5eH@^cVDP-3 z>gak)zCR=v$*)*~`Vb21@k(a0IegC1i^ad%9dqvzY5n|W=L4qBm8c@92|$%`6+k^v zMG_tX+G%Kd)*VJzNH4`GmFD@a%uPT1ke1q73b}_7&YNy?5+@CgxCrD_+}m9XPf~Yy z^YhD592v1$UCUjaRf(^ZkEa1LhhE!9$9>NT!~lO}uqPgwqbb~!;_2gFuEOML1dg{M zvJ{62#&+V&*D4OvZfJfxZhnnG7P)299x^w%gN2~XDfdeDG(yFQ9?R$cDxo|`G2c0Q zu`FqP*yfVJ^fNCsx5X*&>+p5F4Y8IH?ek2zKMw)043S1PqQ(0=3Gh1asgU%yr>R;` zOTgox9#`3Dr`cg2bg{R~)-v3_4aeqqBj|#kT!?-~T&7Gf zyqWnwRTVoeaxM;Y=d!mUBBj~?0OSTpw|~^qvfE0lg@=aTb*ZK8|}qn!afrH2vWEH zp(04&E;3Ybe_?k@0OfvMGQSZ2{`;T-i43(;`g^pFQBki);Xg7-ttxvB^G75#NYZ-qC!f*E@f**?fZ{YmyHlNy(j9018oyFN*3SN}R@u^!>iss4Eu z=pK*^4W#!)M>%(~ak`U|Ieet6)c!{gi}_(qXyKud?Qe#ecj^ZSc)KX)+i<2nsP|-g z5YrbFPz;;@gE8Y>C5r!kvDf>vIW5ywuNvy943~Nmx(*`yIR7U%F)i!!CN9;_{re_% zOZX`~_$)#W!SFzUa1d;t8nyq=fkufs`5nGkoQ3A}Y<|URm#&DSZK*Z@b3ly0+kA5V5jW0)KXEtLw3UB_ z|E~~VVidjermDKbCaM;aPXDz0O=nO{OsiKNrrRQY8|w@773t>r`e!K{$6Smdc=iON zVY~j3O0_327mDJLNa2BJ|29*O3LCz*=j)F&M^`lt!X?HI$DsLs==Mk&!LI@9WZ|Oa z!7KCYC!*(4?<8j0pD%yq^3<;9*AK;vmBC$XnQt>JP;1>#ja)O^xA31r;)ptQy!`s5 z?cE)vDVy`t0O0%)QutYN>!4DPqk3)z$XT9Q>~N@f`2e@LcYLa;^9?MDVq2O*(OVz^ z!frE+HOV!(MZLwNqqYtNN_)aypnnEu!A36S`$X31Q@RqTN&Aal;DGxzRfcCLfx$5Ei5%;dk{s~k< z$DRlN1|}o;H}xuF4n5id?&k%M#m9us~-2!pD&*RzXR{jUrDVT z>&K=oh*<}oP}2`)@$Glqd+dZmD#Libd<^_YQW!bN-JeX;12JJG^@oUzUVKM@Q?Xjf zk~WS8QzE4m#nSWj3;23k=WMrwczqn8{azC}d&M!wXCm=Rz%(q_oJ-_gK8_IJ>lGQV z;AMo9oKkuKvLY2#iK*loMIlMPdM(sUVE$AS>+y2&LeZWd{*i3*TV>0vk~UhXe4uYb z?};o)Ri3Kiazs*>U)SwrfIL3LoGFc>KRU-fjj)U$A%}Ub$E&5aH)jyh9_QN!sON_m z$2P@2Cug{AK84?+ubfgmM-)`ymTz@~u*DZLm1}?b{cnHGp}wpOs4f-o;wFU4iGER7 z-(fg(4zJ~iKx97t>@Q}mZC-cBTc>U?VgE|D#TnyxR7=+f8E@2P^tAU17U4FxL2|Nw zA!nwisVsVs>k%|=k5?}{|A}ZcA5h5D2m}!}zLVC9Z~>%5DRb;g9MhH-X@UTON2OK& zefg0jC8B%X!2z1Ox&D&Y*Zu1Da^S>OgUx^RI=`7HSpmhVtdEzEii}cITndzD)TiUC z(c)+9JpwUJ^p=M+iQ&TP6h80(bdw%0?9YLU@>Bs!_p? zbaUpO-@E9l1KuKs7?cR-N-^ zDa&(Er)gw-n^XT9;oTlbG>q91&Z0swh^()m_m}$J-NALd zJo`QVNsBnGLH-v&4`7FkxcP<5iJ&yKN7Vz+V5^r#Cran8*}IwAb|j&cjDO7?#-9J< z*IG{UsI=t%jR@84Z#AXoAzRn_N)vK~dG1SEmn;y!-MzVT2G4ip5qcWp1h<=0NJ5yi z;3lF67zZk6n_qE3n(YI5h&Xm5H=P$cL!M1~-*~xx;CY>v>)U)CX9R}L9%*8aEPhUS zA(BN+*NQ0BuN(;oBjzFR?Gs!^fD9)?WRd$Kx51uKwk>H}r?Wbrv7Y+!TA-9wsof{j z274Po)m$La+?n|lL>Wh+)%MRlP^q+R1-Z7AXZH{;MAGI|qX+}s@H|MEh1^(!7IRVj zt3j5Zm>Z`v5&*}Kb-l1fNadvL|Tu zd5AnvurQGUb}WMmoECA@lOM0&i!h)dOv-NYsKRi}qg%MP%iD+O!Tj~_;+hEBy?7>@ zphsQVyODIop_g3fGlstq=9g_mZ?LDftwcIZ7eoGaqNEG9QXKewj~^#9xmqAVZ314y zpSWPy7OkT*3#kp=p(&@WbVKO=x_%lM^A~MDe#Ks?SZYB;HzU>bq+HJjW`4xTu*Ja4 zUt)0JIN;JSJb!`$i8@OR4IJ$tgmR#Zx`=mI)^(t|KaA4m9tP_z=2dJ5V1#Btb%cjfLF?w$rHJqrGZ$EqQeWB@B-y8eFd=`Kcp|f@+a5J-OoGg?sC@R)~D zl#j>j7X&0buX_iU+dp)8f5KMubmuD=cNZRN4>gooI7tw6&fc?!>))&M4M(rP1)|h5 z3oK}JTi%oSbH*-n*uz`VRE0$!iMr@X-fB?!hxG$&g-oe}ed&mP z&|E1!!n&wYLYrPW&{40GlvBagzGAN7EcZvg{424A48c;N*h@pTGLj1vPC53hVRLqq3N%t`D?W&2&KY&C!@wBVs~5NMx<+2pJQ(MUE1w zMs%%Dmrpe4i2vyOM{N2eGb{!+My1%<6|Td4vDK40G?ag=HX{f9r|VZRXwvEC6s_4_ z;kDxoNXf=_-7A?`86H zgiq+unl@sg54}KZd4Gt-4^e+{?(t7jSHj>OYx?kyBa9nHCxBeBHK8l=em=7!ZA%lO zth75{iY4*W2U8t{VU~yed4L|Ir_ICo?&=n=ar?~R4H1&flmBsU)VIQNDmjzI4N7(! z3jLAQ%+iQzDf!N7U6Tl|Y=9tYBmFBP5n|i~v~6*KsvcjJyE)={QzA8a1vz=7C-gK# z43xr(f{Y0@`wrmRlK8!2M;#TAb!fe=XWqdOQ#5UcDcri81H#h~DLd`U2VOq9SLjOe z4T7B;>O+*;lF#Dw6w{m9l1J6`gROZQp%QA>bO>wkgWy5j(Qg z#@HP0TPwj#w^3Nubr_4 zUU#H}Cb4@9&M#w4L5eChWQJ$SQ-HRRt#++vRISSb9m`u9U&cF>?O10$GgC6%~ zfS5rV=eY=%{Jl)~pV_cs_asxP#aB;q9?99NiWq+UR$k4A5w5Zno4voLr;GqDp@P_{ za;xUN!I#Z#<0Tp8N-`b3je3|9N`D@pirSHAirs;n%CoN#LDHxoP*!QbCV(K3pm2-d zDN)G;vi&)3(cIqytZ`mvxsui90GtNlwM)RUcISSXt1T-|D2;i6A9%hv(20Od@W8doFwtUr86eM*W=@Q?a|1~jtxFiV zn_kUWtFYbdi-Ogfc!0Ud31ti5&B^h&7w9vg57@Tn8VK&g^{f8nmp;A&gp`iPhn6%NXDfj$`NmtM zbOKJXVpRZrF zujDPZ=nTDWwZX^S)9Grd#L#FkRbwnb*^)3=i99>Kql8vVXb<0Ygt?cfUQLwL6|DHt zR4E;u&m){w*}?pPc2=C)N-96^?3|-V@X0Bk6Hpu@z4r~hIrPazG$f|mYGd&Ye zLQA=B&ug(^lBf?}^&rEMTO8)UG|E*^WgsJTPP^VAs-fG6wOv1nfl)hWrTvk@ipoeT zVh$%~2w?#Gy+`32y4&;h1I1*LN;V_79yLznH>l0oH98M~_qQX;YS}J%b;GVjn|i+V zu)+*Tk0pVtXGy`w$CmypWj7Ghz-M~(auVBg>fP^ezj=X2cKaTA4dV)c^ZFeVBkA|$ zgJ(sUUlkt_MCUoGY-gt3D1GN@3A6h&LM_7TcGpoqin*kY%6${wS*Cv|oEDc|_0Bmp z9Xj4Z=SDI$M}9eRa> zI4tV!s?L6~De1@^hZe7JF}_!o6E65=ynM~I4(oNu>SOp=wgOE`^4@RKS4QBn*mQ%q zqL?>rZenjVfJ=<`4n3v&WWR`AWQPfAQlOzm{Kfa+J^F@SWGVCrT7*`ujgQA?pFo z6S?`6szMql0tHXspNiFos>E-`YD#0w1_j)SG zzFa+LW4pkQvoXLKT*R7Q9OYp-FE}i;faLHU;K}lp=7Z1hlDF$|ghH;8-4R#L|8I#f z=Y5@P?AMQ+&&;_Cf!JKa0%ty3T+)r14UXMpkVXPRj7}5T*Mud6DKX&eoAF{TGDZle0&w;S3gW1!t~Fan%*Sw&m@~ zsVLFfs(o$Q_NV)F6-;QOb30;@J_V#Yv^lie&%F&%=+$pCG}m3Ep0*H9m2>T{`1WAm z5;X&W?^)Wc1?tudD*oF5x4`#@^CP?I`aN$Ejfjfeah7I8^5>d-#5w=vtJXb>@Ri5= zWq^8tV^BCnfLqYKY>G3DNPsyrvXS5bR2sF8phP73#r!&gm2ebjp@2nIQfRI6Ql3$$ z+m&&@RdH*cSd;^@Gg%9gt!my2`8Rjc_D^zg7>2Y8_m}I(n0cgbHMcS&<`t7Dz$Ol9 zer_QeMK3JO?;^UUnbYcR827zuT~7my%BKEwgQhaEQAY07La2C#G-jQi=HI(8!Yv{v z)dV5bZv$LuemAwB5eY!$i!CT&^vcTGN98|AV4f$~L}Wrf2vf#w(kOiW09Cu5bC3Gu ze*dh6=Ej4vlr5=hDsNSfUoS0_9`A{H%8Ee4`aDKC_!^CaPtmTT3HEPf72--pU{uic zL(ykD#)Uf*&Su-1@V&^vM4>Jfa7Y91Ehm|g<4Iu;~$1}wF)yP z6CzT+a%9Gl-=nos%1-4{?U7{7dUrftfBqgCPoLH3UgZ_=HOr94e5+QxxW(}Qq4_F~v^LR>uTG&GM7W*vt2RN@Vzx7k^rkK9tHOc)b^RiVOL9t8-Nc_#5LrAq zvXa~=$9h0=a|?P~V99dXP3c#->7K7&Z18PO8zkr(trzIJttg<3Zv{otZoSUfL^a1E zFjZSryVnsP&v&Sh24>=(uXo)1!0Y;00#c|!3#3bPD6po$`%wn1vg6P(p0A%MB>|Im z$tPrNE%uu-41~7=WocTYD6%(2cQiWyYDY(_lD)kQkOv4daUta3gWHR|bZ#+bpBm5k z4p4uq{wWD46_u8zyc4}M&C3XdjIgT6vIs#cSRuJ_^QOo}Xm?Wg9-%%E`TWFD$zVM= z`kjHk_Qw(KWFc>F3kaIw(@un{aEBBUhgB#edInyt?ue0eXk zk1ug#tQA3}@Mb>-R-P_^Tk}mD%09Gq{b&Gk9=yayUi=+cHgNz?*RMG3U4Gpq_Ja@W zRvTb3En)a4C7wklQ+Jy1(U-_-aQ_bhG|$%`h3p4Z*N2AY-fm4u<}=-$2nJ%jvqr8X zZ`+Y#fyr&F@bUoco`%S~%ow-NTg$4@d!ah_7}!z&lIkZKLlMY3HIEBy^3-m0J3DF5 zwZ05cFY{j`SVex;wk6+GXFXU#gUj2I=FG;TqqqrJ^D@L*sb3%Tki5-U<4d9mk7Nji zaweYH7cJ{9^o=!GayFnj>hUtdQG%=Q17+*A+<#W`51o$F!eO{1{R?C^ zda*So85!gw!Cg<+&yyr}dqhR4AyVNx0{ph~{#p8V_+aHNhN0*sJfSg;9?BtOe;lI6 z7<7hfDP6YCugcX4+(Uzmq>)%k-AHF;s8dIM4u$3{-eV2G^yOb>kD3eXoc-s(?aO`q znF_sEQW5xm$=vokxdGpxM-N{5^Gj&d!tp~@q+oN2Qjqg~Nkll)rt;HNw>4TZF4%hKwEFfgl;~qWqU0L?mB&Y|qV|YRXG2i}dedqp*tGTS zH!n@5l@Jt7tz-|2L6 znRVjDjz$C;BER$qZR!PW)HfzrGPaM6573voI-r zNq6oahpeqS7XA0}F6VkFW!Cz6mL~+elvM9SOjM9_!PZ;a{`Fqobp8MO`uJUW0S0tX z-)cjtpxJuF@=J_hXVNAh>SCbz*AZjn9Lq;|`JN`oTy#gWbL;E#;+`UeIzxtI9oF~?1E z9<-_XS-}xKas))bLx#4sANd|N3ct*(gCFFF~0l+;~1#3;Tcw z!ED)rFQII0HvuT?Xi(lz<>OD6{tA$SGb47`@~;L#iG&Q8=LlQP)ud@tqqLMdNe*YK)z&iS5^)Gx#wnW|p~Xywe?no006L%Pw%;i_J%R!_i@XX$dC8LmzOpZGW2EV)uD^RGm9fjF`PUeSGKNw~2M?IPk1?o= z6B^M;ugA+6sk;rp%9Sx`ZdbR6aN8HyYlNzj`~@g6Ul$3tYF6+OnhGu3{I2qJZD)U(JB*i5L# zKne*c5_x4W~r z`62{sA6&1Tqt7i_GxPggV^&gP*yF-Qhl{K0;quucTpz*mOG$%T0?`@6x18UW$QRU9 zRAjCyDa~0g8heEO!U6m}K#4PkVEPpDgSHWFwEki8S_pIxG$wKj&Yjzy2U^#c zyVKGV)aK>I&d+1JeI%zhb`Qrd{u5R>>i?Y@H3!8re9S$_fNOvG02bsxHF_E>!*RM& z(N>Nk)`Q@mfGmy(uH_tB^Gx3%CwqtIYID8}kiElZBApYz($gNHoYhwseBmhBvi(w= z?rS#7Th6*(Gut@d!&*_~4+9iro*W!N4l3=cZ0|o}mwkdYfx4I|XX!PqZt9?O;kF{b zJVovCG19;I!z$){w9x)}rh1GYT9QuI7M<4LUV^e^{DCHk`twK87qum$l8Im`2ofYfj$d1TgfbhPi>i9pILw z&$cYhr{nqZXD%;i;yXt=KuWQFbGlOFq{cq<<^>OCl>szNk|XxMNr?6I`T7H<7-38t zEx=a6k~t!@Fk#Tp7W?l&XibCRoCKh+5O*cRiGk~7fGbHC^BCfEav^hw{*YbrX^}l5 zP^F=#Uc)?ClFhJ>vu3_X9-pq?5~X1qaeY%$H`gOB{t14~KhiHJA3#!=A3>z4Ec3as zQ^q$BJ&usCI8;S}2T4=id<3;Sh@o`oGC*>){lk$2M0yO`&Wk9|4)Ns~sz8$!ejaoD za6Ueaa1a`T)v?UxYnbzy4%or{*<(~TZJ?shmwu@rq{ksnLu~StcuMAr06)L~2K~j6 z#rD|ii?)=Jk81PhMWZI!*3beOr(XuhlcaDN_Kpb;cU0>~N6iADukTYadE^`**rcUy z>!dX}&6Vt5o+Lq6tB`JyZwfXX4CewN)W1VE!qi_-4L2vDNHMG#L`Vt@-Vw9ud<%De z&0m81=4?zp6GCwk29y_dBf|~1GD}2YERvs>`1qD;++20+tRs6h?Rlb?{(Si}P~a{1 zcz$!AHr4(3?a-^O+4ECt?OdhrT;nVL=;@jv69zLzL@`&bqiDnp5#mt19SRcIjxyus z9lbT>Zu&w_OaP~}Q0dQqj}Q=d@?j3a*z>JDL(5;cKN%}%)ry_eWAp1e(q3y^3aAG1 zM7~ux;|M!Prtj_7fB)lAvVn-|zR1&#&Ho8mJ423Opz4c{?_3Pfd>|F2W!tMM$ z4+6!#L;`U0HCr-u=14FP&iY2Jzuf(nq~!e%co(tt7M67z`FUjo?W$&KK9`D5S)$)kx?ej{@e&0oC>`snL?gzU{l;WPAB~Tb>~F(;nB;B|i&A z-aOZHSiA~93Tc_138hY?Ky!xEw1Qne+smEz+u1J6Uqca5tYa*r7G=G647nHI6XVyjZQzxm>qtrm-wT~4&P4d9Ya z)b-4)!Ls>7bE3nAS>JAb3y?*P(dEsxj28pdD|pd+So!bV0Uz;E9} zi_3KleA^Cz`i75089As5A(gpHc;8y>m%~9mgD%^V1JDpv^ftm#8>0cj#1sLrdfKnnBn{kHTPI_&z7$NBmCjcB>y?~R`8_gE!1zV_{Sx&P(I(7eOR6xn#4 z)dUAfU(K1G##F?e*8pa&3Jpo|7->qRsR{M*`e|(_YM&|UGA>Xe^Q??;g|@;m**x?m zcYcKRzHs6%*Z%Uw-WH72;T^Uj;Fxm_9Vj&rwj`aM5alA^HYI7rKy1DIx|PySM0V|^ zF{{ZQ>90#QRrSiznl=d<7&bQLKE0F6nLrKaCEMn zQLrtn1zVm5rEONZRb=JPwME+g3@7>nwN!ufdm2y%DQ)D?~xL8Z08M~$X4EU|C%@sriZ{iOF z59-jlgG&o0O72zi2_5QoI|COkL`l*r`~C3X^L7#a;9 za=N@L+E&4IOo;QnXfM|AM%?%2(@+_v8^(Z^QJ zzojT_dPc&`esRHhXsUp+mc&UA{-z_5@Q9Z&MmFC8kRuQ$u9s7uBH2S z{qy1aB3q85stv)PCCE7{GzSQshs|a(84{0I9S-8xpBVs&+k`Rf$=y2ls-4*;@TN19 zXKqs~@)?*FUoL-DVh4M#%!Bz$_LSWOv0sD(ng@5~&uZfs9ofd5oSOwY0KXt?Uq)#C zafnmhigFA?>a3jDHHJ#ArbV~I^cEQ^eQ@$^--d|ITTb?8vEgxuT^Qa$BPj*Z2-+KJ z+nNq*EC7!~BuEX!X^m$!SeiphD`S@09YKKsn#V~OE7@qoz3h69gg6(W96kQ`oF+YadyS5e>>P@*u%NTnR?y9^RMA zQkYZ9`mbZ2Ut6G`5Z~zR2#$6tO8-G+tL4Z$Wl~Lq#+e0TQmnRu+jL3Q8#19 zxLKdZv)lgJXI3CfTQx56vR##r-di6)p0a8>r96E za#o*pjk)ISsZfIaFH1oW~EzrBKBodIo$%ELLfPN(UAID8o$_a_qp3dACO+l z<_XfVq~G;?^~g@3oBsf__o#uLD8XXNpdfn>t!S^TlthC>4Yo0fsaZac5T`ZHBIKBL zZ;fro41{PJm1nQ?kn|M?SwiXPXvSpfdG2(5p9hH5+9W;SgI#?4sL*D;>A_#$dz|8P zr=~1VUM93|tC;0g+$8@u7|VDbp>!wR!Kx=DJ{s(~i{Ac>88yTe@f(@xQa+JQVuwd< z;nrGTMu_SJH(>tubkTGBAB}^r7X_4QSb5B|ps+gj*u|-$e}^3B&}Y*S6UWmC(VWPz z3`$BNrk6U0KFl52$ST~*9U&#u z78FSTkQdw7Mfch<;Bd0jXr+ed_&7kGq$|5_sIf0diGEdX3sB_a?H0yWS?~JzWNY;8g1Q$LI?2Cwx&jT9lz0}e_U~gRLNF-=Bk;)) zo=b&a5fdIUAmb4^Lz)zRW}!00>VwjEFVkVhSF%hFJ%-sP$M;j&TX$D1%qJ1I{q)) zzYkeOUX?D-7-3NhRPIQ_c2A<;*2{*LxP8FRh4mLENx0Ds2^ zY$apH2WR?~&NMki9Z}$Z34-$d<@|Wa6-qQ^Zm(cehF)pV`u?D9 zbC##8mBaQw7&T2F+B#{y{JPrQn*a-&Edb9W(B^|KOpkSLged@ADlLm#q5E z)Pipc1W`O}?orN+_A>RRf&2W5FiPyJ} zkoWh(W(4Lu^~@3%9axUI$ltD=-LCh~8iLf7A*7-a=ML6QRh<>)l}`EN z2uF3EjxPr`2GXuE^a1CuNKWpB`7vzm_aGr)2>^;?p6YJ{Bxz4A?c=ZK5v2Wf-348@ zHI)dWp1B@vl3v0eNWY! z0ghxcD`qW8=xt|m74!Wn0q=I2phOkPIJPq4y39Oa;BSbaP6I{ z1Jzy0s$~{kP;0hq&i-k@Y$_cPmIw@-3b#8+;tU3Kw5`0n+<;e(c#f4@5=69gb*hj6 z|M@cpz^vFR*MUh=Y@5yzEh%UHNjz;xu~Wg*#DEcqwLL93wUUT($G;7EV~fn@upVZi z^{6)3d}zWazw#k>9y$e=`I?sgGf`0RF$(Hhvi~?@l=Q4GJ@6b6?Qxknt+pk-J&jeB z{U}@q65g%zsuZ&klkOP*{$&MKVv+xkC?M*(1`0^nR%xy7yF2wY*uX|j=QY{hMjAY=!DoiEOH zRQQ9*N0ohqUy};f|NTFfGX}0pPJ&=%a@}8R3CL6g;81n$6@&$Ri!0s(!%afA9bk`^ z-*%${rJHY1y>+t$KaWt_J(agc3P{Kh0PMh}8e7@zbCWfN`Lijgd|Js@`~rY5wJ~l z2TVi+$@zO0G$7AiyO<%Z1+&uG9fUw`!Q?%UVBGh``E$yp zP4w5x5J!f<9g3yBGjsSV4sONo47TKnGu`l&H+dZSD(nO_8$&vM{kAnVoZ^LHs&F1! z$i}c~)as1zm86nI$OF;*;p~Q-xRVQ;#Lt)OkH^o@_(oXH^2=!llO`qru7!H-w4|BC zW|hrgm?+O<*mJrCP?@28E1zjrF~07HQ#xPUjwGo85QO9W!*0`M{pyl6Ou2I|q4-hp z{G}EP=R;hetS*pLy2_%irbyuI1pI+P*^kPIm^)CLC6C0Gkn;GoQ^oE2&kt9>?a4og z+4Vd0!|uO+MhkSxp0^`flrev;i4rW2dy*|4FF$HNTR#4ZaAEv3U)Jg*ESH+{ho;vBS}`pKv^#hH2}gG=}r>m|N?u3RX7SvVdpf9rte zQ#YdE0DlPf3W{fH{SHkOfz1t9~ls1&Ro%~M4)?5cSly* zp*WPd@P3|`t$X(Oye4Gu=7vt z=>T^*BCXcSO*WUiK8}R2OHPcU55VBuo}bEcGih11FJM^JNA!A~Oe*rG;4N%#Km>gShFPnXkKmDK*%k;qGSzl13*Z#U?A`T)r5KkoQjx-4+Oz9Ppcatn$YZW3U@ zV|}@PWr%%ZRhISf6I9f&&BVNIbJIj~bjW{^g%%U_+dZn&{8tuGVw(*o~(DqwGyKA(R1 z`Kzwur*v@C?a>y%x@?3!MsQQP1`=R%y^W9ngRy=7CnAQ=oR0vtJ+eHt;vOk8Xx&)r zPWzO(9={B5BhoLLFIhiPcxC&r{1gs+>r17^E3a|R%OjZE`8Dx3LmEotUve$k-b0g4 zhhBwj9u4;Pl{K*wM6k?$e}eLjF=KF>*<|_7B9P|am*KP*efO`+$8%v5ae;(8FZkUP z^NSv+i?P?Hj%LytafXWgJG1u|sg?Kq@#5p*cc&E6d)pBds89B@6yexq|VQu*XzBoUj?h^IPuo|b<2M?}cp>{U<%K&TkY$s&n*B-j8XR1E^{$IZSpstB* zmL%tBF;Rg#Kx9+ep1)hW=N+fnVpTtLQcBKfMnMYA{V~_KG*#ozw|}{E(q0i#UM`_?)PvyA=KmK&xqNTfTbN#$|dT0n;FO7V2J z!k@6zEVj!pF^G18YpMBI3flbRJypT7+=&YB`Gv`wSQQ&K3I6*X)9>G}keeLvE~mZZkAgjC{oK^_-o5~WKII?cjxcvA zx*`$$kS?qB9uc=O_;_%7bW2Izh{lD4qLh^}DIwMNl)u7!lSV_twwoujjixR5V}%?HkYT)$K?ptj^~S?w}@c>SqaWa9(97h@)0ZIE1nUG6MDW4H-Fh~G4`sq z|2#m@$~-#2zjsW4U*BnNJ~KD3q?!?oJy%@iP((_s?%$jsGzD(?>|X;EBwbp8!ktqg z8RwZKEuD5^>PkgnI%9d7TdVyj@;^NmP^8$r_HzGkuyUAMXJp&+Q8EIF%Qs@FJb{Rf z>`uZrJR7p(>6z_7&SiTUAk^J)eg+tIUqV9UHX+`^o-)9(#q=8Q7z6W{n48Dz zr=+cE`tzR%uSpFgYUXFIPq7LtwFx0ehL+UzGaX%@yS)ujgzkB-H6tq^>UjQ|(_B7s z?iS1HL(ng=>TDQsFJE(^m1BQBxBBUOPO?pKTTH5F_R{?XhFF;*<%{Gq%-gDdEir2y z&m&ZQI%-rMT(alkBPcwX1S-+0Se^4j@v)sbBeyeoRO=P6TMDl*zw^xl^4zaDjL2&= z1hv4+FQO54h;PB_xNK5K8Df#wbcnNOOM+c?gRJ1lq4-nKj>`KPiS>(z z8uZu2OTi>z9)}t%2YKDXA}@mdhvcJVI}!6hrJwitFg*a@cEso2-*7XZ-o_JBTb!FsBDd*qJt) zsGc1XEqi|<MRmk;-L1Rq-hXjN9R>w;U*)_kZ z{bhi&vL(bDu5!MKdv?elZn8mKx9=>Zm1sD7qdqix4Pg z9A3DW>sMgd8Mp5hp8Z)qaB`zHCzp#Lg_-f%dor*bggGcQO2r7^rtSONCur;K-qkRo z{iWr2{0LW^e^Oi!0a-K{CSWE_b4h}D93X9TKpy4OL5Y=QN;09Imd@S_+H5U_z%iE- zm!+P1kP3=c+h4!r@~8eif?6PlI>-I3!wBS>1`i!z?#Jo!G$|b~_xL5BM<}r7JVt)E z!m`KG0ApQFMP;v$L#;f8=7V!k!Ft|)?d1JRFwNujdwY7*Y&nsAFn8DEPH9a5q$p-as@hf35PPHR|b2|k6 zVtc|tMM@eKFN3dIyH)jKUL3v3F3)MTZ_gJmlPc+2!()(ul`p#eS0_S4CO$wJP-{^8 zIAD1?z`V9iK*rMmYo!$>$6N4QT%C^tY}!E~E03h){3gfT!ea)md%pZp68Mk2hur$3 z|4}tt!*j5X@CMEA|JPNV%lQ(E5xkD)$MUwR9_{15{nn9@v66DCVR75?zeYrQk`B%R zm}1*}a|MWuble^W2=){nbgq0U{4QbcM5v_1IwGm$7T<~E_volFO1PZ)@IAW z_ev4<(rag4V=L(|A0Qf0)aN@aB#Z65+qP@Z|Z2n`UH)Uz9hCPI2@$TpI^-EKJeRi!9D#70j z?2}|XYgvE|=aC5EwX)zY&`+YFt1U0Tev1s)qdjz8J70D#Z-J7`cm>an=Cd5B-pW)N zOQrqMqV80@b6tA7L9Jxqfo6O&=XO-?e0?CM0#+zU<-XB&>E`7LU2^{v-_3?E*UveO zE7*&p&Da9YaxN6c;Vd&MlU;X>L3Fv9wqQXCyo^x%usV3t@_{uv=Q=cZx8dJg3g8*iv;F((r7=0ep+~3vt;_F2X zSx4&MhnJH^jpo0HsO>jNgYQ@?=JKy=SIGBw1osXLdC0}RJ|5H*2lhBH=j>@;m#9D1 z_%#O$E8O*bsJf8~o?EG$h_sKtDd0&&gWl&FGPCGrulj$hva&`XsW?vTo2!(NllAdffc*4-cYme zd|~(aHqYm)<}m>myH)zF=ze-4r#~f0H3JAHO850hT0-tMz2}}z`J7T;?!~xk2zTm^ z{O35j6^PJyG!;`h+1QBYts>*SB6;p=^M1^=xR4xwUA{1HDfNpP5h~KsmE8#%b_I8c z&^()sG(}IgdjEO=+?P62rh{6<5uevOMk|c|y5o{t(p&&SdQR?}UxC?Uo=JO>bCPCj zUUOm*ey-$0qp;53bN$*v{rBY~zNTPj^r)Y~#(L^eHpB);G^LmbXgzZC*WclN!=Kqr zgxddf`D~6+pGUB((%1`KD=F&qyS5B4uP2yBV|j_}C%FKoj`=ay9IB7!5w410JJyf? zMx*5EQ*TFwEKw0qCINdJCA->?oenKy%jL@_s7-SF{5GQyo5zr@+MXnbg5T4_#;TB= z{~mU{0;|><3C+s@CCdM>z39XmVf%5c)&Twu4-g_;IZ3ohx}6l7!tpjj1=`N{lX%S1 zutdfsI}2I@6aJ_mJRhaY6Q!1UmsHKl&(KVLX z%E=^G3N!mi-~nCK{N7(4)6!mT@fgCqxt%tyTD^ze;fXksVgw(g5^_h#N!;_WhD*>? zPTHOasDuDEO&SJrn@GGe@5iLZE!`DIYQ=dxG!vcuEUuS$(y$v?rRlB+piF!>si zkw-}-5-Wm-UlA(LN11PKA?9;5cYgC$YI|+-gui&>0nU}kb?@H>Id4$fXO-}X z$NfjdH=L3P`2%KRyn!VQYa_emlpMy}2q8ls@k-8R6sf=Dq&ROBP>9-lDRsmFkZR$} ztd1?XVWf)Gx#RUS6d{ooQdKtKuJlX~o^#m{zV2+>!VCq3#`^ESAkYFD*$AaZvQ-{O zC_>_zE%)uZxr*Sa$#d!&6*KRQhBZmG@@!ImCUO<}wj3$1zKpOw-KH}vh)q-~{dudy z3$%$kmaq{YLf;Y{K)*Pfl$Rj_?Pv}O$~gI37HJFO+>9fa!{InG*&zjJdD_j4-vlJm zvwJKlua9g263^>LBiMVj{jJTW30XyP2{+3FgW_C4T%XohRRp@Gzh1v^n_p;RYk4$T z?bA^ibECGJNZ78dkt6Wn=#PZWy!kdl;nuF>gYghu(=sB}pO37X_8!s<|A4uV3e*`M zl3hN;s^6{3&A!{XCnO7xu!+e@mrv|^kP@MnpGsA|P+4>^l^dF?{`zL0@6ccsNBsBY zQ-P4>NkAl0=S;@gH9nG9maW2fDb&|IyRo)&n}Zp6;moQJS09yP3^E6cwQx&B!@Pbd zeiK?E>{}2i16L9{Pa(gtxv1H1kC)HN?6DKPrXRmoPVgmZFIhs7H%fk4xeh$?Qnj?= zH}FjdA4v-9l^uL^#(Bl=g+}Wac{yjpi z%tsrgnP)Q)t z^Y+dmg^(y7ynG9l*M9!EMeh*eIgLu%*kJ9F-89#GJo}_s=K4pX?a%Ot&VD@Kq`MEC z9-x)US6lH75vb7UjO-w{-|_HnDS)=JMzO!{QES;GR1yKD(vWMXV%HP0n;2rM-p0~# z#?>ZFSLSW~ObqTm5<*%}+?teKX8^xP(u5x`f5K`2Q~ms>^0`%y_AhpQfi;wc=z!NF zgeFDnP$REMJI=@J&nfgG0sL;Zv@`iCV7b!J@*`{ybmA@X*)8R6HLT@`89r3Y$ID-l zHOvY2$C2@Pc{`T*o-1zBzM%?4>h;+{nhP6`p!>SD6rq zab!d6&o_wT03w4+IUT`v)hBouqML-Y_UvA-pa4mk8TwKYJf255jc_LH9v7>7=#sMc zId3?W&b>71k3%ynjQ50}&zAghjEog{ki|5v5*(xE6?92&yU$nWQNaSk($vXEmI;T+l3JfH^P8hEfJB zBXg(ADi-=-A}euLc0y*%i?fFPE4#?`2u(Gut>871TQjGdcy$*m@zVe)w26!oWR}#W zE!qLhIdw?l*e&JJi*!qbaPq`MsS3|OH+W89=koyBlQ2L#m zkX}DxQww#7ZZRr8t9qR-X`Tk$2Nr{*GJOOB~yCQ%P%~)|_{r^vmfU zM`_LoKzzUaq5Z0`+|>CL<4Hf39#9GoD+Vz-*(lVyJ{oMYeaDt5K#KZS9JrQA?BV)h z(XmNO^IHNmNE2C9N8wPpm4jcT{g|7&`QODPW#hoN#!M&+@cH+TbB{ZW|I<({NYVozk6 zh2byEpVXs>lh0uJsTw%Fn8?H&MDkBPq5Z%kx~|XHuY;|*KT%B78Iio{7kzN&6R1tv zOa*dA^&xeB4@xA$?o@hAfb+~7Er6rOd|yxd8m6xKEuuWk7aU6hrEO>#69W^{X`TIQ zElvy1y3WZZZRhWcp1)27l+K)b=EGhF`DmBzzXlVMXh6raIaASLNOfD~L0QJPrz?+N zr#9rLS?~GUJC@xDt^d%dABYtEC)|!?NPl1Za_zZOShzob4whU^R*aQf0N316szrXz z&qwLwU3`*iHXt1j>;1o9W1i*Ji~k?i?qo-jB8ncZ6P$~b90;gM;+Uvd9mt(ZOD z2Q?$`FMTrHQdm%pEX&}(muwz^HQp0nAimr6=S5|q(*o*Sa|wv5M10hc>iz?FAD9B> znJQst9(TJq=E|)98k;^R8skDTb4b>_)Nb$DeEY^+EK6QGr}H7rVL5MFr%)0OLK1Xv z86r~(NqfqPVEOlo^P9a^q^0@8lO7hFsR2<$suOI^d`6_I3#D&g<1ny=H)c5GpvfUv z9yzc98fj;a{!GLN!~=?2oDGW@8PF?y9aYCQI_TG?frV(3>hLB&uQQ|na456oWPD5L zj&D$Y2P2<20FqRzQytRlil{s!$a)_jQor*d^1XH4T6U%5ozPL*t#OcBv@<2PMm9SU zbkK3czK7*=E~>ES9(0D$akc75LW}Z-*fJ!eW?=JQAUQzToQ!KM1Y~V=EzkQ1j>hme z-Zb)#5*P?1EK`YnfV#~mrHqq{?WNC2lDDI(51O>)Ldbmtxit$ zd@+V^B9yc)=rBGbJAk8nRD*LQu>jryh8@E{J!1IZeT$FifBm=B^H=5^OgBJ$&!Pp) zs%?uVs~OS~iqcN@+gyoQbrB`?uUxy$@Hz_fHmc6?;c^F}Qg9a|N`-__o0g@y{+7aY z`_@&SS1T|TJCuym9SR(jr^%_K7-{AGAwYTYaLYVmfdYA1FsL}G)ftA$Ft6ykP zX)-lNlY#AN{ZIqS*7(O>%~v{UT@)g=U_3izI5HHA8Zb;{uy_31@puo4DZicXzQh5# z0||_%&b2FLXUxVI>gC&3(@K@f+{WzTlxLnI?B#v+vKfs5>3xL8rLu?A76a5zF7I+7 zqPu$#p|=6r{U!SQ7Mz+7EGGKWQ% z>=UX=1P?!T?lbx0*9zHmA0Q}$#yNFRKgvG|`L9VC7Q>pQ6d-Nv*wyr2!a@9`z+uX@ zMIG?zK6`=bFu%*omKs$1gOGogc~DCKRsBG!CQ|Ac3iIU7)_FybnhUj4jI-QF6w8Xu zHy?)lz3QOr4egM2wb9!=G>oFjSqIXdp&z@Vd8j-Dh*`PjZ-0IF$lCANc|0gzf4xk$ z%X}HTGcF)>Pj_BkM3lq#yo(S=ZTf918R?i&R0Dr~`{OA6IT|2TYUcYDcmop1-05O7 zv@H(-wAw$-yBC5h^R~7!_d!cWk68%wMUdORzoy+(PTU4qk*pf3tn+^PHLBOk%jKB) zag4HqjCjLv20j1`g(MK;thhAA>r@+%(n5cT5CQb%%H)I!|62jzV-rd#TaFl9u$x2_ z*xc~oJ0F&f5&C>jP3EP@jDUogoBIyF*U0&6k~YFm_Mp?q!mVA;;K4Kk=Ht3O1cTht}@#{Z|mj$GaZzRxe3h z8umDmmVz{5H|coPxcl`rb#t{TMea$@H_TQqnZ2pOIafV(P z2sRW0w_s$Dp7rJE^Go0#j`Uw|*1yV%>&-}}^qe`X(X}MLa8>#_Z;N;A$Xqp6N}0}3 z7VsF_UEZdxj&rM^`f@iSQzgUTZ0$_s@6>s`~~t<&XI_RSqX%jk&1a(QSae zcAR$^maM-acl2_=On1-ZU+q0#IkqgHOZE)Is~ma{%jdO)guG%z$=*FR*1G%z%(*br zfM!CYk!>e$xW;viWwhnrs=(-LL;*_ekp*hN%Av*#E7p zK`>SB&Z|%#*i*bAKn({A$Po7_$_{fMo(*UQqU%%#38#~sPQB<17A%^^<79&UXz!E4n@yny4Eat8w#m4y% zVVIw;WRx_Zm%t!nwE*7yeT(xoIlY;$UK`0T(}E0$wSXNQ`+a~b<_j}XJ_GnUP0cb4 zW1O3V$gG6;BR#G15hiWp6w{uOW8JO0X70FvI)9^?Use~*zW~xbq}M_)OeUppMCu_v z(J-c-xsy%U!|ipbN@sqMR1pklAVAu(#O$v!uweZV7m}=*-nb?dm2%{lm{Z(_2=~4H z@yqZcLQUEV9%aOgc?Y(T(S}j)bwt2s3BR33x}4+bTQGWX3$`B7#p8@u6v3Tg#4R=P z8~h+ug5!ulYDUqr2#GiNZ^je10Y;60-&~mH9<~()KbaO_(3M+3!$4Nw6=ox_b%hRe zCbHneQ(8k-O;aLaM6+ks{CW{cNzw_fI}ySU$5Nu#nC2)6=BARG!6Zn_nWQDPv5#m_ zQo{Wx?J9VXnA@07u15;1Q`2KU21`)+^ZLUQ-3JKYCpvTKFnf86`w^i*z2arBsno$F ze5N$p%hM=I0ayyXT` z0oI5%Kmy>_$ACLdyzl6ba$D^x}y6ex-h@Y!#60$h+O;;H8Q2ieF*cj zL90Ta(!0EE@pfUQu6BP2P^rUAcED^EX1y?wE%LvwKXV;TNO9yn%~8mKHJK+62_2^A z4*^2?A=85!_WBtxs_qH{?D=Bv<+!aw=A{Mx+2Xi_5yiUIZ*NZ#atq)5+88YHVUZth zQ4}^SjTRw(d5DJlb;aQVS=QhuBgv2o?m`sxhXu=gpCK z_sff@{Yx zPk*v3$FHcl-z{I-?e_c5J<_2+0*sv1df_Ep-m7qLgWnpzfLTl#$AVH)7o}2C&(Cd$ zv_8@sdbPKTgs!ct8*&^mg$lZ~K{Jd!Gh`7wVrM+u>`%wT<5My=@{u0xFxptR5y<+$ zvjK9(_oCRP{fOEm>eTXZ8%oAzWY+7b+K-G(BBIH2vr}_ufjBM^BxaWktDR>FS=kiA zJo|bGP_3DTJhi6VtmUfP9%EX2^Z^5w?B0Lh^uaEjw2w0X+#;yFk4RqKMUYx=pXc&U zmMW)fq;D3F1~_XFhs0CRZJh|Q2j7;6i)?9Fr$X+q6Bn0NJdrh4&!+57V>oMx?G?7< zAO0Qj)}`_W-UIg&+66f#MyP3*rab<8`5KY4L^5WM2}NWC!ee*PE-{1OiDWOrXyy3FIY9n*9&t8j(y2#CrgptwzD7r$iXVTxv?6AGWM9G$ zSj+8Oy?^TBsXO4aPqfwfV(>ByIqPoy;;z3#axyTS+bRvm^x=;B`0OiLq#ZHyv;N#b zfxhUdZaPGF>%Sz6&1bL#t8Zf8{L-EG_e`7YXpPJt1A#QrNSb=S(E7Lg<+Jf`-`1*5 z-SoxRFHx$ZPv-NhOOL4QLCa|nL8qNb0*Rs?_aW*5I*bRkS5!GMtETl3iRYKA7P`w4 z0E->R?#*vzrYc0lD4rtJ+yp3)h584ffoyTKpYf4VE2$)N8Lo#GKYht}0~7<@8wg|2 zS=UQsV6ijK>-xbCM)yK_yhjJ}n=7aTeT1F1M|B+Wyc|@|Xx^ru8{q-%{wQS|-j>bK}u4Ib3M93i8;t@Y96f89789OrC@ed(JN|`^qW_lpY_9N$e zq&YrQj)mS>oGs}~v)>EQf8L;T-5vs50&L^`+|TJk;^E56Ye#$8&0M-@3xKn`$pr=5 z3f74b=J^n!wB;~4P<@$j6=7js>;P{Gdygn`7)4`utnqe;N~+IpLp?l&PgkjmFKkbG z-9&_H{s0tzmE{`Q6jV2N|BTD|JY!-M8DJ=y18v6f_$AV~)){8*!9?TS$~{kl#1)N3 zOzwmc{Cqm)tXd#sG2qnZH;ARgc)As9%KQCY{C4S8`Y`fsS1i|p`X|lFf)?UT0I2|w zC~xLCU)={tcLj{tB*}E^MRNJ;dCi@QTZ8>QHUJT(0^nre`VgS}+d*kZ_8dYgK~ue3 z<(=hXtX}1Rc9t@OowCa)kLX2cjqA5vam$=#Wy+TtrVl-YCEp&a034ZCzz9<}oNxYg zN8o+)QDvTr*rey!wcF)K$?$F>1n!6=V{$bQ%NZon19SNw&*N;iL5R#W;JV06>mkj| z>u}r0!>1oFvt7enFy?vE1b}R)y>Ons^x2|DkGFBs+7r!L)yNdclh0iJyXCLDk50_@ zjkJ@New;#P*5_}0IMR~b&@WI7kNefvt=x~LuOkr8HKHmgmub-;3kD~k79?^H4d%no zhgP3XNr!#%uzYBJ8^$^O`=%Z$cs8>Im!IiWQm(ma!wk;XgL#3^KmVINN5FdVZoiH2 z8V4{Qmaxp+C8{Tctd!*|_1+_w-{9EmOSN=yM?Q5e2BG(Z%;4Sa#ytL-8|i*MtY4fy)izB-whNg}BWZ2C9aqih%=i~djjN7YaVyWO zSO_bXvk33qtp5dkoX62T1CAak=jcO|(t%4MFL%Z_B;}aV`sxXyJ$GbpAjVbimi{jh zyALvYbo^`R^oBv3_pqp@?-hSm$GFQ>4Y1wF2AYx!^3~(ba=U!^?;ajqdrMp|+s~^c z9fNyZLArF0J~K7e1t*N!&f6tlY4kg7&*e3;Ja`G@XSqjsWR4*Fc~AgbYw+vF1mASO zewltaLEtg8ZC<9Wwt2=Kb7xIviu5(W3e?%g^vMxZO3t@$yIs7pXuvKL(-l@E=6Hs= ze+;>0*6dj+d@PY}j6{RMnW3?ZejgxdG$pSkE+{0#0&dup*^NXfEUOiOJ?qc+ z{ESfxeZT#F{Th1e5d&>_ySInWKc79n%hRzK%rng;8+=90eB{L!Q-sS*>*xqSD|@U|4*M`me~m5MBXDvWVwP2B0Ap&L*x`_CWb-0nQ3Z`fTukzUrMea?~*>- zxbB<^uAesCQAC}U5m8bK!%oLlBrY|Ur910GfZ9r04R^ppM&M71SExAGGb-MPfpTc&gBuJq|I^-*5i;Ix!-TDRC z;v3*}02wXvl{PR@{QPXe3T;V2_t-BiTb=1411qzUAD=>$9roctn;)uRKzQiiLZALj z26VkQ;3>x%-nHw?_+N-6CRa) z1PlrC#7j(GbjASp9xO=jr%SiWQm18-PgdUf}z&eWmTHTm?-C8w{kR%thxprkNV)p}C*+rvSkM zU!c68H<~H=5(YG#<}A~e>J>4!$~HtGRH0pt%aQHfQbOKGX!#1LYH{YcGB(fa@$x}R zHGEW}?kkRn_AHUEDwQq7Q^fi9c%6#m#3s&b9nNRjPQ%l#dfR}bx!`{l>my`~{L?<#J)GiNV=()q61cBSRjq~_!v;{}VS$ev(U_Nb88@pLb?LQPXJ zX6Jy5bOZpReft9)h)`ePTVDUIL^==EpsgkjGWK_i>NGZ! z9G%?f%^^a>%;9;n`lx;`s_^U2AYNyp!Da(7f*GvW?{MMacoFJ8;xb4HJVM}C?MQ#E z9w0U3>^mZUu7CVAgUGGtu{Dm?lZ4^OG;V%$_-H=3Fch}|)|7EG?|pMA&jC}m_1X}y zFX{eJh&nHQWLbM_{eH#$;WHC)e*Z1PlxnxAsKe2IM2bGIRJ=W+HhdExB)qT``5eG@ z)uBj5z!7p5x$v|lZy7YfEd-9_<*-R1rH#lc-mITPfCQ1Tim~`ncHyCxw)kI;6ptAX zcughGR}gv7#=Mf|V%PO>feL+3xnMrpiovr*6*c5lUp%t%nd1+@?6RsxNS3b4Vq2Um zSP9*%zXclyf)Ni}8*LQsu2ZW7N@Q-7C4<|r*;8e%xxc}h2B#6}^?Ano1dN(L)}WyS z{!Q^tP7*-DQTNJvSf^`%?gk8*F*&-TjWh2vupd+Ms5a3hIPRCvjyZP$@SI*h1M={% zc4~Z_RBAd;fM2dStIoZp+B3publ+oPJ?s7$Ww2XkH&o10n&W_-B_C(pCNUBh>$D`Kh5Ol8uV{- z54aP33rb{*ka9V5P%+4;A&XW;D8WMMl z=Mg&p;rw`F0WnJha#eu5<{N8tkih$sh-Pv=aI%+jv%CpV9V`y&^T`Hq(Law`K#=LI zT_PZ8k03s$eh1pzi(Ij$FFUtr=xp3zYGs|2${v@M72AGuTq~S+!V4mohWq*>lNiW6 z$``>98GQWA&VW!-w1Gk0uOFD2o=X_ka^1KXo%szHw~3z~eA zSJ|>l-vk&XSx7Hh6HgrCpMFs_?fEU8dxB3;YUUIPJ`AayS=Y4=H-+=Of ztHhdb>7_A94#&Wq=2g^Us3ht+&%-jpD8$OCU*dd>QpdgtuoiHWT7AnLU=$j!?6!xK z>)cDpMWbbj>CrqWD}sjo%F8y2Z4VczP?e{{a_%iLU))Q_Q8K$mG9}_d<1-?UD@U1{ z&eKH--1dkm^HYE*N1o9ktdSQBjJWL5x;n3jysbx(NDf3P^WNMessAh!a7O zFRP7`;g6pgFJ5bWTt^H9`%6Iz9PDafG}nCqxXVVdNdM|25Oda`6K{l{v-#vGCH%P` z(l1m<*#|zsOc1?EFdSvx+t^sqw?!(Vp>!5acvrd1H^Qk!60*Oj2DcxcT};yPi{!0OCLmVwbzj2dvvo%iR@LkzFS5yOjFfhNX@EL!q| zc>_-(WmjCaP1BCYdr=e01uvWC3|;y`t}2dX^Nl|6j#?6gWy{DJmO5YJNLSTlPMhx` zfFyhSY=7{iVoZolVeqd~wiQD6GT~c|pYaKK|ELfo=Vo&2QR*YS$!yI%7MiefaT0nT zS!susK=~`BS@AV>%2`)0!HtI4v@yNUE9dkY_v@D~KTw;TVt(6R1ksHxP7-Lk+60bf zhCIR{anj&KY$?*UiW#tM_v>dSDSsrmTV$q`Y0^1()(Fz1u4m%H&S36?XE2OdLviJS zCmVqx-vqdB11PL1VXXzDO*wAXe3sE^EAW#x+WY`J%2s>$0&A+6<7_|5%RTm;rBW&`E983V2lqTAP4l>i3sJ`J z$M=K1AnOx<%FzMOz_c!GMv-jb0=5{u0kFGm%e<;39SxNm z=$5+r@(^Mx;k9V4FU;JIh=fr_JZUD+*EO1ru}(dsKTIa7!-18IR}}1qe~a1x+~0gC zKEQepVr4-~dlC$Qkm}Ezn2e*phlr!Vl2Lw2A~8ySS}H?+tK1}SmoJ^pZ-dN+AswTf z^8EeQ*$1$t&(~<2fqfM< z2Lqk**hT8Zc65&W&mR`z@>0szGJJu7qw4pP_$xe6rm%V_OUX+qHdu$~Ofqr0UA{Ef zi4nzXZzXS>k6&=P7k8s*#k+Ca>DBLu8#;&1k{Aiqg!pFtmn5e=dW%D8KZ5*fFT|QZ zR$$qFM7fR_rA&ojuoLlq)%I}JwN|2xBE3umv@E;hPKhX&g^nXA2Wbq7KX>c*yQ`kE z?2R#7;`;Mx)I(Ootnt%DAJIkUubXkt&H6z;^Z0d)%zZC<9qAj&=^a4qQ~=~UyGD7P zir?}BaXzi4XlLE7Um&dWkINJ6%Y1`={wTw!1-g-yPX^m_=OHM-Em^0O3 zJrBxcUleB1lEPyq4%cnf&Me;_ljsfu%q1#5yB76Wh01=2M z^$4GZr8nE@`S}@McP;w65Y=J;;y`el>F3tMOW3{zlxmyMOwqNPc_BE~g*K*nL{@WV zk(z?{0g5p5rr^7uxd+$Jfxjof#hqdFNY(4I`VLC81v+blj(8hE0Zc;6ys-f)>5h80 zjXUSZqNuJ>o$U%-pYA8VCmpLsDSF9e>t*ac)CY&NnQkB)$H!jv`nQ+i+!Hge#}Ql3 z7c(GYOBnCe!8SyeA7L5UEBW1H}kfo8#s=LJ(i+p6H?Hh5vK_pOmbT zT$*hCZv8E|YE7^MCk#EU_l#8ZguguQ*XmTH?Y*@`m8>^Iq9f}UiP*z(cv#^4W|8lD zMyz>0t&Kggfpc17KoCzF9EC*;40?3^IJTwAFXDDpJ)bdUlvs_09paj0y@LjnCC$$ z%^VpE6T%2OS6BEcz*X|Z=7YpWGk9i|R(N2V_pA1|2z!r+;WFQVBOu*13IYs!0JtA- zLVYc8ef{urV>s<~~?>;V&TiCY`A*UxrU*67?TfaPNPhp~fS8S#M@KJi0@ zLYi$q4j&*{tiQt%okhi#PT~x~aXDK&QRKpaMdmZr0tNfY3l>jxjyRz$0JHlGHVr;V=iBiZo-{{>)2IPq-njTQsBF)oZMwraqbF(sL@{XJa`90}n9yO7GnOlCP@#kMS86Jm9BlQ3-gne&vYR z!x3r+L971Io*u`nshskWp*iu}^^2$)klVw1GMU)H`XhIG6QEA$FLSBGs5ULS`I366 z8@kyDGT;8s-|xUi8*xK7vyL4B%Kh!~>r3e&ax&!?8KA)8?zhzNutp`tY2G2140YNB z&A?8RFk;~M>!+FuA6DwNWyBU(4M2zCCvB9o)R^IrC~kTO=r?s4Ht1Yscz4UEhT&zt zY8NU?3uxlFou^dvQz8bsL`0Al3U5fzNb{e0M#r`4Y55dQGW7ZGA`F-l1UQ8322-gx zt@U-30RiUyWI_9>7mx1f#ToaH{>ziykW_9zvp1{tXXJrhVTZ;!?CyiGLJ3Ao$_NPevcd}(Q{ z)MDnn>#+t@b;rm4ETS^AAd8dyo#o4!v7G!@Mz$ep-i9b@xN8K(JNJ{36yi$n5GicT z)G*i_qflH>P7|j9tO-rIS}@GpLx8K?fEY)hF7ERmHtNKef4YXnUod$+FADVeCe|PB zB~0ge6)5s{{W3Iy%YCC0&kdSjI`zG{zFtMUI->d>d#U;Ed`41JAsV&Erz=!#E~aHC zn8=lm_iX^f%q{Pe9+3z7FM29$GjWkif6EYB7rC8_jDJ^(a5R$16?oJaMb-M zXKzX2dx=^FIinTf`b(I2ua|r!dO7N6CX=9;o*kq10HKsZvmP=I zTA^5;VnlaqUJ&{xi1bvRO&Q)oiWsaL+Q832_K1kB-=xZQD+x>*l&GcL?$-*Ux9R;seJMV_H{E!`Ch&Yi z);LMg#9I;Fplz>G*<5sEkA>#LT9}cvA^8su2RFS84P-d}v*tMW5n`BS;JJO980K2_ zHF7DF zW5W^b*lmDVd5);4!3BYR=6X;po>vQ`_KH3D9=F>CJzy-sjs=czs%sUK59^nIckItK zLk>8w4DWZ-1DS@wW^T?Eavq4omYs!F@!{-qxbD}}ja4|`eTC(1Kv+(hdJj0KcCM)A zZup9FzRN5i4E5Uxshf`WyiPOS63C!<$74f@D1BqnkJ^_!9IH*G%t35T{Bl=x5u(Q z1h|UK!+e|-Sq~*Uo9n#!o1P9a-Xz}_uo^KaOks8fElRkE&yi<)i1314|KlQ=)gp;f zoQq9gYEsG|5K!6>dxojrhLcni&)262`+WqJ`cJ?6C6JR8Z{0!=D}BS3f@sX$QD6Nj zMk_+nw_nRroy#@ zKE5}FxJ45yLr9X@4WG}9I+Gi_9l5bft-+lcn%#N#6rsnWiPv@L2uBNA@PIW`X+?v1 zI9JKc>u4O_>bj2M3D7lWP;|e1St^AzJSyki*zurRxiea`ZT?%@gQpQ`C*^T5g!nGK>EJ-1-9~6zIJibs%a&s9 ztfZbUYLTn&Beah`i@AM|EEAtVN~Qz{-ekXhEc+4@3gG_dyD;2^c?iD!@iCZ^zv&}v9@Q47+zjq% zvK$K5Gb7lYyz^54)=b;{x@|juzQLtesB(r>7mGutv^KwWr)Sw z!Wx9Q@NpC`?VmcMlpjeGA_Ee?-<=N4^aCz6CVKZFg2P+>Ax3y^u@N!PF}>#ZcBK5( z7dV2j3q6_cH=Ln4ERW@=9tJ+4tkVnd9Tq2VUy&Z8NHT9@~0A7kD~QGVBW_d8858P5e?^9 z0_AFH>U@DF@>nRdDHi-=N3~PWXt8{x)Hy; zUq1-X01aq{eRq(fKPm&;GhiFBY!5*FNb(Q%wm*F2E4-nY$81p$;Qg(=%iP|SK*u;O zKlsXYVhlN3Cad!v>J$*Y96=u61b8*P&nUCc?IK?D(Tt37^z!-#N~pSin___Vfu1_XJ)Lpljkf{H0Y)*<*39_P z4)3m!H%URx)*vDgbYUp6>kB<)d20i-LfZCF zsVTFKdfN6qwxwK=X7(VHgt#Tu;DrMNyjwrb?)W1{$qG(5t<`IdAj{WZ@!R1vH{WF< zNr6t95OE+kK8gCkeTaCl2fD0hY7~=OAJ7G30R7N(J>^_nC}66?f0+iQ;@7<$jILp`8 z3VIkm+DK(t-{A@oNZqifugA$ib`?-%3~z5E#A$;1+!?DJ8-xJs59uo!Aul3*u&~A_ z2{3m%K8x|ej;rCi3nsbr-^=rRmIjw zz8PQ|ERl)64dK83+kgK*|G)p||NS5T^It3Y?|i72um5%R`d`?lU;pbk{`G&1|Lgz$ zpa0{7sAi6sUSuj*Q2WLRLN)?aXiNk?+K47uE3{M!?Os&grlO3S^Z?V@{u7@j=(Bwr zKZKVkG@KfD08hxSrMIPGV=Nt1&Jh85jX$n5-jBBbCq7QVYV#$0D^bh0&$xHYCwt!N z(y&}O)_MX!A6f*1=1X=ND`0>ISNXsHc|z=>Wql64yk_5G%N7Sju(k!VV?BG-Y}a0ucnfVJ*fVnIfU6vF{v(ghnnG=R1HYG8iUywE zE)vq^?!k)8LY)`SI$BZKqrJJeOkdCc%+=|6bj~BrJD`2NM)4D9E@e?F}L5=nhOas$Vj&$F_O zMA|XJP&Y;HBWe#+e3wW9Y;oP5?`$&M`v^g~%#FtbMeL3^8mZ$giqcn{#+R3Q#RiW8 z>t-ihVvMpFHqzzZ*QXoonn)Yw(=*dZq?RDzV-DV8HcrYnZgf}NL86R?YqG}h1q)GQ z++6J`#H&O%%)j)X(=%DY}L$M*m8gKarIJK+^uw+U}hq@b!9|! z#H9x*F6youVU#tRA-BuFB%ZXTkP<*C%BSc{iP+CCt1l042G#n>6sThfCFcM{y`Y6(9+*35l^9q%QBva#0Mk{dRpx2mLwTef|TYdJ(|_D2d=tpQ(Q7|0tA7gXAhi z`4}16@&%7P&wYe?Kr~>NFGIM52qNTqji9cQ!CKauj2wC);MjI@5^yI#&{KrGxu`j_ zN{PYVR)Fa*=74!zCqxnWn7Wwvyy)?qQtC$g*B#Vxel(>)TSa!O?X6O0N#R z-u@J!R3y%C)jFOcN$=>J_WBtNHMN$JNw{vY7cFB?vX$pc24lP?Y#;ElAj?kUExY^S zjZ`y4p|!=K;7t}}^ewen$S;Ck?8kl|VTjQF;3giWX>`y5AgvYAi>+Y0N3{7VVfzwWbdTJN z7Kr10h)aka-~fbwFIyLawC{$qTQzv%x3xwQ;) z0g)PM+mG64B4k?4&^8cjsnZH|;@$xU9S;$5tC1+2P}EXKv`k{s#h}~IYQMzSTJ56F z3mOQbYG0uj=5>0w4b}L7zo9a>m`xY@jr;YNKV@!o40_<`olH@A;+h0H$<$;Y4^L9{ zJ+z&F{^<8#0ftv~NF6jHVxM7}#*pSr8}l|m`e&eN2FH8J3ozIYP-<;4+^QB8E(EMm zkMnLi5>ymfN(8t%9xjru$K6FDC3D0U+OLE#(hio7ePX)3hW(L#pD(Ke1uTKG1!f-a zJvh~mq+qd$p3gPRKJT3 z{b945yy6et;No=3DMtjZDCP&(x0)p9ZC*0N{CoXOAWrz3o(|@^RV)08l__W+V}tnI zQEAXMMvgkmvaqRhHUyt}DDJlbqEDd>u~Fdz=ug%SCiedEj~M@sf+3!9rlfbgF1$7` zGAj2Me=Pxt@&2naMPFN=9e}Ll0%R4($kw?;(FTB%mm;~!KtgWT&miMn1!Y|K%Y*sq zgPUfq&jb|K?u-iD5#&_Ny_Ou6cPody z(ckTUN({?;6`FTfd+8cUDy)ka4=_ao;py|1X`_=By=|5p>qtr=^*^bQQw_)H_0 zgUvW$T(%5+6k9fqVx_ltzVgeboU9|&rO=psgHC4Xgk>dt7Hkv?+kTbWWxEd$(%YUs zGp!92!MR8A10*CWR4C~jp>7|6KI|A@EQ-SXph`CVfAHr6PwPLu6j6Th`+ zIPL6HKnD#j>mJ`kxE>;~>);$jA9-Z9#Gjcxn{LW(z_$`xiSjev`@{OhN|%Jq0EFbO zy&lOOBLnX2)FCni++cZFQ((=YO=<(sD%4C_9RQ2)#P<~dC*H1vZansRljB05PO)@TX`R#DCwgT=*p&ZOc^>4$IQ{L*Vey) zYa$iF!Y^t38ARFT<~uo}1xCSQ-f<>?XZr2)>wQE~iMA^?Cj5g;oB}-qY&UnV5M4p|R%QW*Zec0u3qX zhwU*!NwV$Ro1ji459duB?ML=CvxqBx<`5e#3iZ6(HqOwCF<^xIZ)02;Cj4+DxD|&W ze#%Z?KclmyDL%{iGcw3g_6@(g$LT~a`xK)5x_HP*GY3bgQSNE6c>>p;?*uWlS(GI? z3F&gK4jV6dcvyFTSXawy?ge0;t`0g~Lyo0!jGF~5PKJwtCm_Q57$GJ0inVfqKzJk~ z8@O{w7&W+M&4(mhEKK5wIQ~ zBdzL@iQOCKu+7U7R9%#=TRfVVNVLrtKDQP$ET>snk_6_ES81#-^AJxZBrGf7wWP%t zTOS-d7TgtOdXsWeDfW#3D)^hQhXLv)TAVZomys-yp56 z<4um?#j{SfOe)J(&ASBr zSIj=MdeTuBZ*PZ2VD z*x!|CdNB@4q25rvlE*XWCAhV!xYRB6b!I~EH?OAv{q9DnT|ihGyV4wB%wEt;)kQ`H z0VlyWXj}DY3#KH#jS(DuK{~m}^|0r7brutj{M8a9LX=hKy@ctoIcJb% z+x{V$Xe!Ge`M&{DFh%=Jw=@V}4b9_W`S?e8JXuaj|JPV}fmnT4lGRj(FivHM=gk-8 zv)u9!WZ1&?hX`>ITK}*XKMSI2)n$uD=S){)mJQSr11w%R)FdAk4e13!1aRMUU_|$F zg%pgsuAjMblwVku&k>PnKvW97o|UpPc#U&%FB4V^kv`WdUGx(@` zYYg(jz_dVI?*oi7Q9I+ID+vR!GlbbwE5<;v9mNgfEV}9uotfv%%m^EIK^DBur)%%E z{p+RDJ?}lpK9K0RC>)_q!8GhILpbr7c@dxux`oV{H6w~vs z??6BQfEKN?5*b1=w9E2nkOh)@8zOS_48ExZ|KahM4^HEQ!fEn}kCbUpnCub``aMs} z7I$rpbHl6G6e}mi9Hph=t3`S1B7{!A5sF<2CQT*yxVGc*V;6C)UvG26K*Cb;GVme- zlW*W=40MuEYWrte6+JG~dzv-(0b&-_0hNO&#T|`!5o4ZzMRM-9T^~o^w84|Jt zJ;I!QGXez}Rk6;E0mplXkF3#idUNp(Frhi`BNWaKVG}fqJWdbc7}pvY`G~V%NX_Aa zloRwAQN$sTDUL{X>9$7htBF(M;oL z1?0(k7_8v4#ajHB52#^%dswr&7POPTK;4%$b;|h$Y>^P#}!M$oxuw_J;_i_1NOHPG5);BcwyKckO#DU6O~|3>E^2 z^YlHojOiAxAe_(oxxEPQe+L-lEy+ab>(8^yUAK0uU%5_7#w{j{1iwp3DYGP=>)Zfl zg~n)Q zxt*E6<154Oy5LU2W=>agFz zvM8%y8m=vQB3;FK;If;Vw|=FB%8QV#IXNz;!h zQVfl?Gw(u(MS!#iX89)`3X?%&rS0)Hzb3#fV-CDTt+W{-4v-@>Jf*3gvwX0F9^+h# zkdCdxbUm>=A4AkQSh8~w?EHrZ4k9woEHjH1J-P*&MVU4OKCW8k^Wc^}$uD8ymUDg{ zk7ip(=!CPLgA}m+#pYp&DxkxL``kfPkMQ<^0($f))SNMM8g(FFui_!pxGhzCdzX7I zzO%d!#kpj*!rNx0%rq>jQ)|BukrTNu*-B3M(LF`b8Cv=4S2=$-A~n?VXVn*BRZ^?^ zSJ+r)w0FzrJkE&nqHV_WC5Jw=cYv$dpziuU(Dnqlt8us#uWd)D+l0`_)svkdr-vcqPK4aA91i zM_?E0Re*I3Y1wXp2z-34?7;wJtKs#{`Xf`({SV2j2!mx}@(=L_g=-cqLyaY>Y)r`2SiTzQDTO z1u%i)OvH&ud#4zE2>$~zfEdb0dNmy|m&yB9zFc<^QUhNW24=iUxVag4{NajmQogt{ z>?K|hjMRL*IMQ9K>YZB7ZGaTPvB;#VdKuqG!S4IKT#2V<=;;Q)9u1<5YJ&ykdQh~4 z=vyBmqjK* zoSiH(({rdgKDp8nHu{yyZ;#ETrl~F9;R|d@HCqTZm&OP=-eyyPb)=ftIJN6_j8G?5 zLS*pV`x@~Briw6ebDM}{$aMrg6Cci;#3ne}Bg9jjgY^*YxX1D5fIo17)&lRa{#;`1 zfK3bLYbL?i^&vtL@1$~jMEB-%5<|LghL2Wav^CgLj7)pwdus&L6+%)Gw{tpB({jqI1{}>LQ zq-Z<^oOkl*vxi4w_im60*&ia5tH56&;#Jxs<8&NfjaN^U7PPMC;Elk`avme|rYu2B z-A5=kCS`+;mPP%e&zx~a$N|l1XglJ1bswPG*mEx);n6^L zrYY2CE;K(LjuIT3*RiiLWR2X|7K4f{LUy2BXn>DjBF|F9<$fw;?vGddp$)$pYxC-C zTU3u6wG`cVjVDQ?jO-~w7(CodF1+K|Oy^#!RCS(+*!#>b6g5(_KA(Jvk;kb`360mG zvmRn>@tmdBLzjub`;iXV`3(*t8yEvbP9Ego;JmLeon?AnY1vIct%iGt_61aRw01{K zyzI#!Y6i<>>O*6>);H~_lAD|RMFaO<>tlR|4nm=Ib2Jq5kZq91v?{u+;*xur=@ar`jA7qXgrH;>VhUbjlgH{9-Gsm=h9Oe_8{2;} z_1MX_76GO5j?H+v!-qOjKSj?gs^lN4Ol-n?Zn%uNL=@mUiD1^P2 z?dw;GFKvNWw74tm$@L*BQR!xWQ#ZOJirPx%&D!JHavNa;OiWEAcBdCyV_Yy)_&-b& z1=pc)GAs<=juXHUv4QAQj1aPKV-)`o#qhf;5~Rfv;|1hJKQfBNJdDunjY9*#clPw#NV|i05Okpd?GM1QaBQB(1TERVN(9fj$C6Op`sT!BRA z&d5_hj|Z-S8;I)Y3IX}?OQ@me@iOS^Pyy|+W@g;^(*aoWwdT78s$#&buN&nALl@9L zu*U%LsrDU|MTpVSvGGo4@5A)}`m@FK{ep#?XTSdVivb@Uop}Z=SG+4fb1I_e&05Cv z@gYQH;P;G|NbklpCnsN5PVsuuJq^9!w3&JamVkV=Pr^&^s|zT1tjq5rjF$o322ma@ zfjF-OyS}D}Lz0)hS!ot2vsUUY$6E0Fn>DYOLKtTPUG^&dby^9~)E*Da>RO*J#Tr4po!8!mVS3|MyUZD%tF|9%ox#}zzxB( z*2>19fimVCF=*PAPSI0@UfNA(#E7VW=z7HNbzWlSU@Ny*ezMV{+H)e8vKg{3ku{^% zr<+^iX$a218)wsbs{Ml0#w0 zkn?=$NW9a=l0BWNKSP>aGY}+6D7hxWJFAE{xt#eh=z;EE|D&0FmH9=hW*Ej}dWv?N ztuPdOgn0@C9>n<)A%o<1EdrjW4KiK_sM)^_SYUNogFRIbuTWxKZ;!7ZK}oHYFDR=n z7yu@1v~A};p0sBDZNOCR(&f#riOHAN#QH@;L(+^|6oVQVdz(`$OI^8X-eZii^OphB z6llaC@X4qTF2R-U5kikWwO2Qts-Rn+9 z_gz)-tB}>G(>j?Ct9rzFm^M@#MGc1h%V&UEk(SQf@mOQbz3KT!?_rIR%=~{*3%EYM zg2Pj9;H}%g>@W=kPQ8)8(sAh5==Z0-hI1~I=3R@t>JJ1%jUnk6tMj|@cNih2qhf&qYO68c;O zYxp>VLqw?J`ZvDfTC5nZ469+aapSfY(J=?LQorqU0ZA+)ZBYSlDIj>7BO5jxQrLpu0i*?2OhH1wjyfrZ^Z10IY>#V@lDgfV&fsnH%JqvuJ3?6ZQfBnDlalQ zTUglEk`w-Q)bI-922OQWNNrQGdOw`MA#LpI@lsD4&byNyX*JQpz z@|mMQeg1a$`I&!k^pTgJw8LGXGj;^1?N?@hiW+VreuJRULx89y>+cx_NU=#bnQbux z>IXtvYbE9px9s^9wEUsnR@9@JRFaY1#?v<#1=8RZexQSubkW=)uu8TSprMrgwy4O@ zRQ5Qx8ecA9$&%sm0xl0JYz&PQGx}#i8{FqZ zgb@FiuaViT>-R)Zi4Mr-hIYl8n@ywr4jVS?YQ>77VYY$C^+VA(LRLY`qbZ(?bj3GY zZ<}>RBa7JglG1d)bf*uH6w2i~mJXcz5X{6;5h>wa=J^Kcy}RnC1F?SkQVK%o+7>`U z)C>UW4;m2r^mb(p$U|My5V9BIj=v4s%TVhbDwWw+%;}If+qxAZn`<$i=S6lwDYOL{q~crw z7=9mfyp;6_bE8V^nGumO+U#0;iw{s7)*z?1_E8=HS?znJ$%P1r&h%W$)hNqVKrzR^Q zvO(i_?Egm8_Hb7(2A$k{;L`ITUExPVqtZ(Kh~AG~V2>5a_&1*3g8nU}h`#&<+tXL2 zSY+5dU#$5?nR=>i8DeTv1&{ITut_HbGh>OfbjcuxI3G~~h_M3LKalk*e3t|gJ|Vs~_W^n_=v7I_O1`(Xvot}` zu{Q7lX5Z>+*%NMn?dK|#-NJ2oWkRy-58ohCnS08YO&^*zL&nqY?M5O&zdOI>G}U1l zhF514oGCPD;c-z|mg_#mRa4QJ8WGt4{xkDff*I0P1y)P-vtm71^R`p_ymwgvupIt{0?7+hwTdD{J5G$Xn z%JSAFK0Z)K(w<5d`j0ADy4?rg)^{(U17=mN5|Y@mje=~CT)GzRiih7MU5=5?8Ie^=k*c{z0LF;4ypm;$XlO6O3xhhGuY{ zC9k{3GCr)~7A2Uw2vLGr>3Ht+Zn3VaW7OaNXs;||4Fad>7n;t))R)0go5%LkJt&sk zt}Kjl2YLBwxN<<&ea9ooS8%C;ut-BkGa=Yj4%sKC&8GlC=I=nqGb`dwx8$Cp}~_JQc9ycs*LX8hTUDI)ukj;*hsPEDDyaP(V{u8 zm~7pL2mo!KCO>rCSW@iozf;xt6?!#+(nZVO2~G76efp}}{yxH}&Hx4JuU{opwX~9k znR!sAow~h8S(@#nDx?f3Cl3o39UVK+Ymc8+*vvySNHQ#1L9hFISzU*!tA5RGpt_;JiANytnk}FqUlcVp-p{sE^V&c z2*H~eQSTT%XahM~_(ZIr>s`}3>bW41m@~Kv(c9kVV+9j)0`!jc>0vCfJLuyj#B4ejb4u z?V0l>(!#!Jy}-LnyE1}tFhf9fwBw+qD7{13$O!iP^+&11{xl``FdGpDe{RcBey}au zuhLD^=~KEDXUHZ0+5P%gb^5c5oy+-SxDj6@R?~Pdo5A22P-QO`xy`S9DQ*^hy}!`j zYn1Y0Jac}||Dtb6pJWpF?BUo9I6a%vN z3#nK>3_LZKD_XQy`UB&FSLdr4`0Q^IE< zV2EQ#EcR|g2r;TJAFFzB zoh9k)!IPb4)O`G}*Ja{X6jd|=iA#%$O^bO&<3hTg58tBra9b2(W<{CaPe{sGfev)@ z-8~8noFp8;Ew~I;S`mXBVcN~dNij~un6kqaLi!mPngIpQc?dj zgVR=vWHJSQqBJV)tGI|aA<7$#$`wBW61TM=8Q~_4i+HmsKVuWUV1koiYaD3;%2h#( z@vwg8%ti`Km0%rvExHX!YC!RuRe5d4!}?dH`3_zJS2)Knr<9JG zi7SkB?CtG#mkegso**lcDLFC;*=umE+A{3^< z=|aqWcUqR3Su(br>T);;Dmp%>t6@y3!F#N<4Hkr(4TSkVM4lzsAh(MO73w<9Vj4Z7YxB_F~07OWS&0@o(eTN1HiN;i?oCB(o zso3Q|O5&B%_;&qmy?f)j7T_N+DssBm97HXaFB?T>Q)d(~@y(3Xch2Oth+vfuK+}M- zjQ_tRky@rcUx&m=hRG~r=d0(Q*iu?)UJZ?fEv7%?e)((<4QcQUfqI0A<*EO17Bm#v z%9IS@bqyZ3MXc8wV=CDmK7A;O8egP*m`sdPE+Q_+JI91OB0%%2%`}v38-zib&&h!) zhZ6UvL+0bGH(&8Fh4zOB#*rZS2AWsm*e!%R| zi8Do78}G4^SMqlU=~!o32UQ8dd4`P6Tq-H#4dvHfe}c;bsKhqMNNx zd6s_ieAu%|1SenS#xOk`^thr)miGks#*SiEL^WQg+3<0{Li%Y`X-;afy$D{n5jsO0 z$=x=yo#wg6B!Qe#Sjs->5d|GDBQhcG5&D2&(C;HMLyKtaPa($38yR{4TXtUM;MULe zt+1clTK|kNN;C;O!$L5XD;+Q8y`$%P2yvC4%={YBaD#e^d_U&WyKL-?G2*Z_v#awe zRMZ7?x7cs#t=LU(L#*+XA>uK&v8buee-fbl+kQ=`uY+@T)6#SwH%rZYJ}i4)B{{okDwddAFNn7d*CPbfjU!H*awg z=`;l200He|Y?1x`R+pW*GcC=~mh6k;OgA|?N0NF3>-MmIkM0wdt#2^)M~9uJ61Tr} zC_n`G=fxNh!a2AV6S^q04A1TU5MbN|AYAqH$M7A;8d0euRNy@wuw-S#PNm-4!}?cL z^oRza5EDn7>mMNqDi)Va%mg5&L7-1ZDbfV(KxT2RDI;|heUL!VhD?|{>%jdRl({@0;rd>sLoYChO+eBSlfL9_27b^dJS z*%Y^2M0*{WW;R7O29Y+SLA6JxP-&BMYEk0+I_QWw+ArdL}vB8ApZ@_{*Sn z72=n357X)(A72N|A9|{Xp!2<&1yoG3s{!Ci#+nNMW&eH1Js~q)QW>T&&Y8x#Yol?# z>YZ;nA8?+pExKh%I>rFkS<*GRet$z{2s^jYZ<`~p^3BBsh`fkBvL!@?`+~Ka=^I+) zh5T*Qp6BR|kg@D}j{ecWkssNn0&uU)X)%Xbc zjzVG2)9WMblz?`}aLnbDRSLuTDs5Z)bySH3n%fzBgyB>up}j3+P8%%|p1iv)5^hUo zmGg;{A{hW6%U_WWApRR3BB`xCYVKz{s~7*0di)lAT%4QQnLAj?x@UY3UQVwxw8OwY z4VvNbrB|Cx?=C=p^_|b{>#vBpwlB)H5gbkHrNGE1oUJ1eCg5Soj4Qdv%^Pwh!wrC8 zE^#?Mnd0dYYULiZ72tc0ExVU``l4>Jq;|evzXa)eUWk}} zMiqPQV>|??xi?(kH>)7uKxT4Drq(@}7^q+jynS0qgxO4<+IoSdGe^K~I24xUZzg!#aS6fuio8LQH<~IQfAV2t)zy1g(N8fZw=e(U9_=8|^K-M4o%W3`WNLzZ}SFv0U^xF9dDUSm@AgF$AOLF5tuEVyW zbV#Y`Qj*_B2vyz`h+C>xe<@;YKYr@vqg0X=O4`v!W|Vx}pxGdWwGG=tfH;DJ#7JSZ zIrgJO2lhP_;XM{SuH&I}*ffdBv(&bR_ z>7H)GS~ikK(G)NI$iPuzv3>nj%g-;cX=}Hx4UWPoN0E% zrH zZE_m{+g@2rg=X$yi-J7eSpwYOIeTymyG>7f=ZF@Y7;&Fy@WJ!3ofitg7?i<#pk$0T zKwhywO<-yDnqjct0D?-t^IGC4OD~IiA|)jS+{H4E3fcYgz4TSS5;k>Nx}6dVJA{p7 z%{YGEXjwpPU*jPRb==}=wo9k)*B`g*bL(8*oDK4MTSP(^c_Z%hp+6hM34 z1-NR`MJS!;HoVJG{p+g#;QsckA1BYl$ac$xomnHGc8f|8P2Bqc>sIrcKLq)y_U|^# zR%(j+gs3bCLqHu1E#=4_C)2wP5M!X0qH}ruj0s@>maK-_ksMNKJfj4>MU;aG zi^2N-uyVV-`mkLy8wEI=J4CC7H;~eKgnJRV6~7V%F`dqh^cxpEh$4AQ8^vd;9bs5F($ye1E#3d{9xLm{Od{UiE}ng|8P(2z&*Uw^W*%c)W_$QwDVl4T+gg5HTg+9u*Wi@f;A# zoBP!DkC8XZ&&xZw0Pbhz`t-@>>EBU{0qP>SVyb4w%Ks14@IFAqRK~j;O2n&QUUe_3 ze}q0vEw-pm!d@lm;@rBDiCX*PH;7HEF+eZUMCKl}N}#d`pc#lqtWHg$0lThceEt~) z>0#2EX>)rDk>mLM9{rKoj6@G@P+d};haMT3nB&<~XRQ_}T<4#=64>GGV|%y{QO9xn z?vo<^Cq&N*$V>ncyGK3*Ju0FQ9Lf)Uv9Rump!LTPIgx!7?U|klcvM|z!gdac7_<2n zW$y^s(PEhEc#S2&FuvO#BedIlk-X)t1}%4O1%9;R*6N^t8Z*!PLA7c=ko$9#RFm^r zc_ZNES{*-rSrqQw@x0|{Y)_KM$NY%iEk^Y69HK;q6#p~dP4>2xg}m4IBu0kP2SJO< z$>}``Co(4HM2~Mnoy31^8Lc?Ae-tHNkb}!MJ;0fp^_Y*-l_C${kUDf^_akljo$&y2 zQ@L;#d?3yL!~T%LH^A}_OK!`!GaMioS^n4C#|X3(ZZmz_?I}QX08H6C1ce78t1yzZGb@1&h1Sl?!5m7`}`-EZy75COO2`$=f44r{j~mCP-n75{uwcma>pT7 zN^UmLg`Al)IiXvK!8z;nkLJiE@{gvkhItIJ--ken`UcnMc$;?sT~Zz`Q+3|Wr;4xh zAeGS`Beaq(i335bO^n2yHSi5m)%hvU5x87Vo`k4=I_w;-H z@q!noWjW4wq6JS7#Na*jU}Ea!C#$rXA9jxp)Mv(Xrkuuhsu|5gU{H-?j(i~K#*E*MGbmaRqJ z-wa;Mev46M398S+>>$LW3Q|+v2PhGk2oh=}Eic3^EkYocK0@CQQrDhn^E49;!mHsz z&DU#1%-f$Lq~mm@90NO>S2VT`41#|;)o01W&`wuASxIYA&WaaI_{A1|A7Ksl**l4} zy<)h$G&Si|&zFFBYp#-R+=ywV0z=Pqqk^gTwHo$3ehDwHQtKbKtOQ)-z&+AHrF62y z?{400IQJ;Y%sRx>GP_p!cihK#Nk|sGOk=C9uOA`sH0pC9tXw#$WTc%2+5ukM|H=@RcGv2$?@CbTO&yg`rMMkx7}KU)6V3*0!qweCZGg4icmNbu2%kk50J19rUQ zwp5(Nj(`D&xx~i{Rcu&G>|Cx9CD=VpE)}sJav*$hOWT%AroyhFpcMS9Jf`2T-@ivd&=<@Z8)P)9Oq>Rqa+4?@=DY5D4cz72J^WL`A%=usc>ozI)X@Npqx z2DpYPH{6ZEDHfULVf`Xmf_yHg?A&qaCj2q+%g1NmjN6Bht7Lg-F_U3_NrR{ zTHg#@a<0x^wz7`sMo3=yW9tuBXpKG>?9rL=PN5UW_Un7$2S&w zxq@wgFJ7L$K{=W6Y&ZZ3^7mAD(l05TN@YpK8cX4FANQ>kBq4asi?ql{pCWi>=h;>4 z4~lFWG!b9kNATS^VNv7-Q=+|*PRtrpKglO2-|z^=Lxl8L=7IVnotXB-JRY}rUUU3P z2V@Ifj6VYah{~Qo`VoEKTEJ5(sVl`rLmUs zrrx_Y3$AT@2vLL=ZXHJ#_vdo^RiLa_ycR9cRugn zzsQ{;YJQN5IU*-t+6T*#62GAQ;H)r8b0eZ^8$2sXI`1MWaYGWs6Wn(dCUwDcZ@-FH z<5+vA1`aQ|^kY2&dgM;=76OCGGVu>czCy!surL>eruswudAvbv9lI-Dg- z#EbS6lD~UcDi_$yxt1PTmOb-?cjSmVO;)d?)`NVzQ0qTy5R4^3EBcpLGlk;ZPv+%^ zC&t6X21&b)>2$Uk?}Gf5%o}rzRc{`D2Ko_QC*m5v2)C^_uR4kcPZDYhMD8-yqZ)O> z$wzijBZDgdL;{2}`V{<1GugK$4r!p)yAGT7pG%*kx_w+oe*87J9&FS0G4_aBfR=Y0 z$(bW9pBc&VzJRH{o&p4<=$2|Bwam9ZMkWi;SLYF-SgLT2yqOnkRLoRrfCKZ$F@f+r zKCM5Zk*3cJdg^EY7^wIw36_zcv+x+^Q*K$&bOczbCNwPz1L*B3K$O3tO#?5~18f~L zB>7@K&#IX<0k+bxUSl*q1CUbn`jXh$+T$%K@v{A0C)E{dV|j5CEXf15kTZw* z3_E~G*$#xYKL&^Z8;Wh-b8|5kpOrT8Yi5|2zQVP6GwsaHcMdb5LsNm?o4H%Tc@dg`;sl>2b$ZmD9bLH{FZ3r@TFW9Vn{h|x#<{g>9JWYv!mkl2lCJmr053B7Ys`cp8zLay z))5uQmSipomqg#CJNDoyn~<1ul96tCijaG}M7~m;$3MLI`U8r0n08zx zS!mV||Ac#@MPp_Hy?os|rs@IIEy6JLSPMf zl%wahl`8yAh<^VaI;C^aOjF0BYD%C+L~Mhwv@Q5wM}Td`z<3Cd=LqDSETG)8CBTT$ z!xlTdbpAD2i4Dw+ldW<92@P4@T_WE75a2~a=L`hz9kbgYpx7TW0nj}-Ysf}0*=GeJ zN@HZ8w?SUip$saJW1b`X2Pm@=@&$ZrZ!O>!)fPT;8G-j5)gU^cdlUz;nk8~w*#r4= zyX*_Fv>+BC1W?C0OM5E{IpC)SJEi&GV#j658MHX)I1z{=hAs~ga`9=<;YS6^TTb^q zcz^908h%RnuKy$j)$fYMC6eAkUbIt%dWW+IkQhtEm#elN)li#~68& zNe4$CKR+H~;D*o^T7vJ3666)_k3}=by!a_byZau;C(Vj7TIiWx?CD(4JeePT4={(` z-pPRTG?iJO?nS-J4zytW^>*^$@}mbEOoN-)KNDcD7hTx7O;9d02zXj{ye#8(1JCHB z{3rCT* zmU%TeUnmDh#N8sJ`A5&pKp04C`T3vtJ|~=6D?r?qYU9gVBXXB5W(7xVSVv*xR&4@< z@l}J`=?N!H;=_=TzT7WkCWTQfu|?bRcVrH=2k*W5c_fLww&mR6u+S^RK{V_M(b-w` z>yT*&U*z1slf@+C5=tG)d8{tgr6yOuMHeelEVK+mAmD@@=Ik#cUS$t={77jhEuVMv zO*th!V0r+V-d|g(v;y>=NoO+M|8>YUgk~M)O{KEGLhh(e^XVhlwbVew<`6`$$?uiz z!7;?(FV3yM51Dbts^LTM7ulc%VoBYe&;pcR$dYIDEf%2fCG4^#D#b?>Ul>ySKIY43 zkI5TBDkIuLt9fG0e#i#oj?2hrnC54XJdjRv#83{(l>bK1*YxU#dXfhy(yU|yOxUiP zT~Y9u#|Lh1k8KJ>BZI3|H@N-&p)I4>1MWx`i;Er$iT|Iods(s^N0v1`-y-`>LPAJ_ z1jwh^gO;B4s4wr|_|)j`D>BI=aVjq!Sy_?9TD*XOnd+|;hd-tQA7f9O*Q!Wm0OF0G z%|u+W(SJRp4FQ4dxqjD`(pp|aBR^Wzuky4?8a=F<_bu@bmJfDBaiN1C&NIU}$g#vA zwLz}5kVw2TzW+LYUEVK;R5)Ocbx+}q(h)8roWeQxi!N5a+t6HHI!&Zx|6|Ld0w`ntALsMoZvi!E5O6u`;Az6{2S}JI*coT@ zY2UFY_i>MfL_1OrSNERTo)3OK99#lIdRFrUDU><7l#AeQ=eu%?Wx=Ye2k&ZS`tsQe zW3!;Y5YEby+)`8BKe?PaImwopQ!uAG0AWC$zm&!AO2G__JOzNVmq_2TW z>}$=}_RI0_1{F=^oSCF^nT#d=r(&%sJcHNe@l^n{c2eHE%Pu%06X7gWg1m2+0}uMBO()SNIss#jifRy!P^UgFhsS-4A|Fk-Bo?6kW|2;sE z>X%mP8i4Iwnxs2Dl4;1LDpe)HT9!q2D@s2R;hc%A8fq`&c)0*&EJr1iBQ~$5DuDZ+ zpX{fsvZrcFtBoF{US=cNxnEBkBw+?Gr>Lyu|NaWsPL)k^lcdDx$v$cA&5 zDznW+eek_cBb2wiQv>bh>E$ZU_L{{vZS_%{R<&2s(NJxRO~QWeZ9f-yNw$AFejZes zdIbx$*M8j`{vE52D5=*pw*~w(RN5oE?UpI&NNrdRZxO5&bJUPWNrW zfz{g#)(?zKZ<|5>SYOUi-p2wyR;2pI50s1inLl#ep^XVl5~=D%(^9V^h$X4$FNd$p zLxSDTU3LWGZRWI_mu)5~<0eHzc&`D@#dMh*mRQbYVUD)}!U)91(pppFJR_!GKWRs8 zzxg1CdsM9?y20Pf1rpZ1!Poue5>%p5wH>xtt(Kxh=wA`1skYu6jWby?lGyf0nYIL( zDfaQ#9WMinJJ?bRyGt!k{mYqgcTb|0;ur=Hw#94VDgBO1Z_V0T7D|5^A8%{^i%2HXjn4zPqj`(xc2X?`5H_AFPx@^vS$bY_ zLGR}pjLV~KF{?EJ)ME0Tul@PfQ|Or=+m5(Ta7>s zH9iQ2JFr_zL=G`7iDg2VUXCBCD8E|}+XI=Y;Ah+ZsRJ^?8j{U+1jFv;A2IsHA245j zFE{@hAXQJ{8t~CBCbeI0zb%u#cVTW}4(8xi=qzo@fL);_P3{Ruj)*Dfzt0K~!R%@ex>0d97H4xVeA@4(}*LX>0tQ{LcB8$Q#SF z-qv3yw{)`C_4gA>i1Vim6E^dn*rHR>kO7=WBOw`oHQ)d>?6P>ip65uv7MqVF-h;W> z4{A~VnrRknG5AcMMO7W|#=iOQ1`I_JL+4V9%r78+BrKLfcIsEKge>>nB2x3r$ ziYnV@UvFqug?%IK?}7j$v$Hoo13+H0dHK6>LJi%15`d4CXc}9PY7AMK0ar__MT2&9 zNp`<8nQY{+(9cKz>jA?K4+?0D38E{!%qNg&XKwix&tX;3@D5E#8i8kc-|%%!386Zn ze>q}q&m?o<)ONGp^N5OvS|#X|%F;^b+{jy^Wd8nnSaW}#AAUFg=Jci*3L-sWbxLho zm;A9XMN4iMbZprpR}w(}`b%zgm3SL_lD=7c&NUtIxvTZN)UF6Jcad<7H7>946(S`G z@?gIZZ7qu)tn^>BTPVBUrxkS8Aa9aEGzaJ!X-%_^R%Wekywb+X(4oSOBt<;eVvF#K zT;B#51=j-K`~uenAp$~j(Xg&&&~Q;so4&bJ+hVZ+3JCZ60FfSKBF`h7m9z5`{feZb zp|MsYLTw@O6>~r2ia>;bJu?!h^;sE;lfK>txbhX2Fxq%L6w&Du<~;Z#S*YZQh7>** z4tc2<0gz~}_H?QC{yIYWWe)*ZXa~DBcRI3j`lTMtX*esLv)MHvd|dQLk|QD^6mNed zbn0b@R^fj-?&q8bgmOm?(aWhhr?Yv7We7_6Idxo$#VI0sp~u-oeEU8^W&L^ecZ7je z2(lB^+aH_~!l(Jx=Xb>5(C?nNLk#JuL@f4QFGJ*R3c=hPT(4=_D+$0UIPRZO9bI)s z_(GdH;=B-DnVv2DvT=DC;!K9&`5}u<764;fRN(Vo8R7V}V*cmdqi%47n6|?sQ~UI{ z5pE^@%3w&eDNZfef#na`Xo7{h=KD%b3H_v|Y@e~Z4d=`fn5`#hgZM<*pUWiKr?-4& z1D0GAVG=8?c@$(7EyZDTGJi+b`zLNxDm3$*yT2WFM5pr+Kec<(NV9!6dg>!d;dAg{ z@#dB%p^!-%qeY3+HgNoYybTfyfd|ckGPzhw2GNJou%An=3eM4k?vh{?#(HEs(g>}- zNc{DI-^RE~NA}@%?@2S5muk-7P~meWu-<(ewgnyGQJN<0#0GMIDh;07+ZZ}y(Nume zXy+J7o8~;tuaQ!z{iSW`wX3h;9wuT~qJQtNLqr!sCl>p8Q;>(+7J||4pAk@bMRJkb zZyCV4c0M2TaZXtJ`v4_YJ4ajtK2}Z>Z-m@D#5j^i-$9OWVoD!HH7W;PUS4oBK}GyIW+>Gi$CObj8cMfr^ zW3K)Wz=I?HmW#I>#UXg3U@4rsIqNT_l6xGZI8%06s*?Mz>y^xVT@kHYGSm^+*P7D6 zc_tw}RGKqkWP|laSe|5cFtKGGmP=yM=dBXE_TEo$@==*u^Gi|nb#0GAWkM<--GT>% z7rjtV^KyEjxEfC*q^5EnA?91K;I(*XcmTdZHK|B2x5-3qv;i?pDM+%bCpNyn3{dIi zZVa4#?ajR~HJTclyNwxG#p77(1G{UtlvJ{(l=>kn^yL%<-#X$y`hK#LDvCxBA)s`@ zJGb=AJMB_*4X?}<+8J_a(Wq6PM!0iJ2ZqyCKXZ;+YI-9M;Jpa3Eoq;J&e9q!gIB85 zqLrC+eH$P}ls$FaBlgMaGI7-=q4(OhFZ3o>Q}@_&6s)0t&S_!cWN*Xu;8-ajBZC#hh<9Y1QS9pGZ`D9k=B62hUBF+{1@MXD5FC zO88oXXv4n~o%=FCR9%+xz^xeF0bR81bB&QNpeGuJ0@N1?Ho;D z;22+Vk$6})vlWRCJ!9U!DOg&*F1mh7(@D!o{@Ip3Ji*C)G%7yr`Y5T3Rzhn2CS|N= zI_;_zEByF;@!dWH3VLbEZky+$-)P4eT4cLzEfdWZ?PqNJS7`vDEYB!gA-5FbC<1vm z~(%xO<=KFgwjz!?mP`qkc^6(mex{bEbr;YdcJ=|@INiAS16+SIaD&{@9z$^T2 z`drif%cozngW8oX=01g3Y`sOmi3cpcX*LJlgvx|qKL&cBc4K*_P)J)lUPj1&jp21E zLCDhU86wHiJMueFXXO3igNK)kSDoHo>ZD#_%eMEoFHt)608?6mze^Y`Esy1e-0O;4 zAjR+qLF`RRT$v=;{b~aaoDl{r@0XGNcvgoM)N^cd^HDFU_0UKa@zkgFN%U63(*3tdi#M(4ByZ z4E38u>;2L=JJa_lDA3`kXwa7K~%aPYjg0Ty81I10CNUGolR`dKPSU9^T` z!>G6aN*ljdQ!x&d$AibkzM*Daw(8qj9&3HCxi+~gHhGBw7&GLfd+%wh@=Z$(4j<=u zJbZv2%OAUfymY<6mWckc$N1S|p3J?Dm(k6eXc zIlte36#q&u3_QqEHfDIYW~41V!6Y43#oQ7ty~b<&aQwVw7dzZNRjsX1MRi65Bb_I{ z&=KdSuBLoCKnPl==R;Pzi^{iuDbqs7+8NKXyXP2FL<6ZTBGKAmP5(n#twy@ac9~F#Y^_H&H)P`w3c8eplKM z>ZIW(PLxS7X=vy1_61J%Rqlr?NVHup6t(gAh(p~9Qlfe9Nt!P{+b;}O7)DBTQMIYi zJPmOrRjnr`3_x)nI)Y|L`|P@~fC;nlwxaqD=5`FX>~f*6uU;!W7V zvmUXH*Dq&kIP8O-hlnv0iT+!X)t;_JULDLPBfP4t@*$lGE8*kkYw~S`7(wk&zO2wU zf7PZZ6tI=%ex4t)rH$q(iCbZg^hAGM$Lq(>y;O3wKcZpWJxdRi*}b_Yt`Zj`>MP>f zFoGWsx~0=i4mR$-`;Z&O2b( zZNlE3uXicu&pBiHG#pnaRp=lX1F@cEkG;f&mfHnZ+WbBaQ28mAU!YIHKou!!PNEW9 z>t_W1Fiac^39wGW@QNKgx4~%rD$x(ePrF)YLCd*%VUbl*x62V)_yaOS@L*E%4{XK6 zN@#7bi@k~E_3?OopdU;65BA#j^!aHX3F78^#A{_#29`At-S}DI>Hj`_HLf3yKmIi4 z4Yb5K5yW zvIepe0RfDxbuIPqUXzbZ&m#!XNXQO3Wbs~wP>iY-{6k*w2+7u9)-MMabxN|0=^0)}>)!N(h(eoz$6Lf!; z73fl$jcIdLYi z#m964rtGygB6hOK-BM_K9HDYal-})HmY{?lF2iDVizZLPj6RT~tDZGYch9s3NFGFmhQ)n1R>>)0yEM(LS-ynf{t3{pW#UNxS8iso{ zPSvpFOUML1qA?}pB+#jx!g0kj)h!dwz23s$!M4_O!Ftw&M1X>M15*(chzoThLG|FZ z&gT(ErOR&hZp1LAvyE~vEd0ir%MD_uniwV({axdF(GJt{Z(vz6Q5GM zti5|7))5304kSxvNkQYkjEX$zc^fvM9wTMSu{{3 z8?N*Yat=l!f1d{MC=UQ#~^oW5h~nK8`}$9j#C~D_y_%` zIgOsyS<0Uva0Dx!@Hp`kt39_~0}0{f_|cPX_6`d2^Y`sp&FJeLhU2p7A=w}aDVEYjwv0X`-g^Ed52bIoF2L3Wap)|gT)B_uaa1M^_GZVl)bTH?CyS!b6+(=opEGaFU z-V!>n<(VLq-AAso$MMu7i6Ssz@Tv*B1=$X_cRUZF7ESg-okH=1sJnZ#Z09*JH)hr7 zpiXdU-)Oa8_Ezrc&8>#c%EIJvs9F8mYG;z=a|(U^6WCuAit9sga8xkCY06a`X?bIjUXC+X$)aw&<)|V!_r?)oQ?q<$zth z(%Z21h9Kv@*{l1ru)k54NM?Ke4q>C}uEoKtW?HEkoc}~ZD)!zrLvNii2!OihG_7Mz zv1B=Jbmj91>2lcoerYia&U1dH)08!XG3ZiT@`U)tN&ZMXcxAvM?s*x(`}zQwXg;u= z`4-fB-Y9zvdw@k~z>Cb|bmMgs()u6X4CfFa8$$^67wgSjxIkzyzvhgnK}$Zda+w2+ z_%>+<)O*3-z4C--j;}711fIlwW%UFzilVME*rIHth1uhC83q5!svT3g z)+;aNL}V{$3Ug&SKR*uZTqzt`*{?BFJPhFR*hw&k@dmm97LD-WMj3b@}Z@ zG4Q8bav|Qvs2#!81F91BoBYVR%l(k4j3iK?e^s%1LztUG5SngB%yr5IZ$qTCb+$j# zU+EbHJB<)*b%e~M6fm+yaC}cg%y+ozh3BBf!TLBt0uSJ$#~D+eiY!P;HmRBu;Z4(p zxh?0@btV;A@-M;MVX1*vo@ecGgo->k|E0a8jTgYGq0y4YLS;jY;HGY2ddzQd771MT z%7gLeUj+K{bv1>F`x~*@-`;NQDa!PqWfH1lo@JU zGD*&$?iU##FM}j>k?CY;gK*X5WKp`q1CP{hV=@-tjfxksrLS?U$)26J*=c|HHbfb+ ztmu7SWhWrzow2Vq{KWj?MuzCX0qKH!dx2;a-bVEN74?#W$9&f3IuJu9d7Jk3+jo)8 z4U@9=zI}$X1cbFhK+NAb<|U>hi%@4VApvp{o;?jPs{R4VVof`Qrndl0e=6O%SbC<7 z12P*N}fnn#9p0)OwZTwi21Gg*Y_opFJ3%im{_?-~ooD=T`caBe3B|gqtFCmpy_vQF$`lL^GECAcy%{pdb z*P@y+V4kvi-iEO7z9eZ5OU~f>t8l)A1J=U9I4WNhqBz~MZH>EPslk!txq8MHve(l9S8N=2e~Qsn_Z|6uloo!?G4nm=M>j3b^LR$qNWjNN zojm1rYtI8zHEeEKY?P%3Q0aC$eZ62nC_=+ziUMxA?w|kcXj)b^~KT>t8v}syu z<)9wX@Y#a3xX!#{Ur$krsz9Z?lf4HN^E7~Ei+Evzku_=xkr>IhoRhJ5sPQsF?jyPM zUJH^1_O;5kpNs<=t&3IU@{#=(2{-U4ta?M*$;&s`atkid`aj9M{rU}Tg7Va?lv=(L zjj2vB{Q%h%R~6L-@%~oqt&QDhnNyb~=#^;2|D$|nI%=1Xl~sl)RI>lEM~ z@H0+zTWx{N5YXGp0GY>B`Vgdd`Oxt-FF0xRx)95|RAIjiiICzYSTnT@{bu z_Slo+1@N}Nj!?R>*_h{%Mn3d zszFC}Hsc!Vff%h7WWMLh%>QFq)0Gh*sJ-B0^+xhUm1e;h)qwx+MbjY31b1B&Ff~79;L@VIf^ITXO?^MxB-vrTA^Nf6mRn$|~ zm?gL_jKmjJCxjO@;XM~g6TL*V3Qit7OC#<1YmY=c`2dH~?eh@fL9P1-Nuv_Kpywj< zt70o>K$_1M74z!{i?(JJ;_S@@;rTW~DPNjw=OpRLRoKH*=jY$UUkv~;iacvG<GvEnSacD-Iy~TKrWY)}!+2{lgNd@s?iq%OE4- z^5IE7dv-MSQks|dsMz~w&m(uoah(e9c9mh$OiO#SbMK1eH=qeuDs{XggN|Vq8H@#&C zuKE_D_?$;Q85mAz1C^;xX39igtN$&a)dybfuDj~lQXx7#KpL=$uV)}5$*jd63x z$K4`@eH;6dn3rE$0akEr#9&hG4ns39XZ)+A-qWL-!7z zPaE&;N*B}nx_mzv zO0kvLI=&A3Jbfsup|`tf&Fw^JWrf$=rL9Pvr-DgDNms6Qw!J+G+9w#3>B&WxZ2?ZX zzl=~;3>*IBCp+tfW}~|~zHFMAdAFFb)nI{ly}GH~!DtSX^SobG+*QhdZM&CezdX`~ z^7xqP4iujXq;h*AFhusie=2wbuQl~!b3C|aHfHPjc>Flk4z4zxCbPj37yx$0&mS?v z%`+u}cMZj1i$Hdjcq(w~NGIaq_!Z1EepK#hk{mSDdi4O`S3;6oWPCWIw$Y)_&lBJL zr9|D==krs2taJVGq#czAV_`v;G#rbU(uOyn)2|>~t*O*X=i{M9_Hguy^Z3{4v7wb& z@srujj+#(W33f5#k!J5%^&;x6(TP8e&>lw+f2=voJB<;VG!HqcD2$;b9!FA1&d6Sg z3J~C)he$K4uOHo@>Ronw)Jr>O*X8iuW=(^G&ZJEylh{9WOa=Yz`S=y?q^(U>=6yFR z!A35HsHkqqR>3VjF?*>EM0?1I==Nw$!tnL-4a!eyxdojvk2X^6fC%0bT?zM^3$N;# zijwxgnbZyFLp_Kc%I*0nxZRH49^YJ=sBS7j^!{1s^q9THeJxw?V_X4>GpeB@h9_O} z#tb625)A8C{!1 zDSz3`ROwL*e;dVf(Mi2G)=9!XEG0$DJs}qKZtdj@oTZTvONwj5>f-Py%U+4e_N-Gl zPvR09hM>hpx5T+gj^N)sAN?wW-;4XT%5d?Z+Wi;bE4hzyh|rYAB70(kTo&RnUtA)+@xceAbIW8Ev+Y40H{ z?kLSiY~75P3;61quA{T0$AyN6uANNR$02&970nl)?i3qPMLNyt!~2UIFRw=k5SHJL zLZGl@HsV&4!rIRGAYO(Tm2ivtV2S4?oNw``ogpDl%77RamcI77V$hWAM4PYWH+vW$ z?swGA#C35iZ_#oomsO;vn$1z~AW~o+;-XR8h!P4gZpYB@G(bJd7F9?8{*h-*p6hnj z&-CthRQ^R@BeZFRdObnj6VV5yshgi^?g~JIt`dCu=pEa|s`t;v!>MYO7C2uk6gZr94a&^>b7S?9g>%h`GvPAU0fy4Be!s zUA5g=jS?f;>j)K^xqQMk($t$%UXZZ|NzfiI$;J$3uA5(_K$O_gYjWl{T*>`ykaVo@ zytMWR*pdbsg!QL5#@#R52XsV7hmj*~$ZzELiv;Ud2-mEDiM7PT!-qCm6 z6J{z-BYA>StU zZmNqbLr8*#Qf24du{~y(ouF~qQkh~rMyOk}jonouFK!MZUtJus-@Uw$3d%~5TKlDz zyUG$to<(iu6j-=*eYTB6^NNz{cs^)qrHP@9k{G|UBt3IRlbtR zf6wi+?Z0r)(21UoUa3M$qKmFn?aqQBv=9Bu9{$fB3{`+Uf=CX{7VYt~pvEufB-P|K zfv-cj$kRA~1nE7G+xday`Feu(_I%uR`#A8acdsK_EQ1tTmK;WbOTM*u zVj@NWyQL^^k4J5PeB5K+Fdyk4N~KaZVuYA)z4?myo!sfeMqOU#`f}J_`T_|LHkJfG zVL5z5Db}=;&s;OBDxI%6Att5*bXiIJNJjeO@pF^-wBI|yIxA}$j3#4tuAG)5q!bPC zt%v>YS*M~Bq`SO*f!nJ`ic5=c;|E;u9e#J#?pE7%KccZ_5(RzcOXFH^rTlq1`Yi%! zNxipj{|RMD*>$d8w2M_ogm?7dLIk056E7{bNr^J4@k;=J%+AB8V(l$EKR~jo_Vj6W z)G?}#9z!E5f`%c4NxFQAzX2V}t$pr~$B({X`}8&RGXA&lmI$w9b$e}KwJNSlX5_~&^iMGVkwpR1soJ*1`t>boC0}P8u?~a}Mi*4oM@R4$5 zdk3K+Dd%?3q2O5Pc9FH(N~ZO_l?8N51fK78+Ag;&?jDYQ%eY{7byR2-^4i&WoxeyG zSrH`BL39N*g(a}eh${m&KJNAT)R%}0vhC;3STV(hS$XDX{!P;}KQ~v>#*K0;Bs$BQ z161XCfU-bRpepH{Au&0=shd`pI=OUS2Z>>jDe{y!v4OC(Zehv-0(m%oS^6aqo?Daf zb#A$NqDMWTHF#2T8h6|O`SKoD!VV(d8cBDHf4Dzi;cw}d#;!o8>vkW1+Ik{OBw)zI z+0y`B@GKkQo49UkoyX&QWH}iGS20mhW+xf?{H&EP=_PIW8DZ_~;3v5Rf)H05vgl~F z4)EItB*>90vzEB%7FqUZs&{H$T9F{+9?;AX;$=>o6NrW69hh8z*6ft#S$!K~R9kqi z`@P%e|9_=$_tQ3}5J=P&)6A8Fz$uJGe`IFHbP2+7POb59`^29<@kl(61XO_ zwIYl5%Mrl-xke!d7l?VxzaKM#?Y?B4(2UMMyHq03FR{IoG30?|QfW>(^U%({8p z_3ilevQ<7LU$A_}vECMz|rl5EgKj&FO(9dcuB0*Hn5`(lefi z7*9iF782~^Jf&tOyn*yaRCS_1LX2DJe0w1CEmrRMa|dh z5X#~T(7g}0^8#n}dK1-KnBxqfh>LBMbwcxnUtYXa#(-^|AAZkdl_V}Yos>^W0+QPe zmgyy8s+miIkhpHVnxLHI;6x;(3f2>E_eFE{zY@hN%7qI9Y;SJ6E{60zUJjpv!hGhr z%9HFKH0UA28FXhg)h-3BHbsahzET;Ky5bGw6?do{JsmyKb81qje!!9(ufS;kEOs{0 z>ee^u=3PSFOQE1juu#7`-d3X6JZG+L)(%xXH7zA<&*^z2>5jT zJ2096;)<2;sxp)uVVQkw_h&+{UHePrQqfWqg5)L`QESjeOZ#zvvP#fefQNyi2UIQp zb)U@Xq)U=p|Xi?OB@Cp z&cpA*oN2RKGtw$N9Y03!3*P7JudQes63MVmRCN>^RuarBw2HG}cT`18&JB)vp5EsH z0u_i?J5_J($Zbg|CWrwTc!dunxh*=^sC7s#wwKR5XH4_3M$!TRwzONq`^gK9gI-&$ z!Q@=#RLkg-^As&h(m?Y_J&m9c6-IN!vVp6mxF+n;u25zM%e-@kmEz-$v~IEya4;^3 zyjc3n5UoC-AjpuU0@VpC`g7+CG$7&(E)kie(y(bI>}gcaua~o=3dPJdzSZ%uURR`x z#(rWzTW$cGU>mXdBubYu26}MJIb?9{<>ET;33Q)t331ttx-pvbPYM-R(eJA9x3r$F zq$5jf+N9q^N_xGFa2f$aea<>q{0i%F{_oBQ%_Ko(eKAw(pcYoB)!o`s!rl`EObGWp zMxg{g`s{7VMoKQ^5=^Hn8WIMUBBH8pU%)0(JvV*>jG1DvjU-Xc_x>+6Gwru+tJsJ& zMOGopC(C2{Pl~^uwo~TjUX|@Vz?T?n5)pL3$nBj#;V`L|gblA`yqXXO%((&aATU#MB7hYq< zQ@AZ=Hg#mKA-9$o^hp!3r34>6ED4?)5qvW(MY6?v&;@bvJdub@P3;od~1=3yc6 zSrbjGn!9g-3LmV$HthY5fMmEX-?aX5p0oY4KwtTQa}J-s%R8FFCh|^R9_2iWzw`0< zH@>t5<5%{kJ8kt-h8O%FbyRF2b8}^-l$tp3|LQ;A9uJ?5a_ZH6tDo8MeTx1S6aZl7 z`Kf$i=??sC?bemqZE|$sX!@YUCF_DJo+ug z`t$JgJ|EGyXQdwki_#ac<5xLEFOwd#TksW6`riYjR++6a|0zKFDAXcMkQJON@tTtQ zmhzbu*BXIK3$B*PK;!m!K0n`4%s+dYH3M+*31KPgf&o-E<)g8)2D+e`%`P60Pk*@p zYu1c{)K^G+V)(5zn>p*N-1kkG^N8l;O3tMXs!)pc`H&SFx_oQ})@IYa;$v8kK=h7Yz zj{o^vw4FF@$QApdiy{aA=>B`IZT74n&ZL{lf8l;yXju<0e9WKzc>FnuGkkFl@gsnX z99fQna8g}&I;{DxS^*?scRTS;QdM!ERIa1Ad_2DMFL=Wio=0TYceRX*4%B+ck9+}L z6W%NHKsg5+sT^3r7ftTp#}C2jHykO^gx$?&(S#7H`!uDn7EX(Dl61AJz_I$fL#U!b2S`1e~5FSRb15~s6 zW8Sx_UrbqIG1ds;H`*~tW%Nj>GPZxF2sTofzwK>=+(t1JsP=BuDOpXchFKS~0Vg%|)lmzq{2wT)l0502IORu5(V^t78_n}@tr z2V5bXgde0Jwx#_a^i`c|jF$cODtf+HBQhI#nVrwaAGt3+RMm0+skgIkok-A?70up)sd!fU7`! z6Cl7^$%prHA9;ucdi9@(@F+7@b|e2Gfj$SzT;g`I*9s%2pkYv0wZW}YG~aGi&u|N_ z@(+9Q@_YZ-X)Jpl>jmV}{#a2(^A6-;r$6x!M@#THM5f7Zq0l!iyped^s3$)D=v7UK zQbYap3_t6X4t{QlL{LF`9>Q}xevj}AywgIQPmlEnYT0@bLg)7i7Un$TC8P6K=a}=s zyjHq30|Y|ZvaKg2NLYJTHRt!8m`58+0TKU%&OIn`tSXL{n zr^A=;?L>(m$!=cP^rTIdiHln$-(|7FSkOY|0yl`o%SuBnj9g^Ae1Rf)tC^)>(v#Ov z2>l+Rq`_96$twHyvjQJaQv&Bd;3SKNwh8Bv(vU#O!<6`<+fdI zjHCaIpi`V?=$y}ouSU~9j_-7w6)L%Vx^sMz{7CIvn`?*!yxA9>aVyRL_HBUlndY;kZVtuF)AlA3;Pq+PIH zSy8XDiZQ;#qGTIpRR#kGN))G+mm8;y-#SO?&jZvRg}KZ|{r;I+4dB!F_eu-|@#cuE z74>r~Y|$0gDbnD7{}*}TW!LwsCoX;+chi`T{44kPG0Kyut_hb3J@!A$1ejL$IDYBo450QhUOJ+g=4H)~jUynzRbm=0= z*BDM(Vd2lnJP-Ee*hX;Q$PVV49>=2kd_+XyF5CR|=TCo?5{({^pJmTa3>$$;P1C}n<=8(uL-3utODpEcV;9_R1Bub}LX1Sx>C zkgg)&(N6BvEHZo|FW~;l^2p+CeLnsuhbRam?fqqYn@?^7MBu}U=;vdr3y>6gQPf{nzOF!|JB)u2I!rn3!VB?zrNm@0iw1L*1SMVz{WXcYUr+QAn^ObygAg&Zke7^O0 zfXJLL?@`d>V=f`{`2!YTzJ5ka66@~Vo6&$AK|E0=+uU!eWarB_sPG7yi>-yMs-jcQ zfrIpynU!QMz|SuE@`@EKBOFeaysRudUoOEK(gO?>NFA}4)mWOqf4DHzURVDPon`f( zJ4ZVYde=wLV|p1Ot60V?1*8@#P0rFLokKxR?I3EEcxSmv<^pOS{tV%ctwzk}@pAae zi)p&B6)FW*&qXG)=j+F|*Ef*S<3)3|WnMmZ3l>bps20!B;|Rq@{wbwksxl=97 zp9U!F?4{NwyGiC_@*}Ch1as-XcvH@qZu2k0?W#KB8NuEWpK|5KKMjzUk~s_BT4!e@ zc-#LP*_$*ES6&8lZ2^y<)^{aaSXhM?Uda0C(mxDvMSKb=V5O;L-Rk4UZ(V73!6Mkk zSlV8=%v-Ps@SO}w&`SN~3wZOcG}%a5S}#kbliNVlc8>-kUw|rDgLfpjHq6|)aD&Yu zg*u~@cp9OVFI_uiK#SXV1~2>N8u4@M1#IlVl=g5S6S98916fpB%J-McyxvNt+c2Go z%`8x0^g-;dl4Pf?p~VUu$B~iN9)Fox(JC^r!cQaQ9P2`Tgj70BoFdn_f7beB?vXP4 zrrFDhk=SoC3A~}V>u}yZL4F}OcY;%|xN^TkfX})zjP|4>^J2%IvI|+^Z&++U*XIEW zXiHKo4Ti>lkIg`Sj7I7RE~3}-P1wWWw<+S#oAaANc&&IeqRx*pAtp$>+J%9R8!h8HN+D&ITOAF|wregzTZE1O1m$Qi66|LI7oVFwS zP3Dis<5x2GNGu3ZPirByi%|Lj0QLf&!t+p#N=^)g+(UL+v?$~U68`T2imwH|vfh7( z)}@88=d34e}~L(fuqg~aOzTqrPcmmX$YL3>)mDp z%s-VYaG-WC@n@x=_;CDuHk=H}g|S-a@c43a?TM7O(X_?kFZOEP& zSZSLXXa1{lBN#wWLkbJ0a`$479e+ZtxZ;}GS6xX!EutwOCu*yWD?;XUi6IdJ8n z%}D5f=p{nXHx6Z-lDe>)n^1N_+Y z@k=i~B0BeG=aLQpLdlA;p{wDfKrs@{E2E21_!o(R2ZPLwvLWDiK2?kG|KtDq|Nfu< z_y7FA|0m0J<-qIvAIJ7TBlr_C*I%hjHw^;*RED%{tWG7H&L4#U6id|-HPy9MkfYv~ zuG+}s#2)#djh`yih1%I@N3^(0K5Uh`ul4g5%*fqYQ;2=1HE*Qg2*qxcOz} zd=n&OfmKo~;_oOC=!C-m3rH^>X&a69AB}H$ztJRr{276?La?35T%w$n2IA7^ji);J zEBp=OB#qpG82O)#A7xo%ucM1`G0*)m2&9K44^DpR($*=ESWdXIhB^PAeS%&q;U3x% z0NGv}QPvCiEdHoF>qMtX$JNUsnjwayEuxtD;4l07pM8P|-PZFTay|9uBnwx}a6Z8f z+Os4KJwn7S8)`F{-PrP<-GfyC1fTQf+py2K0*kjeC^oZ0@%LzC7LC|-4{X_LNLDBc zf*S9CH2%F8u{#yM!xp=y8a}vB+WcTVR4D*k8tBaQ2--E|yjpGX&d5;y`@ecL%*)*U z%`1B*)W>ROG%D+#Kj&V(&+qs37Mu|j}%5ON1~P7kgqN09|yP$pop8WhFvzB0Zj1%Yw~l?0AQYNiRrY0n^Dr# zr$iPJSUnzJ(jSvak3^gi$1Ae4iTp&zpUwbkK$X8x;?g-#Kz=K}%UfWlgrvED8=!^Z z3^zJ|fBk$OKpfbqpLNnzZ*=bfln|evR|D(={I*OQG$^WlyfEGiP(1nnG~)Ysfb9seC(0JQ(|2QT89d z(e~>kZQFx$hyYb_8<+9~FV|30wId{#64 z8K|hY)s}7jcAM* zy)4{sBDS;9OWy}ED-0@5Bo z_oP!UGTj#|5+J@_DLgs{5bH;BMVcsqxmiVGo2#VueE0<4sO{5;X(@#*WYKFr`dW+! zX0U@ufx}~YR%?JR*1ZO5alU+k3clGs;~)2UaGh29))1Owe{0DP8w7(a2Dw<|bD#gl*fOQUpJf?VA=lb3N9bYtU{|=CEe*;|Njloe5?A+Pz20EcN)Shn#*R|Mt9% z>;-##?ppa zWq_FL^j%&kkvfO-wE%_ceM-p{kM%eO!=aNPtV0R;80T80AHCSex z!Rzv94H>>>6R3okS)~Wi*t3m8Z+Kv7Vf(K&g<1)lzamvUzLYO{lZ4CAm<2gAk6!F8 zCN*B$@Rj}vzvQp@4wX8S6 zvDfi3LZrZ60OO09cib61nuuFEinr(!<4Gck9u0^2Z=KjpB{HeK=i8TvtenClWHUO9 zEt)}0TiyIc{e*h?P&)yCB(xvl0IonZKx6ZpkGJpO^HHIE3MIFp=O!Slu|`Afr|q_DvRd9mSibN=WX7GBJ&zn<9jg$Q!twq!<`EYQvmY;)@-%6Z^K}b^r{DlRbPtp~Zq*{5iLRKhKfRAg`WGAY5zFIYv zgC-tj;OzE47IgDFv+imf2|gP0#kJH*dY%{<%46O_CZD;`*xCQT{t6^^eo@soa?zF( zn*IpAPtM?Y9U+>flMxKb!OtSLcJfD_)1ZD+RGZUqhq)1kvQ;vNVoDM)_Q*9JM>q>1 zL4(E{bu2|?(D8{HN$)-8z9{8a~2lO|75^BOA*q-eDVd( z;)xUCpqI(UxsnRWYdg}Sn8Ut~P|bFBNie!IrsIf0wr>^v(n^mI3adA^QRZ(FhN%p> zB^HNt>}7}+S?88i!ZR^%ZzBYQ8R8Tj=3E9clrY+)Jh8owP(W%0F}$mT-tc%Cqa{f} zpCxo9`!dX7y5A!Ss;`6OhSC8MAVNfQ1e~cPJ~Rjx=C_Ptez}s{#5gpE$f?x;ciFDj z5#rqRLFap{)sIxdsq>NB>u24v0LVRD)NnY7EZqy|CCIqH4N+w-AC8W9amxnunnr@~ zoUZNxw-e|jPSVhq!ii;?jPUx!BPynz3F)xN&%0oWizii!`7#Jn8e3xS%uZ>0XOiEWSggQ8xzB22e9oDH$U}+l zWVVr|d`f@)HEblE3u*#bm*=C7xoP;le)d7Ds4I$^;zRmGqVL=%>GcR>v+O#xMQ?`m z4S4?d;ZK5|&>euU68ENrIbXhTvH0gHi!Z8%N2tZoa6yEoT84OXyqu8h>7%r2R#%_?3>dRq6SAS70I9 z`IqL@zVv|3Lm+Qz8E2N8>{)N$fK=@FwJsmqCEvh$2EV)~B-uBBv_*;(EhHlZyQh=h z5j6AWy$nDgj`+3kP`dG!sywTme``*Br#&ulxWFZr6c!RjI3kPCpPmQUa(8Ty8k^o; zn-A3bJC=a&m#y-;Tq~+*uFC8#1=Y65%6fcuy#mBdmIGkcH=YWQ?M1)m1zmj~}*n52=?}7$jtr?z1d0h%KXvA`B#RJwgzZH1+0x z%;^UKY%$wB96pNkJ@T9jglrF|HlTNHfDI4Bc6>*IZ+m(ciiclea1S{Z<6Qxn|MV$y&Gudje0nGiOr8sD_~YVbNNNc zb1uQIhEnOl=$YH?jD@LRVPX+W%xf^7k6%*rxj1Ud5X!@?Qo!i8j<5T**8o&_E%CdB z*MPt+2c8E0j3m20i%ZE^%#w^7>6g)zdmEz;VaZ^$)wjEg1f;L2r- z26j#O+QRR@UzN$mHRQ!{aoG>oR1=3998)QJwx9dXSfdnOx0E~U?_2hyTB4slUIr*PSNXD%A$7KjvYz)mX1OGm zxGR=?!BAC}qgDyJMqKU4?JtLqnQN0wzynDhmk96`S8fUQv7k8YE;Muc_J|`KHgorV zrKmw_{Bry(8}ALS3Sj4rf58`3Munt%=QVfoWU%cKmE{+h?68 z=)KNeC$*Zwe$={tz8N>&hMvjH9gBFFyusOeNOz{x=U9$f45L+*|q0p|>e7EuX zEzaAr_vH&GW+S4`;+iL&86jFIb+CQDxX&$i$=D<2vIX(rc)yEkRownupUDRfn2>hq zN)*<}b$4F09Kkt$qbfyhZtX%ZjX$fl@vR`y1Z2%$xBn5JWsNN9AorC!L(@ge?MkSw|34WHJ{On^}1%I{uA!UY@EvW%8Nhm z1uVTe2QPk`vkS2Q_oG&**tb=^+gUXFUw5vIn0BN?F*geM0}Yp;1$}1^1u?t<0(W4# zza2B~-}&wn&(_>oJtQKai23Om)6ysqK%uG|%=26x{vz9{j63n&v4s5npx!0c+))m}!O(@tLP?6m~qf<59o(<9CX>Tky^bP~sn z-I`qHJVRJbdoW3~Ho4buED%0v>BEVGkLV!mJSF3Q<0-8i1xCp6^>@E(lAV#7z!9m# zBeT)H&=j8_=x|v*`)TEl@xSp!YcdM%5HCm9tO8}{tHeBc29Q%cZf^V|inSx;d+rcW zZHnu;Q~vM&B8Cb!&6$}yR^`yG0%}aj zXGtndM#;9~TKxnn!|e(z>5Fnkpo$$TGrgw)jg? z2wwJmi0lcJ!>_co)9OT-6Q$yoWq+u20?F3=RI4miWw(|WtE@9V?e;oE{HM#$-Wo_b zZQi8?ktgywByK#Xm63O$j@K^=#Ukx^W{`8dUhaFFu*Tw6G`9L4M z(eooz(xmJRyU5UFE0CC@`7EZ>9uq-0Dh;i8?$aNSUI_`S4k|q;wQlwsA23^{9?MKn zR_$$oFl*Ee*>v7+`hMZcJ{&&hzkMW#oT;0Oz1`^6Fr6zX+P?nEL+EWINJl_c=QPH5 zdOZ4-z=uD{Df7b<+?*Wa#ho^LTh)183}g_=L|4?&W(vKI$D@0CcXY3gxVVA?mew0f z>U-^5$*{J>P3+&$Xg>l#FlAW&1#A6u{7O?AUm*<6ck0=all!WL2aJ$xTXsC1oh)%I zty^9|^SR}v8P9jlno7W##m-0`&e8TV8hxUAyS{r!!xs6=nuP=_VNWHuQ?+3T-?)KN~O`$Z{jQk z^{Ld^C17^F(@TO*z#%)$UuUU#Jbq<-U|wBEJbE!U5<0G!@fOm1!rj*(g|j(}{PI_} z3J#-Pd@6Dufr3-g{#p|mJwIEkxXPZiH1mQRDP~P*3|W*(1C3#H*|I5D^;+VI9$6mi z-tispZzEKiWBd;ncOIEVXz6Iu5wC0kZfK(8#2%ipresD&r1QK5#WMFA{cVguAGS|$ zY)k*qZjF)oYAsoY9`(h0F5zIkS6E_d%B;N z_~vCI2`JP!=Sw^eJz-66LlB?x9PrA#F9e}&PxLgH;&hq_f#-n@XmGivn;sUcW;H2B^I)6n6*@aqUV?g3Ra^ z`zwj5?uNz{{oj?SWEwqX>iHeeQvdtE_)9H@?f;Zd_}*?0>VB?2=z(nKFK@Z|FU18! z2O`(1&BY&HjM!R{}=jAJpTf?@8PmxnJ7sTjD#hM6-z+fiJN! zo)tK$M?|%8q@0s0hCi@u&O1LGh4|b#>+hVhH_vfA4-oO?@ew7qM-M({W870w)qB#d zTf#=|S~t7&QM%YUv->wr>N=hWNaIYcJ0X*NpcQZT52qr}^AOB+rnNk82vdg+CJ&hs zM(VUrBcylMKWeo5o@V1E{Gp{01UA*equFT-^9EtjnTt){#LF_hM}_n>z#44o&X2u) z{m7U2D4vT53LI9P>k(l|5o#i4p8sG?%zj-&)#n@(zyn6Is!2k{x zp5yI1)V;L-qtHSVmaK5AtsVh%V=UTGM$S9sem><9aGTeNzg|W-t877VafPHPC8>6y z-wg6S;eE|YY1dki@s^v(Bs18cp#zumc?b_RW+YzLyvkgR5$jvONDbljy!T%+C)}uk zFL)dj;lL4T)qDlUb(_ZiU3ohcHRdq&4`V%xV%*f5T9jUAL5#1B5=`q#I8wI4m+tox z5Dt*g$2pl{s@K%E0~(rA*Atyd#6@R`If7UaNhqvuLlNQ2aLoUIB2^;?O-= zg;k;v?}Jd=gacm%kM3sfIY$x*2O|;`IT~&!YyZ0eD=UZ_at}-Iiqk)LdH<0lC)f6h)&wi=zlKv{V}yII`i90Gc$vUad29F>~QW+jbf{x<>?{&K_sxJ8I{ zzQO!V!4Kq!!zfgt++LK!Nyb|%3=>>km64!n-alFE{LP4GY%a&gs$|WS3-69LjDvq$ zRrZKT7L~6(UvgnAlYbN+tIp`(47uIGSm{8`S^Zc_q|%p<3U-htopdlD@Z-8F?_7nm z^<+3*5#Fg9#VPkojwQ{X{eb7sYW(|;?ao!e01q3W3O-z(bBez$fJw0)Sz)OC?x0VP z1Jq@j>mR}2lr-RS$w_QK;H_x>llOFRK5LDUeBUy8+tD*BipLS+4&bz6q|wdW>#~s0 zYMGu1GU^p%wkOR+4?H%udHolw<(`zWx07_$%}pygqAVDhGTQf>EFR{f)w^&|g0MVNlzZ{^J+|=sFM>vfoSv2WVJojW;L$xJRS!XLTzM1u zZ-szt0EnOgXRM_<-`H-tx3pC-X5K5mTBTt!x>%Sho&D+z@0c6+&w#testK|sGwlou zhO)=PGC1H%_~Y{smDAXh7}aAx?_sfhq-d-aVCh=hh|kr-8TGM+wpOAo#<9GP5UV#s zj-bRV0iO5YsUoUBvD3qKBq3s+sUC5HJ<#@c)a(1GXQXsLe};p)>B4(cZAop&26uO5 zMPgpBC;awz?|TA+$Q1?i*Ce6#dmW=aj-j^!?{WWKR_9c~ zO&(WE?(eZi8_3AIrw8~lL@y|)GkNDqsYGq&uok2KR$glV6zq4@+yxudiPRB?wtpSq z@wN%x{kt`nn->^BJ^5Ngw@M;!w?py#ZK;vJjBpx(O8NsJ-_?vRx;z`PvocG0-zj3Z zs6l^OSLl(+r{RkU6nkLOvOPxt=yCPfl%3c6_TL}F`h;1F& z#4D36>GP4d-Y9|(jDIpp6^FL3eB?5)+eg~nqud51bXgI=b2gE8F9YN%Z0qM)kJQe7 z=PT`SO=T93WJwUqf^?4Z(fL`MB|(&{j|0Ss;O?{b@~&O3yPmz6NC$sz&U%lR!<6k#LXxD|96LB9Y`_q(#xo#PgC*3aUvT4KmI*WWd|F1#Fl*-r3 z0P9xhqK&ckZu53Stm_{;{FoP@1GTIYy!xcNQuxrZSdkY`DT}Kh~*KpPqHM0qc zg2y$EPU-bLjeG}NPUXe~6y{2_`0%#5nm9&{(5Vo-AT=~><>Xx)VvYGH_x}1F&Ku!$ z$YEZcn+zfM_dC=o6BpC7&YD#BDpD+Q;&&cu{<)VC3hCeqN}ln$T-GZ4&+TDLepqhK zBfcldX(jY|S|UQptwhoT3>o#TomCd{bG0EG=~v9bBgnM?falkU|Nq#Ff0tKo1(>o( z5R+?rc{|McDIi&DiQ{Io{pH@wrkm#sab5m7+DWrr)FVeupMzg z{(FGr65)5P42lb`Lsq2>dtPV&TRp45kVfXL33`^M+8K-`o>F6EJ0FjKIX?dhQNksI zcFpnAp8MFOY!RRADz1#e%2l2hVLq&J9T4F88{FAqd&>AM+Qbhr#>-;@=E7~=6KW;2 z8VHWKJ-8H`yesftTI%QHCt2*$TMvGtRZv;agJsU?g@eyIBK*`(zDTXoW!X~Gi1)KU zU*ifEi~4ZxDlE{{53F@};=-MxY|DAHanzf&(72)i(({%#XcMr!oS)o7^Y-@2!gSa? z$v%G!a*WbWwJW;k8OAlSB4)|=gyNx9aj6c1>?eVW&M%bu$D($=2GDFb3JCgxH4qH*5NkH=5{a*KzHUoT3+ zxt1`g3mvVWIsSrS&CfmJ{Y-`r95ayFC1P!V93U{9rcF6dwF>4uY4d0~*JeN1iL6VZ z$A*~JJ;4&$e(3C2*GW=39}j<}#k>5GvF$CXuc(h@N7nMDS93Yh8Z4*x{%gKB(}!?D z0PI>)i2gmonvjde!tmWO*sB2_pS1d?-h&||=q_-@Aby6}`O~p&WpmHx{`n%WdF{%z zYf!1ywdsxPy>0FO>EEz%YNh~VyAc>%lz*T|j^x!pA3tv=o!iUybtpiV#zCAc)vHv; zyk2+=VRlO!{m@Zqu1p-;ts8GTCIU3BM^)j zF0@m&m-BNzeg617@9=F6q5HjW{gr2vF-)SjfS~=z4x+c`0q9IAeNa;MFu@4wra%8s zP`c_m;@JggzEzSH-F|}RTER1=y{bF%h%DN*X@J`}&uK#Zn?fZ3unnWCdNwoEQsR5! z(iZ@*Do43SbKYUsS`sSx@1vI*SkEw6Pol%}#qL28?bq}aHRDivis|~ap+F9 z9k#~-O2Eh+$byk{yh_s!)_K?Vlgln@E7mwm+vZ(3pYPkr@XtdgeisEmfuPSf2V@d! z2SNSGV3US_eP&Tg1+1+aL1QvVGF%|^$KwcVqQVG!wYUII+g0gpM{>`;G#i!%P;WQ@?natK-=TiAY{~BP;3f*$rdNbI7p9`{&Boo^ro#-|Cwj)BQ3X>Vhq*KKOE4A?c zRT3p@=95FqBzW+=w#AMOgK=1Q8U*8T|Z&~e7 z^E_b@a(~mo`}kuI@qdR}I#?$ZZ^Hx!F!bVG$N4tGco{+bcBs=Ed!+%Q*!7gQxUB;V zn3GNJ#a3vI#}TqzqX5}{Gz7vulzWF)DqBS@K-bWc4r#p7@)ojFO9?E7*8!?{Wh-fa zo?n9qusTMa94jWP@1(IH+EfWZW5 za5i|D&{5jBC%*Vuvv<@A(kxihAzT9(^6ov~@Bww#d)so5nbiV?puQ;;5 z8L>EO>@c)_^|Siu-XjulC~2z&vkS|>YLm+v#=Ot7sKUyp>3=wf*&& zBY-!BLwcf<ru1#pY2dyuLjk+WHUKv;|?qRhoe^({U2oMSe4>^u!G{(JaShw zwuNMRaefOW~4^MOK@-xozEk*moIUOLt{r@aF?nC znzWL+8?r7jfS%2~sg4^+|2#mix0Y(r*I#v#B;Y_>!=yxTb1=%ywcwH4C=G_ej3I4P z-|YD&UDFGn#}zK7&2(;QG^4O}M{4qpBY|R475z($Nrq)YIy$8a(7!+vS~u39Yt*pbRu z_%t#x4>NfL`{*un#2eYnv8oFhoXNyep$c#1_Oz zW53&m*d2DK`>|JWUforL@wrM4LFgt1V(va44_|2~+fV59dV?mffDh`IfDQmWG5eqf zVzNis<0j1IIkUO`_wkFUc#5;5)S7S6rMw_C&xs6_!=3#q1+)HwWCsB^zeTpGF1cd+ z{(N;SIQU;*pkbANDo=BM_`j0Ro*YZJus6rS@Um*tJ{`Y&VtrTi*>y&)Y-7eJ*(nsb zkl5zZebajICQXQ&$~oE4`h4@~dZD;jxN$9R`*GJePcqg$KPyFB$6>E3t99-{VK{fQ zfMfIZkbcg?@dICz!8_+l1%hRZ55;=e1{cJpCpF9@wx|i~63f|OffQt359agL-EJP> z5v%KEQVwGxdG|ozvK$LEs_rmi%iWgn|6MLJuKA8>Ei@W04R7b;NV(}KOZWPRF)&r) zaR06uJ;9*p~kMsHPW&78F0R{1E7$+p@vZl}cL#5A9Mw)%EJ-~jIlqG)3xgBfg z%kfjDW9PpTnt1gpN3ts-FQ#FX=V>dsHEdE(b4zkj?CpTyi5+=3e4f{Y;}M_jJ?em` zOMBqO+s=ES$n5w`HU+qW*ttI+K9#n4?XjXe-|-0GG*nE@o>9HCMshQja6j`p@>}Z!?6LSZ?ooxKUAR%H51Xp{o+R&Z#7D!rq~v zF9^Y&XH<|l7U=*!xZWv=4?X1s-nZJ8Cu#>z01kLO9z7-T>!|mtY>Q>wV4sgab4@z^ zH(3aqEXg5|-`->Qm52Ly__HiegEcg8vQg+SC8tjVNp`~sj9oFX!0oz=qtIg4wmg6Q z*uq>j(GT_vXHPVpWa@>zpYm)g5)i9zkH~A!0Basrnp|N;&!4}ZR%}!^Ae+}w%+t%O z&MRwP;?`?`U4As_Hn^rLMxdp0TwU++`~_}P7}l5du%D@2&u6t@oe);J0?sZpTD}+@L z?9s2nx*jR_&3UyS&jZx${m%!C9bCByO`tXAMYt}>V_geTe30%0HXazsfmD_q#9VW0 zRqY>PV9Hzb%;%pNv6tHV+Od5vX_vH)w89s(%80w7leQ^4TL2LR=?1~<}L@QUK zqvO~TPtSUgq%H+lu%P{~2fdRE=69F_=wtibah`#ttY3de-(bgJ1vD1{BT_vm0wHEB z&vS9~_g}SYZjA;L7^J6(P_RqHRkjC~&+K7O0Rwh~gY~F(cS16{hjc{}`uky{a1o%R z6$s~WtaV4vXGYU>t{*UIwz&e6WCr4=6TBop&#ksqAi>Zh_}f7fp|L7G&2K9H^2iBh z7zb2S1iPvpKwzU$sb!VqhCPK=dkWO+{eR=Ko>fQZBIVi#Syb7MPqvC8^q`Y=kY1V`f-E**IWDj$~F!y;Td)Sv}1dKhj*bd5dg}NG( z)xVEE(zrRkOWQ}~o&yWpKQ0mX23WDoCzOmALezvEm4cPkWlcJs51%I4O`w7%V8!W_ z#2y`ohGtI=_sLGu$ujmOATjFIl`M9%ZaJQhf6?`Xu1{a%vAS|N4wg$apQM_c!F5lf z`4Jun^o3NEBfypzIj+a!>(hbN+&~IW|J4_0)C`=Re6*DZ&eq!KZ2{s9pm1*W==6k8 z^mP2-LDrAu{+8pDfDPr?9{0r&M7RhiGGhJ+YxJ}N0EgqL9?t{xr!PR4^QU_CeoDY~ z*Z7odEv0iGX*Mh`H)kc42U7NJ3{9{%tg%?ZdDhuIj`$G0w}G?PD`HrM5|Y8J1u~^Q z43PU}b?cdyymQkW+y%^^cFmW6LBl6sWve|{+~r5mt;o-V?sI=UzSdm=k>=e(>#~4V z&xdKAF~!(9ZJr2zX?Sj7kPXGSOjJvtvP|Rh_yFKg)l}oiSvwEn9eNMVm}w0@YGB8kWW{M-RJo+y5+}4MD7=);4RW-d&+9FGHxI|&IgO)b=HlEVoX!22~p6*tdzDd}x$pGR&=YxpE_ zwBzLq#3I{CKDi5-jpJ$E625)^h+BC9Rq_UwfSF}1jvsjPk!aHAkAKxt{PU0S*7_eP zj&vhV=^KQ!OIg@Q`f9^ge>twlqp#`x!Y#~|3;n4AVF-`PoHpiRQRW)Wi>vb40w2o8 ziYVktZl;quo=1qmK3s^CFp9b?K(v!5qf@etLSld$Coe=ZjU@i}T;BoI5s8Z8XwL%_ z>mhnNDYC+#LyupeK)xK5dyx(-P|hrtJQ8zx96E4J-s}2&_)=XLT4d&6;iahaaT>iEDr?T zoQd9pL@T`7R(G4zN+59EJ7u*$UQ-nOYAUX&JMoQg0nBhk&wGGX8euynfrk+y;fHYs zUqV|SwCrY%<~+t$ z9F@zwCq3yQ?(PY=*19_v%ZMHd(8B=nFVNWW*4|Zh19*&g%f1F%nXix|+hYnz?6+!k z$(giF%Wmvpgf)JK><=_gQ%g<5x}&}Yr=zPL*L07tepg_KO=BpTTx#%9lfIm!FDJ+u z`HMEmx}Ps+iMS!@&zEmn{xtC}?$Q%Y{AGxKN9g^)_pi8$&-KGPR>~Pf%{Asy*2F|1 z%P5>*E_q@v7ozMS|N0UXQ}%XOcyMRkO#DS7vIAg}aKWJ$h*sc{uj^%qx|_ro^P8Y4 zAGz49EWBE<&poz2@^m*#o$CiK71J2+J zOhxaCFIPC)Vwc?^80MrQWr?WHW1H$m;OWvkk3X}8>25Du^sm1P)eAY5c;Hxr^g1sS zm!RsE?(1=YIsu>7`f`V(!>EA{O&hoH2toYX;(EFBhor0R9Q+()Me}|dBUj=`+@`(I zBATOKsGm;6lp{8U=hgfLf1!p)KrrXQE31a~GD50nSR4SK5<`2jLUuJBYkK;C`&;Z0 zL|2gS6{b`}-Iy_S>g(nM~aBD3LR{c1cr*6e4Yp?=%XpvoQ57`}~=M|*_OcO{sHF5K#4K7{Nw2_vnm zon7T(x>_jG<0R+vjDleMUtEhy2>sKMk`?T{0^j@jiZ2x&fiEJgPxtuuo~)sHt9U-6 z-*_COuuRFU^m5p+q>90+(f1t?9PAf*26;XvV=!X#t#Pn%jwAZ>Nc0K1RR9tbADZ>A z#8J%SY00$uCD98i0pG3GJG*iu9r*tz?cQ=Mx3R5JzaQZ5Ny3n*3#ryr|9f%xjj4!O zS(bn(Y&#vU?VYK)mPC>BGA>kmMv0R{KTy51wA)L78ceS0xq;(}dG2yiCjEvggziCW zkk9>GSujR9wD%?ALdza%+e?fZY+s*%xYSY1bbr7uMQvDQMWkd2B5+a10|u zZc9M;@?M~b1eh-e;LvF<9%m%fBc)aV1(Cy#)9fHxtpa{~3{@pB(eU7f!%OlZz}zav zk@9=K)Ym;7hMOBLo-g66KL^-K|KHDVlw)Q;KwAARK=oG)_zxP#6ebfP zl2_YJILLE$l%DD8%(t-BeUdw1Qkj46uU{fXw0?Kwmu#vPgK)7jMXsOawY&8*^E0eR z_)h|Ou+hMt+bhR>jL|ZPSJEieCG}?-IOp903>UL*9gO#d$URe%NM=fDw{Vo(7FAa% z$=Rg>r+zbQyX>YbBxY8Noh)g-*DA(W@MI;gwx&}UK_d82x(#-kQ|`Y!U%eE8pm8G< zNke>N^4R;g$aFT_9>Oj@D|Nt_4lD6|gCj$%$E#1gs|M#)<2S=hN zH{M!UnE|Ug*N@L7Z6*gtHwK!m3f`{$MB4Kj3DXmaCO?MK~b3t zZbyvF@J`S&uDch|to1GEvVh7nAn;(!d4kTRIJk2Fo+&W$6@)i!)2|u9EoVIQYkI~r z6*z+oOtUg1nR#q$KD5}N!+K{BqOvCgUB27GhgLzFlsTxT$heBBW79;$x z>qlYJL9y)X{j;op)M5to{zRU~9>0YWX6os~bSo(knjUP-!{uc~RGoC}zJ5l3mZH;s zriB$nt9hF*ur;-^tHTn@(n7+G1^v_2BRhxb2dHOuU3N*wS3c)wZcc77-?W`KQrch< z7xV@2VYcV%=Vc}4`%~DQPj_D>0lB{)<=h!Jm=#hL6>Y}pFJB-y?s;d|-EL4npYDGY z1Hee`R;vx^3OtQi2V>YmvI?DhNL4Iyj084S@P3laDY&9Bw zUObZOt|!PvdG1zS&lhm$Z)9PfcaR2wkB{8{bG-$Jy^?kx{t|OXU;pr2V!UpI5i_&) z$^JN4=a%vK?ZRrV8)?p!*+=jrd%SbD^R^C<>^@#U%zdR6p(1S1nLMz!-Ps?JGT6=< z<7$Vy7Eh3cNy0p!>GrS4=M5_T@%rP7_&&csuL`=L0t5o3HPYf|Vv?CWa>R*xZnA%7 zsl&p?sCCS*mh0n9rU3)<|QDwr+a5|N|1WcJjz2pN13A>*y1(Y(i{ zm$MuyE7hs}CZ%R#&cAy}fJ3YNun0oocnc7biIl%!1K3`>0NaO3=Mjmw7y#McMhvg` z%;>nx#Q|r&_P2*{D?lJ1#g@BKjD2{_*FV+_Kxuj>zQWSu-f=*kZ*R-Adb0{qDZ?UCelwH6jQs-$sUQk98(4Ja%3pd34Lt)Kou=!Ic(y__%<`Ap z!Ko69*~u(zbjdR?jMdx2<-@dvf?Jj{f?5h}KvQZi!THu6<18VQ9*r(JMF8Z4CbYL< zd$;F{kB5sB8DuZkqTitPAGQN>C_hE?c(W)aK?eMk`QuytaQ&-XiLMX5#fSm2)Z)uF zt4CeTBaVvm!0OzyR=ow99akE5l&9vRcn+W*KL05{>P#e#eekvEKBNnZ=SI^h4{ zL!W0mOnZ0;AXPwa$FYPY{uH1niH3_bAu&9*R)YTQ9dE6XHT{RV->{T5lv8nE#BeeStuQ60xBkdE>1Gqv}QGS${19=rbbvfcAxx8Ux63yel zKEJ~^nR-dOx}5)+J2EucNP>V?y>F%IJ=dB7Ti|=WaGQ%5t}li>4Ni`YlYL%tG)QWK20XoYG5%!&q7PCclkXsM)`T zB{Nhg=f40k_RK-;KV~~X6B%0=i6EBG^%{QRZ$m}}yyZlay{GS6L|y$O-Pm21Gw%dX z#+DXkZ>bkI$KVD7Zae(-jjchJj`+{t1#F=B$#_EpGF_H6y51MYRm6xiNdc!FH~?6` z4w< z8kIeH*|VE+S9FNDyA@xY|@@&XSw3}J!?&7itBG9wzU}e;`8>1daL$7fGURx(G-`yJUn_N@|73x{k!re;qR7vu2oc>FIpWe9ZOdQ8_3rK`||* z?YG88YhFECfg2zZLl3N{A;7Ogj$5hWd_+OpIv-Gr> zDGlR^JpwxP(n{JtS=09Wpl9UZNib7v=8G0>@DmG@Mmtr}v!oN{XHJjR$rD_k2??(s zJ;CGm&r@}BaS0|5XT)KG0$WcL=!9t}B~y1d&15>an4{Wo4M&0m1Z|P?_c2GnwE9HG z(VbIN^y#&kbjBjTR~&YV<2~@{NT&pVnfV|Qs*t{a8MJB`wdkiA;|0*+b&cOimh!+i z*Q(qpP^D3SLX8<}9g#t}F#sCw*vMBjkL=yii8+kFf!;}%6hu!$gCb*olw-@x>ca@< zHX5SftbzwmBSev{x3?I=RO(rZhxZ0Kll8dFCU!n zTZm*fltsQEK7|;gZaOdB(Q#;IJEP|*r9IC;aRk6>>IWH*EN(%*#R#@;9$)h@TIQvT zYf?B@)EXp7AM5=s2&>HBD&q-&G8Rw4PDtp-TZjVJdHeaDhD&nGgf#eYxKe_Hv{gI^ z3Ah%1NRLrc7#OYSbld9_UxTCUoJV`~2cV$d`rBm>P5Z{>f8DV&klKFn~o*BHr7 z<{zf0hPZMRRt1abLkT#HK#kzQGchX5Ye5R7TD+imUt?rh5hx~2#mz($L}fx}dcN-f zbcoGz!Qi<3#;YJQkTonox+T+&_nVc4#mf7q(~<%+sAza7U9b{mrkDmy0vZ78W3hC* zI<}Hs@b#+{u?$iq>ySC=78`diZ{G-$m{G|+SQ;3VU4c)i8f93{izUc|@7+bJG!v8m z%qK`gd9KetpnVRUURL7{M4FhUwtL959Es;6rzZH z{4Qn&r68av_xYJ_208TGKe~@FuVIL{AXo?Ui21dz2+uDx#sFbJp1-$W$IJA6`)e{4 zRaHcdA2?4&k>~uM@WL!<*|pCXN!faVQPSD`ZODwvTQC~bEa;%e(o%w^`K0$cqP)7N zh9ebl6&sR~(;E1@!o2@B;+lm43&HM#?{q6GQL>lL1P6uxV%L~uKo^fJP?gIwOJC!b zz3%PTA-AF^H?T;#CyCUcHveL`ZE#oy+?p1x=K%K3Pz_w*Py6q`X*RsHOmDY6Q~r}tSELWPifCwZFfC0eSOHyGTHW)b{`wX1nEn+K z5-f`P${$5}eFz}h_)dklxd)NXv!aLZ5(x1;dPv{w@wF-9O*R#VsYK10pr+3Ga+VC6 zFgh7@edZr3BX$ingqcn(r2$m5=j-?AK7g0OBC)#+!%kKFEj<<#Vv8Vei-Tp-Phe1L zX>&;u{C~cDk>alJxuGtptD&&dcZWV;!v!I@WZI?af5fJgV_?s;cp5{}@aSu*>COQslO3G|9_vZ+;=kc?f#hT#>*4K=%AKPEj z%%~;Xtu&KKxbLtKO5SsLF30*7A@DzwYgIo(j-wY&ZYNVg8J2$9j1U1S3u)$g8=U8n znZ=T(8cDC00B2bld&7{W=Bc?yFau|Yq6N{9vuJw|IP?8aOKy!~9YXjeKZu0lON5~R z{!%?`dC(j&z8U3bamFs7Vf*I1h`SV|jy0-iPCw-_L=L-?Hde<_K}ycs0UE}!AfA&U z39df8%5aAdj70~k`bLlSTwYdE0sqLrq68!fDg~PV;&kps#owA7Yu-<;y->V8V8U51 z6nX-Gy|kJao^L_^?KD4R*@j_y6LYOIj-O8vWiJ!04QatR<#)_{+uOC+rmR?@z{H~{3uA|q9BCY#TChX zJ_-mtlxUEJnwzX+i?7>7M*5Tv?QxhGqd#6fs8i%d@+uv@s)px?G^B-$Nk_<%oL%5H zz})9zI{%3^;#e}(jDy3&!C^-?x|RXG5DS43p+UF%PvEH7s1$ zqkitCs>X{lmRfzQ2=gV740;~HOgu%%b|crGdng=j!5&#G4@5!PXF%v3ZSQ_E2(kE3Ja2&b5^MNiq^LXm8t`Z>Hjs%)GG^XgDlf6 zh|zG=&`7787JX`>Xnx3@=hB}0YDmOo*1) z6q*Sr#vzEMIM-b9`a}!uq}-*J$%>-qx?aCS!Ln<_eoMCm1G_C@lMaV|i{9W7IWKZK zdBaWnh+Pnx#ld)cA6guSy*2h3YL!dgxGVJVQw8DCX1QVqp)AKgEC(9kWQw;QOB`Z) zmWH0DLSwm#ShMZt-v|5L-g-doH@G*j|KT-TWsw@+)ypRaK}h+!3ETgD`Chtvq=dgn zZI!9?3`5DUpst6K0xuKK_7SZQYCHsw4~gkaS{|>TOlYLH9^v1!Ht<+rkZ&}G9aVFX;L1l)Njb^P%#`K_ zoBKssK>jPht?xRMFdWyQMiSxrujG!}FiQ}F!83&d$v%x#e6V1=`g4HfvP$bl_EFy> znPC7EiyN*{ny+BiNPs~C6!Xgb}5O^ZEM4%(}ubrLu4EOAEx-Z3QiRDZu_0`xBfrPgQ)@TuLWK(=Hml(N8WvuH{2N zwir8SFg}gl?G>njZD)%;d>q1_17uWrgi*yJ7CRuRiTTg`4P4e5i{$8qP}Nwy7A>A8 z7nZM<#eARJc=-kq#dFuc`}x^v8mxTVa+ELWdO0(pIgy)F<`Z*(y5x{-uYJCL*h4y> zo&|5klu76DO)YDwXvx4bbCXTRfHr#$T%?2Uc)s=;Y<-KkXOM&ES((beE`J3chLQ({ zFHZr6uyL>J+pYg{eQ(Ghwk@1o^saAnUIVIp`1yX$@&jYq!g6aA-g|?9u|vJw`}M4& z6)Kom9sFG>z3Q@s_<0^{WpewX{}jsO6>J0Y3^hgi@NeHB%8_G<(!hNLr$1Ap%q6Ug zBmIbO)~=(oj*`|{BRP*udTno)FF{4!c)g9Z%P74P)ZrZhim{iZ4+1|Lnx3iqL+eQs z>D(d*{e1nlmUIv2Ji_}9k}Uo+rO^yJz?b*0x|@`J>k;ly>6u9OoLt=_2CW8{qkMR1 zzaavIPabdQFaO_=S;jXiSfU(k048Wus@1Buv^O^g8mK%`p95qpLajK;9G%*Jf!ydK z($QMN(MQ2;(Ye$ha})r*tG>G(iD3Wp^#d1-W8L?UGCo1g`{wTwzFmV9B>)NXuXiP#J{ItS6%x6{fyyAS6-mj5i?mAJq8z5~BP_RSu_T zm=rAN7ou+p923bi)1S}?JR0wx-oEye8eGk=MI_=eLI!0EG2akW&wwMJLu~P#ouvtC z{!zez9;ImKKzscXY|O|neNR7H$nSbcnDnyGFtz`|!G+e_;Um{r*`v?H?jOn3A1^P{ zWfP3O9ja(QYeZ&BHS={Yaghxs{xNq=si9H1T#~9)VAPl75*z+8#J!r3>#n7Wpc0x1 z%$`Jp1{3iD%AbRT$H{&{`*@EmteFdZ+j9W3v6=egMPgUXO=9GApfcS`3t&A*aVm>0 zG!yI*0gCI$u_H!Ip8ntew-YfX3-f149wWhjk1`OI?W%JJPX9YLO{6+!ae1|xN$FLR zwm*or9Y~Bcth~t}$IqAV1!dg7y~;cSzIdOqR?CeQV<<<}+Jq#-8R&wum%Q`&@&)qc zK`Sn@dsx2qa4ve1u6dT{-4eaS)+2cP`B7*B!R7xoV8Nbwg#d^0qjj%B%RSe)~H6${rn|B zrs%uLY%EhFCg>YzOm7aIMh#d@b;HjO0qQDc9-Nu@63HV(mU{4tkF(yU~qHU z|3QlfOf981N6Dt-Ah%gcZ=O51RMmK%1M6z2R3yMX%Iq}TC7t`+V!t^2|8@0(F?`4- zP+WBSBM{9MnD%4ATju&tA8^rNY&qCS{}HmLz&D;QUjTg+Bs9?ckDUGUhIVK;{)0VF z7>aBGQm3z81FbchT`dl{dtvnd`}$Wg!?z#iZ^2Y4UCq5W?(hB1296SKou4>rA<74i z4TBvmU5%&fm(be{J>BE1RGN2)e*C@ivq!oH;4G#y7qzWu67hOnVT={Xk5BjZJ%UXB zE@zMDzHML_WJ39rG5Tr6Od3F5s6>p>m$P66T+bp1nHMtMWXA;*EFVwF(LSEek( z|8@1k=Km#dkUc`1jMl@}V`LpOrUTKJ^Dqat3|txRY$hL|?CawTP}uzI#=sJ(y+V0S z3W}6F(;h-@7{no@pBYNGsFd8cicYGWa2J+ArdZpbvm3{-PoD?2NA#y7+rVe@ZazaY zaPDiCsl+$5c|2E}6c*%sxYyhDd#HNrt;ztwJTPlOJlHo%vo-28|5z;$OrygGRiS_O zcG+PD5aA*Pd8kJ&JGWUq$`JxekK<|!3M%w-wcZkz9}CPSII8E%UoPKdVW{DJ?GBgs zz;@M*d%e4JQAos?L;wm23t?d{!WLxO`W9e~h3J`9i564Tw>m2{n;>nOncAG_BLV)> z{7C0Ms+fEiX#WwhJzgVh(Fp!vdF?L|Xu7Q$Cx{PQoP=WOe2jp*#%qKa%FAfu;WNM_ z-KFd99plmOWqZ4ZOS8i)G9-sCY#9v5&&KlpC3*=R14OS|GTAdR^eDbPI+_?D2(}_t zmf>p1_+S9^m#*`5fl@!-qB0`?Ig-e^PaJw3f_t#DA?a#Atf>X(AtVBUEGKQ;0{dG8 za??E)fJ4frSuo$R?%6CqziNH8J-z@ikxBtlA6y<3X&YiR_dw{67!$M{^5++r6mF|g zOr@ciW*5iulE9?S7g!J)QYu5zt7+U{Saf7fEgg&zVHZvRRwAZOexi8Gi50(VlJQUo zMMdR`J4?6%(eA`rU;n1@I%>grjdm=*uz%+H1w<1K_3M0#&_xKiB>u&q8D7D4gbOb} ziMFS%T1xyi%3`a<@&BU`)!SW0R-K1RT2m<7>%^^I6})*8nb zghHpqwq}<@G9SeCpwKpbHRvI95;mdnZXv(_9HTh6O?5}=jW^Wjij(_xX>LQ>t z%3yLyONqqoBSYtrP3k4YEj3xX@x1GNQAt~p>dPpX5~SXE z2FOlh5{$Z$b~;$kM!`P9pWE~GqXa*`90kzIGjPAoXs%s&%~#DX@dx=y5)t?DgKdFa z)z*8t`VGMNugjb6Es-CnpP6AZ-QL|Dl;b}!2s>kd+#+uFa{a>4M*}o&YN2D3H2RCG zLf}hM1H6!~rov%y_Jd>@4YZfrzgEL$tA1;5GX)Ba<;aLe0~dNe!|AwAkk)IabPlsY zu2(CHhQjq0Ax4!t4SUD#ZqVKQ#cVo1Cygi2^`GV=ot6KQB&;)LNA6jokns{AONjjM z_g^(f<6RA5INb9rQkVgAZlhl7mXei*F&WE4ef}Jw1-dO~p=AigW=J_QLh0+*fbFQD z^LZ=6&1=17Q3sT7)7M*khe1>r{)p$U(QYx;$Ms)a+%q}^Xm^ksYf@K7PAQA-c?IOQ zjROOqo#OD{7mqg;eW~@^U1XFt{NG&hju~!LKY$@J`e>oG1mD#odHV0Ghak)!mhmvm zOy!6zi7*AFO*>g09w3YM=Dhj6da;-;q!#kgwx`P%myAflJOfN1B2lbs@k0+=a{J~I zr?hgRbHSjrl2DOx@9=1k_oh@BLmT@s$JN#)9K+}yyW(hNbiU{=&ZEWnF+EZzUu?{+ zYzU=?08wGwY{ffHvYD*_+fqF7ykMg$AsV*`b&N`Ke^5sNm`;jBk9WT$7>|HDcXm`D z5Y^%DpW>=$W`vr~sA`hl{!?fWY^^n|sppSh#IA$w;{A8-&wLs680()f%($>XQ;-f@ z%Uy0hW#E`ta9-o_@}p!nTa3!}NHZ4zw55fboTOB$Ilj*|sA+F0L_<$g3|X5YH<XDLYky`$g~u-iuAZ0G$Zd3)#14o<1MBL&yW^}GvKY6sK*LB&UYI`u9YgD$@fKc&UYsVWYmZyXuRvnO){6 z@#y$&M$W557vx}9phJGo^XDHQn(`D0=LV30H&s`I2xGFF6o=NQZ%XgJ-7f%n$t|w+ zd$=(V5%LDZ1khg!4eZJm5q`u*%KBPIZ4s1;xkK*}HaXMf4J-#oJ_^>45lSS2!k}%+ z=~M<5hqe(*+>4TZzC%3ft`=vtFwsvqi{SOw*pWR4$V(g!5bd=20ZX2N^h-~+KFDik zoq{2j9?^)7WQ(+(^{qAje2dnMZLe^VL0wykwNoJ;KXNt2N8KVBn|5;hQww1xLF7J< zqu@^u*DqtrAIU|ZswNK(^_e|JVI_;LhXlFqQ6awSly7sRV{8C=`x4<+<2`^UcSGt} z5w8El@Fnu{bMG6+eT(4Katw#mm3DIIfuPUZbA)=8DM-$blH4-2tnWrN|j)x1USrU9f2%?CNbb%oZD^-LwY?$0WdrPaS>+bB)y0!^t3+CK;7qXn|5*rPKBt z;(KVUI>$dJs~IL_|CTJ?x2#=|!AsEznpO6dJuF+Tz_|dBr@ER&V>e@MokbINz;q^R9_*5Dx**YuVEKp<`5bMy~AldU#_1KLOjF(EiN6OLqq;!wlfc~sMSS4 z+vK_@G5aT?;IpDZOG?rG7$CE#`MmITI3#lV;cv)P9i%)zinRp>vE6F*@t7|Qf#}`N zKiXa*WE|!0YOX{jrMqbc#plX7&Tw@ zPkjNDEX9KHM>>tOT0)vxgDTr!zd;63MpfIgMHO%!IOj;i%&i!KKIXZXQlq9CH~Mm@ zBB*&14_Mz;kvSAD2tNO%+6Ha!H1?t{H20r*KCUK$A;syBNL@x`8>9-(Xu`hGO6!P# zP7*a{0PAwgo9{j7X|gIyY0+`aT~BR3D>qamI&o%5xP7dh26Y}1dD?m-GopWAKL-E+ z4kMxhzGWlQ<{D;8GttWtJvZAG4xa^0Sey!*L7;YUdwslk=~JWQe;F3J(!pIEhV9%WayljV?0@M3IxCgAH);BG1+hJcY|DL2i-@DkdkYBM`(XX$$*$Wi`SP|Y6!!rRN}CnK0;cD#dDP*)lj(Do8wyLFD(+#hjVY(dr`C$_oHPl7aS`4lej zJ`i(9Sbtjjm;K*%G0yh#9M%Y$wx|Iv+$)e;(s!Z1)9|kmYx6~M;n>2V%79e<6JCf~ zkSrD{`^N~K$kA00sGadSt^cS_HR&426CQ6Oklj|w1VR%_TnX$EvxO!5(PM}*mIGH; zYLQgijxwSD?E4;-;70}-*=;X@HMsc97G`?t+dao<(X7(I4x#7J-wGB1WpUpE+a>w0 zp3`fftsNe88sUS>IGPU)f}n>rwv+7Z2&_jK6MH#_EJjXla@4#(3@H|nooK{GmV|%d z9@6V6J-7b>Q9`s=3bXQu;7q9!{+QxH)ON0oDWYx)|U9Eo`t!uRvg^R_g z#`$1W{cdhN&K^>2Z}nfk~xejR0=+bOYsXIWA-j z`%8dGLr{GFaA`VXZa;VFDl1Ig=T`uOJB{B6few5~06sYK`Jt#_yhJ!ll6?H+Mv+1~(nE=#b_eom#s*!>iZF3DJv%vD=eGzEJzu^+uo&7sS~bTfnzbGS<{uIrGm{>f z6d-hk`bcyBB|=zz(M$$tj)zU2b1MK@SWNi}h6tW}Re0|IXxz&#g z(cBuSBAB`O!OndSG6(PMgVeaeGP@z?9=;{6^;r>O`-e&x$hdB3q6+4f`;x8Rh{XEMYH4KoS`UXLhTBCC90m7_9S>83=WGg0~(F)$B! zP=U1|XsGtR+FUKZGmK@_I>+O4EkYtgvFK)>+~*^5^w*BMeQ|ZK{fq%;3+8#tq13j} zhs+u`2@hoo zA?YEi>~QQ+ja}eSN5Reib@^NQqsLW&a`~oK?#Y3_mBAz8vmqwvD@Ybok#P^n!{9>* zGC7!EwA3tDR2+Qme6-!IL0`>I6|9A=%OsI{-F8k@~L5QGSOE$eZd#=uNm@v>9h<>~_H0@(Aj^=p~HR3m- zb#aqMOI)-+CD?oTOi9h*F? zQB9nW*zFIMEKXtTTHl~$3~m=&qTLYX7`!@GKWf&O0B5oGPdvRnl6u$UJ-YaN4QioI zF*zQ687h^8Q;4o+TKp6u!T@+{15uRW`AY-{<;8>&!iF}Gr-s+KwlH5`t{EDZV8q*F%>qN3ib;?0sFrzq`vS>bBSmW1e^~t~F}@WRg3>R)nvD&nL4HW_Mb&|` zLCJW10P79iSy1;l1dKiRJ4lf=LbWO@5tK*ef(;BBd2J-R6Ely?xZcsVR(?QE(ryH_xygTy24 z^SP{G5H;OX*gNOhyFlQ~{OgrunRj9RqBA~b}`t9|-W1+iofxJO9d;1#^2MRdPs zJka;I5QYArp0|?wkyoYZ!OjyFVPteuX!`4l9szV)(z>}t9qFfGn{41xY!me=dX(PImC|GIw$8w39k92*Ku?%~Q7kbH(~ zWRR$Nhca&m-mljXv8K_%K&QNeZiCFWADZ5XUD>o2o18Qs8QEmvD-RBy?cpKbci5O6x#~wGY#y; zw4K4mT5ypV)DXLhdQ+r8D1ymUQ78sVDeBOFD&3GYxW%_R;BP+nX zWY(GrCH7K8OC9`fst@OO1o`oB{YtGH?Zz*}XoQ0HBMcP{fb-0b?t>e%&x)Z3d%)AK z8UUE)ZvnvKwpj?>^rxJBVLE zqhw70nLzv#i8(K&^Q9)7X}L4Rk@g(mOB&NGh4@5|2Mfs7*naS7k@q>zFpW_0wkWCw z|9EAQj`8vZ%2cCKM%B)U0JPh&7@>dvBO*6LjEIBYME?`u%sEoYM(F143!G24h>IGr zG6XhlQvsGxw5UcP1+8;u=lX2UEDz`}mrrZ-%;*K6MmFp6^VK@xVa(90E5&VZ38_iZ zq36V*#mMLcPhg(&7$9rNpSh@Sx&c*ffCB76JM_ElrLmykh)DsJFDKtYe~T8{%L8a> zC|zG-D@A0|x?G3>f1Bc+Ww(OyG_-+c9&Zg1Frxj-&nt=7+__U$ocB;mikWd0IAZ2b zkWVQ`&g&z0&(UoBJ%D2{VL0}(Hzk2Ucc-7vmoI<$nIJkafFG8K{&9c32=F#Y#lp&v z$C#fUU`k6aiqrV&OMK6d$IFk}G^yj$=Lf%OrDwm4V&Z}3L4g?)1E4S|Oxa0?`Fy-+ zsbI{5<6Ag z`mMA@whaJkR6VK1jTZsY%asD3PG6IsJ8A(u3Q@mGC0s5_@pfk)$XOD9MKs=^F z;6~I*$#4KE!#u-4UGYjVv>|u|$NGHv(!Z2FYyl8nq3=d@ROb*r-7w2HQVq@{pHya% zA2LIhbUBtUx;GCIO2Fg#ls#*a_2O6u?hS~So}aVX(L5-2M_-WKni~C#0D3A7XtDK( z4(QMf*~mjyg5{M8d^y02)^>&_z5ciamB9Re9uL-!7afabpZ}1+BB($}=Wk z3qg@#Cx}i&9%XDrVvurq)982}vA%u%=r*=!?GZ|e@5C+4gubPP2%K7J*xa}%PM9mn zZvHXKS2we;x39m2$e1_+aScj}a#2En*dG8iWRU{$-&-#S<6ul67Z;mRh!*@P-XheP z7!s70X{NJF5jp4#A(7{2LTq4un!2%=zB3fSp-k}i_h;cW*!Uf=WYhDZAQ|0B`4_%-eGL*2=0foIi{26i5vLJtC(f3E1;Aw(66;bBC{#DC z0nb(Kvqom>B}fcwE@P>X_c8nf*#?ZWN~ur5dQ{oKT*;(+kJH>gNx88}hK|?%y0Xf# zMq64hR9?#;BBDcQ*zAW-TP}}Fp4~{Fyr*Mr+K43?%K2-If>lQe%1pt3E_TpG$?f}B zNxrRn3w%^3tvDDO-*^F@+_?p{Dt7?9GneX7?{dapFm&V;k}4K*@dF7}JWiP-dH--m z$$4W1@mX@PtcC3T*X83N*0^!C=poHyD-FAmJ(D8#6=SWS!^{jQKr>Xv_dcYIa~G#5 z`h58`D6lAPOpunq7aX!zXHTT$7FK)Bp5hT5WB^8sPcT7l(|8Ex?eX&0?T43#j$)MQ zixbg4iJ*!m?6seKy_b#7o>rg}2}5k&!X-GRC{IOe{xLurTl&qiZrY6LlG!_d#Fw+> zkr7UM=hKWpwP(G}Fzk%r&H54`J+A9hpJvOKl8c#!)z3O1=zS~e6*gCS;wkQDF?t=n z#>Ed2N{8#rK=KP<#akFd7cicf^DVl^s19=u`Z5i!O+^pR*O#pZ^$!6`h-=-GkJ#hm z^$TL=SWw%XH|r7&113DG0KtBIx~m_xww)Nvwb8AH7J zXXay=$Bulqy+uf?Ye*V&Q06{)7NHg3W$tw#2Blv)KL@n$N-UM#R8CA_gGIu*#v<)4 zNYNt*CMb7($UWbKhBna=Rs0z~OBDDeEQ5Ahh7)K$$>f9hd=5~`AM?sBj-zLQ1$weR z?WGu5TTx;5Xo(;k0u3bFBU79sLv!GIi&1j(otIn2BwR7~qDRa_BzQvSS4tk)U6=*t zqCu{%kq*>DU9f^<%H6cZw?98ll>@u#7>oaD<_3-ty9IiiKNN|wkLOV00<;OrJZk=B zn#B2UBf)(|(L@JMS|~O9ZP47i<#Tj*R9kQ6B0s-wl_3-*XCyFlX6ERhM4s3-{?cVM& zjh9_`(Er0XE!AwspC6W8VDCT8;d8{?4Va9^BD&M8GP8{6Aeh}&CQ+U zIZvBhZoA%H}jHs4bE7HR5$mCXG$;$~5#ndh_l%9*UIkN@F0wkQD} z>(BDMu}kac+~$aOgpQ$_ij?%O;Y+MefwHuC2YWfk7DKf^j&bOyQtMU5%l^spfxg71 z!}HM2Mf1!pz2bF<-KV8dc5G+B%g)#%c$udF^@O53V*p1F=_mxyr>bIJJMD4~qAYXP zU@_Mm%bIl<`Og8;mRm3p&M*Cg>|Xi+Gu6fvTyDumUQ21qlA?eD^kyK)6PY=Jp??Ta z=v}?)c}KdVD_j&Ee8iCX$4jJsgGsgf>Z$tpJE@eGYR91Y`RZ$Xy8IxJ#6{A}d*$Qr z)LV1vnM&D}8sGz;itDP})m5&&z5w4Y^E`+L z>G?~52%aJ^fAytW-h(y1c=Q8joTHSvzyL{yMSv5^Q+u1Dw2|@a9qLa3x@%ifj1%Zt1dU(n-sb;jxPb*z=t%;28l^?23>n_aCUcc2@@fg{F-v>&(*h zApm=_JGc}Xa#~cNjq#w>s$?#=<8VG*zf|`#2|S{{9*MPX-b7uBbW&+1BVWuTE^d0v zEF(M=P^n^CKZrhbIFU_WpCYW8*EzIf{VuExEiyQ@^IJk%$>RX{2&njpwGbu@Y(EX+J8C21WBd5Ts*0~ z0@vY?_aCg)mk=3!ul<8nd^1F(RLWeUrK8XRSUc7%A!$e=Cjp_FAi(yw2$3LD41Y^C zwQzan{-wb|2%(i1jAoz_?G^Wk$N{df@SqkBi1Q^vmJ)OdHCCe7T)(vMa6=C+tZ{e9 zS~_IOT8(S=Yda{0Y%44bJwDbOl)dKAjSLhp5*BqDS)#5`71<9 z4C?xbP)%TBv|DxN;~cg|WY7C{hKr_c`WWKzNkhf}Wy}*dKa;u}uX7mZw0G8BO6v;o zb!NchCBXR-;DZ6SZFkij%|~y!%v6LW4zeT08x3CnF~sv?nzxD5_B__8g_m*`n_Nff zwnwX_9yQC2d4#v?24+5mcwS7Wiq+VJ6e^oW*$bU1s_E&miLlG5iI4$%m}x#mP|ZI? z81uIGk6%aeP&IP91C<>!*S{!bsG5ONx$dcyS2 zBF?LX`)L{&M;xe5%r!Lrk05QyN#_~N;K_nT-kex&rt+$d`2NR2s9~W=e^~zQ8wU3NF~?g2SNZ4 zikoRP9sds>Vhyj4ny=+K45`dXX7`mHV_qyLhzrqwkAa7=V z+F{H~tj#NU8LC!EijlxVg89FEki0Hy_y=wY7YKL6unH8GYKH;e_76I^R`J|Q^j{EE z41o#YlZV^?kDp^?z^TwW&Gtzf_#9XuOK|G!dZ3f|MW3h z-UT`kOT1c4s3YyIdCNw#r)PV_U1)AD$~Q~*EFewG8;d!ywc7*~opEOH<}Yh3@8{QS zElc~guk=R>ww z8r12${8eC<7%~nq=?p(!KZH3&+0vl-2bT1jEc@cZ%Bd~+BeiNes^;zIxYud!*Pcdt zd;a*>!{w#elq*3lbWp-`3e)fEG0%&ABh^o{ed77&9Ru*XFT*2BhDjp2N~u}1MBrH{Tm7f4be9mZg7I2D(njAgzhO=q2AYckdmDm zFCj{oQSCv==!3NOw+NAC{7^aVM+MNN zu+325z2Ydy=-kGt5Hm#_kuuk0iR^5Dx5oqK^G-ysxmi%4L-!Ff=(=N=(_OIwy4qWeV%mHZc@TK1w|`JlLG_XU z?fBBPyNKB6HnL(~ZB_Hj%Hk1}0J7(P8uyPa5;9UNlKsdw^ce$y`UaWqJl_x-Fp(N| z{oI-gu-67C_W~Q*mhpJ`%sCM3i8AI#OUq_KzSdlu>+Gogw{_i~_!4g=;>wRB2hkCW z&WFp7vQ!4l@|J4Q1YviqKYz87X5!tRZ-0^4|NHuMzqC?r@wl9S1Ny;$n8!(=%5%dw<6lp#>v+EZHR}J1-W7l3PUB7%RS9KFddP!jhSK$W^`ZA3R`}tj3LE#2znZ zOL@RM16VX9=>|aNQvhspG*Gx?45P(_a<2CI8;%^8mvLigE9ARL#2vA2jFwe5g&eU0 zJeTJuT%Nb*k4o}h$}|hx8;Lab-B}NvbzyYMN6cSzq~@aYEvx}**q=WuI+Qwuxbk@v8EWbU# z|0_C(a(R{lM+^K9qwx133$CVU4#@R5GtS6s%$IK6Bha(Y+mR1;rE6sa{vRIR?e!@a zAX}(km~H0={4qD-LVh+x%ee#N5C6mJX4mmwL(lsk0+#pZ4(TIP*N&Guz=0w0xn_AeNImnr9!F!jk@h@W*24(KZ zk?AB~zCDGsMh_aJhI?o9(ln;7Jwy-{tw#rTi?&EIRm?AQ%Y!i+ z%Rx4G$E`Jo^P-rBBr29}tOlqmG7c;iE}j;16AlUDsJWO$I?rC+ob~zoIfX)ZG5?oK z?iq_=l7C8x;A?8&od)HjDc@;*nxosSVZxsSWUJQ~W&m|by>7Jpmw9XH#YEI=sPLzMC9b6A3yH?dH9U;>ub@Kk{8P!?I{hD;_kzD&{SemrVxIN5V%gB({r?3C^462mxSQS>v{uL$K#+my7bRw$nPJ)zpls)25 zXbjljE}WC);_3mUQl2y3Y_CTK_k{6EtFL)oc1{Sr%)RYoKX;3qIlcVF`-n2To8lDT2O!~(O@#(ehuJzCh99<)b)PF1^(1dFyNlOT?zY8+5!uG!) zEY@0qM!yJO<0_Sf zxnbk7!#bU9{cWJy0H7DN?SO^0d^IiGQ|g(Y*}{6r=DaetTRURz5hM|aLC#a;*LQQx zB8hnS(28aueO9COqj$CLrJCol%E;#hLPpSDK@wqzF0iqK*zMqLNu4^a(pId5PC z1z4RUeB*x|GiTD(KHA@TqJNow*4Kpo#E;-_U4%l8aDzhEEO~ytSZy=^y0gj=YCmCc z!8|5!pD(yPdUG}3+Zx(~B-V7h(yR6VYO~ylF(aUTv%Na zbao{;XDF;L6z^73PoBAsH}0rQ1XTAyU~uAU%(V1 ze3~j59s`8lq}z@fG+6qL@}4%~A~l+yfn4eXEbbe}1K&ZdM*{$i&l~U(q8EdTVh=b0 z`S7iLb55Fv>wX5GUxc^e?AOd$OI#PnCKA49NM0|`dL6|OF3A|OhG&{}T;9I_PS-$t&HMsc!+wIiU7p zBrq5Ol3>Z9E&nHZAtNwoNh8P_AH%hL2U$~ETzee2Hn`41k8rNEbVx16_Hy}v&WYNm z-;8p3E1jHC&81-(zh%A>RId0lY-mTc-r9HxP{sukw69+&UDzRh)Ag_Fgwlt%@uI!pR8veHL1y)R zeYt$OKTtrJFO|p2RA_#F6Dm~7dGN2Xd}4j7>M~ZaF$r*{j0MytvGx{WD@~Aybq=Zz zMJ1kz=!Hu**LWCdJmNqjsi_pN7X~;&_|ojRt7iv2LfYLhF^3tF8${TR1cNzuu|YIx zJ%UTAOX)H>In;xN`uh2MfuGt3buEtoQW}yrr&|lDZbYs5xYFA642b39x@yl|{q-RP zro6|}T{3mB*MM!ED++#?4SJ4q)R{&DhBCwEF3oKObexSR0iBlsCE7u3ifXro-GUcn zO`%#@7>BGS=xK-m#(8tYF^t_*c-ORQgJUC@{nIIk67sb#|Iz2%!|bck0@ByTE8WKn z_bwRV7-bzMpmE;r)l)tBu+2igh5Y=>K;eOo5Hptij31ywUJqhn-n`Zjkl4*d$cTr8 zY;Go=10GoRIuqXh%ZTNp_?Z^Bl7>4zA=IsO{Ge4&!1c!Z+^7ryXF!<0;KNpCr@Vd{ z@+vis$nVj!gQ-og&$f4#vSBE@`W;B(G2)q)Yt-RPj0n_vn6eQr6vT6X9kQ%Gk55^K zkh)jIZF0^!L}fP$U$6xg!$S^AJCey{_y?Bv`P-PIjCoON?7IXu%e~k6j9QK)`ZcqR zQ{4%j7tAvx?ndO{M1K420+Kv9*{k_ZmTX;1~(fak8?OG9dcou1h09;!al5+ zrvSZlT}a&ZG+laHt^hdvB4nRia&Cs0vqUxHl%=PN8d1Oxr0(k5bAaq7baLV_3Q<`( zp;B`YiPROlT~=slWeb~M*9f-7zvPTEaohTHfHGy}Msl8XI84fFTCI^vE9Y#0P_~mR zLOmzR>mE;3~tsqv1^7~8zzFzh<+T- zFht&dbAkUIVk^xfzzOyjt=(xS8KhZsQHxqiy9OZKN72X)yF_X>zW^ht11b0=Ldh0< z7OQoX93L)okzl6wY+JGBPx;rhRpauv-5~PAbKHC=#gUSjiyr?3u9zIzKL4H&2fz5z7%g{Xg+i z1ivCzx0aV;@4-Dtb>>#6;65!RFc$GC5w?gUg;2DI78&&Vwt^sqr?TFd}pOJ}V_ zOkcMmGC2rV1~)r4`%8eU1gyWnUfg*Epcn;T+9|Ug0&GUsTtQeFo4(rl)!#_P>&w-F zi04}{0vx%2u;V8GMtHoc+UaS3qHzS^TA2&8sq9HY>IT%-Sq z?Kocnj5-3puZwieBHGLJhbKAo?hIhYZoPOx<`#Y8N}h1E)7M6c4Aes3L<<`egO=qVAX*g}C_LRN+ z4}Y*rTj7~BQDhZnyfCivbjitraJQL3HbA))J;fmANo)zf!<&n32lFp8L^m+y8h56_u>w}bP zzMztReyrvGW}NkDIU`F|)9Q1aM{0R8p3eKZ4`euXmFY2C?kg&ljN6|>bRl|Vr;uOM zSg%&yPm4FY=Tuln0j5a=S~|wKbidn@1M=zev3_HjD<{j3we&=PAVqP@K4@*xQX+;B zUn9?X)$on=`utwcYtsqr+t05by4pNpN*m$0eXXOQ+)MbV!vV}!fOx4he&|=24>4j& zca-W4Es3or_s8UFzrkDA z#~3xfov4aCXyAqR;Ldbo7)gN+kE;1OLR$0vqXz%d7>W-S=7l3SZmL##ab(WqS3Cn* zu4iy?ruqFCB8_M#El(DLQd^ms?x{FPqHP(g1R=#Z)?9qRe<#%)@p(egTcHl0W0Y9O z8V!or4_*O9Z)A&aMnG9J@hjkjC-?f2Sf>D)f&4yRpF~mpua6`yEX7_ZYq>tL_4d>{a$KhA2e_}|gsV*4 zXXan+h#dV(lv?KmP=@Vz1UN)CCm&h8N5Lub3?}QGj_9AS ztlD{%Gv*9D)$V>Tk<GoR@bQ(A)&fh}LM(@Rc3c=wqM$!I__@d6M)05dKaF*e~ zJQ>s2Qk-8p_cO6M3P%N)WvPd7zb)FNOC^;~v3aC(3`;gPZG`GMVB15U!V zIb&`~W>HqEJ&mqUDkDYa!}arSYfSz~c?DfXod{5KXpFYpHg68D3O|DNw)dE~(gcrh zNq-BGx_f3hJ)r%$!Xti6-clnC(Q>4IP(5vIGVnejbm`uZGgm72fb^aN)G{%6*)=yH zg)#)+4v@A3U(zqNTB z(pS8-$t+TL-eT@aDdXiiuiHxmquif)N~D(1S=b@>GH1h{>Cp^o=m zc@Y!bcw7#R$0M#f`3f^XAYU24XO`S{C(b1Od8SqzRI`YDRS5k~6akQnWk53*Wr2RI8} z4qd111{VaSUK%E8jdMSs3AD&Uljx9JC3w%1u-cNp2pAFCfhl`L(Mweuq{0#NnBZ^s za)U<2?+Z-fh>vy=Vq?J?G|BHG-XtEI@7F(tDdF#IJc~$`8ssuAh{ZX{bT5ZOet6E2 z`~Sdgk(`^S%=w>-_yJ`D`OVIKEaD@ixbRyvK=&<$_4yAAm>&g#_>L|mK}I2XN88+i z=g>ImD$Gi=|2AYVN3CyWnvN0pn|?e6&1l%ipkn|N@+2W2oDI%mCB5kM*nS(Zbfd;R zXutoeliG}ixbR$uqqvIHY_Q8$Bcp7h*$MdS54UaXTH%5pV0U%`BK@p zJSHA!fruGDEnH`oNPV>5hAiFC=mBn|fO?eTo?>dDuV&k;%s*d}r(lqzJuvxSM$Fpb z4S#44?A778==RKvYcCtAaZ^Ce2`HM(8r0y+B<%2xrlkFK#8e&a{InHGRu{%G)#^c< z&X=5*4{63+Ym4KPyMmCjhW+~M)w-qz(Ry7axpJ&5p2DA5oJ(@clV(@In^#Q3<1w}A zo10!AC06*$kg1%BOyZW#1UcTDiwUuLI~51u*rZiiPfqQn@EM(Tua=Cz0OxU}OfLce7zf2O~M6qkcJ3G}&=9QJF3 zObXJ}qG@kHv4s6an{S@>?L--kEPtcZjUCIo z{4GRD4u448Eqn3&R3#Vuh)z+;F$P9E214mu{S)h1n5w4H)!ri1MnN9o``7kzqe$y8 zg*BE0il@$Uz(J^O)>w7E#3+5!Qk(tatqbvCt#TIOis~JYm-J+vDs!vtkrHQ8`2H;F zWE{#kINn`bKG5ImN3l5T^*myhey%!I^70YrE;{PkmiGmFbAUW&ogcmV_I#W0fjd@7 zi;1T7&{;q%v@*{9{VvtPw53q#CND?PCc~Ej_F-X%0g5@d{6nif)IQDg~pK zwg!RQlG%f0?xxBEvi|wb7Qb#*(ev|^`9c(;Wmk_qg z%d=bqr`)a&nXNp`jilDs=~fCC8P~FJH|LM5L$3-hfVRjoJ_YE-LqW(>5Ud&0ZDf;H zgdT4ep1KGd7hN5?<<>r(FC4gj(sE1TCBz7%^0+ay)pTY$3@G@WHs|hBgMgS*Cxq(V zfeiLkoxQi`>z7c+f8QhahW+Q1x#G>DZu5z`Y-|}`VOX~qJMR0J(*1?T_a{CN7l2Ep zq6QywPFjd(IYH|qT=Nr!j|}TS%Th8a{_np2F+bd5Ic8Pe_>q5Iy_mak?8t!{mz^ZT z8mRZL2-F689(~+{WQc#6%N-=#?WE@#AIrtRa4l?XPAz9iMb$CCN~@#VB3) z>?pCfqj)Dr98SpM>@SzEX5f5jZmaV%D0<~q@?BLBAArN-F>q_#>sq=%y(HOpQ(08s zL!M6R1(Jpt{Cqo1DDtmhYf|?t;N!zPdzzXDU!eigd#jC^_ee0VXB63=uHS1vAwkV7 zNc%qS_MZt{9EpJC6&JZJuq$}~BUq_=9iIx=+*yjM-Q-JZ0TpN|zV0qoj zQr#)fbY{Q3eCD2yzT`K~*UdowIYbU6H6Tx0QV;auSg#&@0OvtA->9qH?e**_*=bzc z+>EK93{pkE2IzST7Y(R+j7SZloruPx1&+7U^B4%7?aXfG>QNk~m$YW46II98$53fh z8o=w4WGu3Xr(_U@8OSD@ciMj-m4}7o9^nZy1CtNFz6Z*|`A!Dt_GQ`wTcmWxJEdH- z#)rPtUWxBOvYj=V8tfgY^q9+|;Uhe_gy~1Y%l|}a@Bo<8!*vBi9RpI#R z^hbB2`EyKW=U%cP6(~rKmjG$|5M2!8fTGZqDs_(@_Fx;qErXB%1>1!tHhLU%rQHH` zDAsn{TZE!7?8iqxB3jk+Vmcr+Ae}J`j437@hIMNu&TGyaq!Vg#>&x{+fjOZhXAT!S z#QJ=F0}mC!Jgr%%C*It3l44WiPAOs z@LPmrS+~1$5#sCY4+IO(pVLh4(MF7sw1As)+l%dtRpz$ZZ&j(M2<5pp2>NXr9{tE& zI%d6PHV&9D-|szCy>#C(IC3UvN)8!W3Z(w_nAiA54Q!&g311~uZwyz)Qpa}-b+!)vf~Br@ep{qXbTwS zb0}Cw?vZnxOJvptH$MjaYi1N^GI51BXC{IUSaX>=uJJboxFb(D&&H($E_7&G|N18w z#kmV`1zxauD8Ex>#Dhu!YVsJG6%%f~-N?$Uj{z!$K8;pSNStBFaKsIPiq{f>$m5H2 zEO9gfnH58%^vcu+?6Jog=^;QepueQT%97j-9OM2O0<)u$*XpSsksOh601br2hN!>z zaQ!gdEl6GHSrKg#Lelu(6UD9Mp9~&akBa9^`ektO;?)f5Bv1Co%a;mDVHek1-YhgR zI$+c|?w=*6xRG2ykKN*MxK>HP_|M?|>GE4C13-RV_s@t!6DvItI!Y#mF@%OG3>irUAq3>iUi-2pMCZd^M%tK8~&p7sa-skh>d!aQ( z^u&BmVrSPsvziBXO$bc}bS4#TL;%ko9?YQ>M@3_F(7H^6#L` z;5cR9<3K@>A@20xn8_Q%ozKs-<%MkP$Nn4b%Oe&0=F^?!%gGkw;wRtJj3_n;&IL2r zH4PK0eU9fpC6$-yxXbUKES)GUGud#=cYw?kUJhA4w!`YhaUr#>0ivaHuFqGGV8yIr zTgyp`*X*NynV2oiJo1QF`9Gq}s9w)V4Z9eA&!gh`;-x1GtA&u@HYZ^~ANUdFh#EkQ z-yD*ld$Bd@+H$-!R)F>A>tD~;59RFp?`+C*cZ(?GWpi9JIf|l_yc4qN`T8X)``-cB z4|L;Ezk6hVZhZVk2;lG=cwm6>+C9;lTH8y2ys+2$m(fakD3A((*U=Y>F2q26kO9(T z3s&$X^|uuAeeW+3$|&QEe}tkdnmyg*j2L14h#JBym`qe=vPsVi_i03Vae-l*`P}gm zptNO6bgxH8`4(!u;vf>PBghSFXjA(QZ=HLHp24=gTy@-=xZ}d?W$q_YhMCLa`Z60T z;U2jC-B5Og@#TOdwNfMCNo+6orQX_zdVMDZ&x_|&63-VM;jkAUX^HhP_Zd9d`QGwS zI7g(iB@Rx&J6rw{Y1^Xh#F`C5O&@KI`38*Z+!RTFH;0GS6O}j)Fy_-sdx;RRq&7P6 z9Xfhc0eR7U;OUYtn$hxu(@bBY9}pQXvhSCU;rSe(aIhbM&*LhEsmw-qXb9!r2nKr| z{2D)V0U#kQQB8lkXib>0COO>`16VT0`QaUMOd{vPV=a!prQD!>4m>t5sx$r=V0#LH*$p{3STqLtL;{!O z;vV5}Ycm$bgxBVhTwX7qVKH~_A6KpY-`76~J%Ji z#RPGdD=S;3CS!b#P%QGl{gfl|`bk5MkkGE^^ z-h7d(os(>e4oYf0vnS5TTR_DN9`$2@fYoO4L)9FPg9T+YjW?VvYPQ*1VUA9!AW~_@ z&_}@4<~lI~R!qM{xQeK#*r0Yc-f8hSB!vaw_|jhS&pscCKLcR=`_Ald585kA@@J%j*DGX<=O9JtT-oob z|MxSBKFk1r%3p?&VWzF3*j0ZRk}w z1TaQt*$N6WYcD(i&OGE*+f|eXQ(f#PUbM^`3Vkjw!Ht_wZ*IWJ%4J>OLx;3d4(OP< zgsGUHf96i5_Yq#kj*j_!`R=m~l)%j-%a5G*BT|3}O5-T$p=kIpx6tmU=!ko{9nY5? zC2K=_f722;uga^ww5`nEvcW4-G|K%D<)cruNuPMgHn=2`r zmU&^fc^L!RkwXs)l5#JGUQJCOF23IUYn|&uH3ZX{lxp65DS%0lwj|kAI0X6;av?-f z3?j}fx-&~<&<_zpBB6e8=2&Vy#UeArmj2rjl2Acg<~ouG&3AC^VGNo4+6lFXUmj~g z&F62S*4b#4Zch)u<<(Gg-<;nm6KidoSr~g|pj5}(H^{<)bX~ul=`#!aP&SUX@j@?m zk35#+0$Vi-V793i%f6R|zh(`MPfw(8tyFBlBRujRlv^4-^Oi-bU(gz>Bc2!dXFWpO0#x_SJExg6z*#nWfgOOG{; zPPu^=mZ8KR#GEw?5ICqG^vK<8BTkM78OP_#=iu;uc(dwfQik>^pAtsfsoCxf3;WSy z-9iIz=C|iHp08g@7iI-N2YOO*bs|IErA*n96mEg~m{@Rh`6skAY?W2R{Co~@mE?@l z6wuw>E-8A+0uP;qrC@ds$yAv4gjKPBX=vc!0~q%9eEm9qft^9DPnMWx1%URj^>ZZD zP}|`?v6dsV)o?xWon9`RQ+LpZ#HN?6UFoE5d|P-DdF2bP%q2VGEe2B|7W4J+q)8Sp znX^B}C^Z$sGcP1X+oZUVo_+spDx+ELgy1IOd(yquzq4%pKU7ZfpN_BSK*TJ(hW zeED8d#jvTr?ohV+rK%7DLJO_Y)m-yBUjn2-Id_%0A)SH1pG)9%$Ms*X_1fz#u6IdJgF*yWCgU_8 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

KLvw?g}%7cBm{|2o@ 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

F~2aF!sdXaVGx~{txAgxm-q8sT3a4_~2_ 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ˑ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

S$xwnVuHFEd!yMLh|w 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 getDashboards(Config config, SimWrapper simWrapper) { TripDashboard trips = new TripDashboard("mode_share_ref.csv", "mode_share_per_dist_ref.csv", "mode_users_ref.csv"); - return List.of(trips); + return List.of(trips, + new TravelTimeComparisonDashboard(ApplicationUtils.resolve(config.getContext(), "leipzig-v" + RunLeipzigScenario.VERSION + "-routes-ref.csv.gz")) + ); } } From ca82018cd1968ae9bc8a534cf523eedb2fff930b Mon Sep 17 00:00:00 2001 From: simei94 Date: Thu, 21 Sep 2023 15:51:09 +0200 Subject: [PATCH 091/106] add enum case for teleported bike --- .../org/matsim/run/RunLeipzigScenario.java | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index 2f18cc4a..76daa053 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -48,10 +48,7 @@ import javax.annotation.Nullable; import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Set; +import java.util.*; /** * Run the Leipzig scenario. All the upstream stuff (network generation, initial demand generation) is in the Makefile. @@ -236,7 +233,32 @@ protected Config prepareConfig(Config config) { BicycleConfigGroup bikeConfigGroup = ConfigUtils.addOrGetModule(config, BicycleConfigGroup.class); bikeConfigGroup.setBicycleMode(TransportMode.bike); } - //TODO we may have to implement another case for bikeTeleportedStandardMATSim + case bikeTeleportedStandardMatsim -> { + + log.info("Simulating with bikes teleported"); + PlansCalcRouteConfigGroup plansCalcRouteConfigGroup = ConfigUtils.addOrGetModule(config, PlansCalcRouteConfigGroup.class); + + if (plansCalcRouteConfigGroup.getNetworkModes().contains(TransportMode.bike)) { + + Collection networkModes = Sets.newHashSet(); + + for (String mode : plansCalcRouteConfigGroup.getNetworkModes()) { + if (!mode.equals(TransportMode.bike)) { + networkModes.add(mode); + } + } + plansCalcRouteConfigGroup.setNetworkModes(networkModes); + } + + if (!plansCalcRouteConfigGroup.getTeleportedModeParams().containsKey(TransportMode.bike)) { + PlansCalcRouteConfigGroup.TeleportedModeParams teleportedModeParams = new PlansCalcRouteConfigGroup.TeleportedModeParams(); + teleportedModeParams.setMode(TransportMode.bike); + teleportedModeParams.setBeelineDistanceFactor(1.3); + teleportedModeParams.setTeleportedModeSpeed(3.1388889); + plansCalcRouteConfigGroup.addTeleportedModeParams(teleportedModeParams); + } + } + default -> throw new IllegalStateException("Unexpected value: " + (bike)); } @@ -319,5 +341,5 @@ public void install() { /** * Defines how bicycles are scored. */ - enum BicycleHandling {onNetworkWithStandardMatsim, onNetworkWithBicycleContrib} + enum BicycleHandling {onNetworkWithStandardMatsim, onNetworkWithBicycleContrib, bikeTeleportedStandardMatsim} } From 50d35e5ed2bed722d80a0a1c8c7e03f9fec9e5ff Mon Sep 17 00:00:00 2001 From: rakow Date: Mon, 9 Oct 2023 13:46:54 +0200 Subject: [PATCH 092/106] python script to create reference data --- src/main/python/create_ref.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/main/python/create_ref.py diff --git a/src/main/python/create_ref.py b/src/main/python/create_ref.py new file mode 100644 index 00000000..e43e3625 --- /dev/null +++ b/src/main/python/create_ref.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from matsim.scenariogen.data import run_create_ref_data + + +def person_filter(df): + """ Default person filter for reference data. """ + return df[df.present_on_day & (df.reporting_day <= 4)] + + +if __name__ == "__main__": + person, trips, share = run_create_ref_data.create("../../../../shared-svn/projects/NaMAV/data/SrV_2018", + person_filter) + + print(share) From d0e251ed10b54d8777f93179fe569d5cd9e4b40d Mon Sep 17 00:00:00 2001 From: rakow Date: Mon, 16 Oct 2023 16:03:29 +0200 Subject: [PATCH 093/106] updated mode shares and scripts so that R and python is consistent --- pom.xml | 4 ++-- src/main/R/Analysis/srv.R | 8 ++++---- src/main/python/calibrate.py | 10 +++++----- src/main/python/create_ref.py | 15 ++++++++++++--- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index 0adbfd91..df9b0d99 100644 --- a/pom.xml +++ b/pom.xml @@ -4,8 +4,8 @@ org.matsim matsim-all - 16.0-PR2745 - + + 16.0-SNAPSHOT 4.0.0 diff --git a/src/main/R/Analysis/srv.R b/src/main/R/Analysis/srv.R index 743acc85..7da008dd 100644 --- a/src/main/R/Analysis/srv.R +++ b/src/main/R/Analysis/srv.R @@ -7,12 +7,12 @@ library(ggsci) library(sf) -# setwd("C:/Users/chris/Development/matsim-scenarios/matsim-leipzig/src/main/R") +# setwd("~/Development/matsim-scenarios/matsim-leipzig/src/main/R") # Person data from srv ############################ -persons <- read_delim("../../../../../shared-svn/projects/NaMAV/data/SrV_2018/SrV2018_Einzeldaten_Leipzig_LE_SciUse_P2018.csv", delim = ";", +persons <- read_delim("../../../../shared-svn/projects/NaMAV/data/SrV_2018/SrV2018_Einzeldaten_Leipzig_LE_SciUse_P2018.csv", delim = ";", locale = locale(decimal_mark = ",")) %>% filter(ST_CODE_NAME=="Leipzig") %>% filter(STICHTAG_WTAG <= 5) %>% @@ -29,7 +29,7 @@ tt <- per_day * 600000 # Trip data from srV ############################# -trips <- read_delim("../../../../../shared-svn/projects/NaMAV/data/SrV_2018/SrV2018_Einzeldaten_Leipzig_LE_SciUse_W2018.csv", delim = ";", +trips <- read_delim("../../../../shared-svn/projects/NaMAV/data/SrV_2018/SrV2018_Einzeldaten_Leipzig_LE_SciUse_W2018.csv", delim = ";", col_types = cols( V_ZIEL_LAND = col_character(), GIS_LAENGE = col_double(), @@ -52,7 +52,7 @@ relevant <- trips %>% filter(ST_CODE_NAME=="Leipzig") %>% filter(E_HVM < 70) %>% filter(V_VM_LAENG < 70) %>% - filter(GIS_LAENGE >= 0 & GIS_LAENGE_GUELTIG == -1) %>% + filter(GIS_LAENGE >= 0 & E_DAUER > 0) %>% filter(STICHTAG_WTAG <= 5) %>% mutate(dist_group = cut(GIS_LAENGE, breaks=breaks, labels=levels, right=T)) diff --git a/src/main/python/calibrate.py b/src/main/python/calibrate.py index 187c1214..b94d25ac 100644 --- a/src/main/python/calibrate.py +++ b/src/main/python/calibrate.py @@ -17,11 +17,11 @@ # Target from SrV target = { - "walk": 0.2278, - "bike": 0.2179, - "pt": 0.1669, - "car": 0.2966, - "ride": 0.0908 + "walk": 0.272564, + "bike": 0.190819, + "pt": 0.170200, + "car": 0.284754, + "ride": 0.081662 } city = gpd.read_file("../scenarios/input/leipzig-utm32n/leipzig-utm32n.shp") diff --git a/src/main/python/create_ref.py b/src/main/python/create_ref.py index e43e3625..cfbcaccb 100644 --- a/src/main/python/create_ref.py +++ b/src/main/python/create_ref.py @@ -1,16 +1,25 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from matsim.scenariogen.data import run_create_ref_data +from matsim.scenariogen.data import TripMode, run_create_ref_data def person_filter(df): """ Default person filter for reference data. """ - return df[df.present_on_day & (df.reporting_day <= 4)] + return df[df.reporting_day <= 5] + + +def trip_filter(df): + # Motorcycles are counted as cars + df.loc[df.main_mode == TripMode.MOTORCYCLE, "main_mode"] = TripMode.CAR + + # Other modes are ignored in the total share + return df[df.main_mode != "other"] if __name__ == "__main__": person, trips, share = run_create_ref_data.create("../../../../shared-svn/projects/NaMAV/data/SrV_2018", - person_filter) + person_filter, trip_filter, + run_create_ref_data.InvalidHandling.REMOVE_TRIPS) print(share) From e3fbc12a1f6609960e9256088c2f74b77cd6e3ab Mon Sep 17 00:00:00 2001 From: rakow Date: Mon, 16 Oct 2023 16:18:10 +0200 Subject: [PATCH 094/106] switch back to specific PR version --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index df9b0d99..0adbfd91 100644 --- a/pom.xml +++ b/pom.xml @@ -4,8 +4,8 @@ org.matsim matsim-all - - 16.0-SNAPSHOT + 16.0-PR2745 + 4.0.0 From 66ff9342760694252d6019a0a2552c57c8f81281 Mon Sep 17 00:00:00 2001 From: rakow Date: Mon, 23 Oct 2023 13:47:39 +0200 Subject: [PATCH 095/106] final mode share update --- src/main/R/Analysis/srv.R | 4 +- src/main/python/calibrate.py | 10 ++-- src/main/python/create_ref.py | 5 +- .../resources/mode_share_per_dist_ref.csv | 58 +++++++++---------- src/main/resources/mode_share_ref.csv | 58 +++++++++---------- src/main/resources/mode_users_ref.csv | 10 ++-- 6 files changed, 73 insertions(+), 72 deletions(-) diff --git a/src/main/R/Analysis/srv.R b/src/main/R/Analysis/srv.R index 682c4e9f..2e427457 100644 --- a/src/main/R/Analysis/srv.R +++ b/src/main/R/Analysis/srv.R @@ -51,10 +51,10 @@ breaks = c(0, 1, 3, 5, 10, 20, Inf) relevant <- trips %>% filter(ST_CODE_NAME=="Leipzig") %>% filter(E_HVM < 70) %>% - filter(V_VM_LAENG < 70) %>% filter(GIS_LAENGE >= 0 & E_DAUER > 0) %>% + filter(GIS_LAENGE < 100) %>% filter(STICHTAG_WTAG <= 5) %>% - mutate(dist_group = cut(GIS_LAENGE, breaks=breaks, labels=levels, right=T)) + mutate(dist_group = cut(GIS_LAENGE, breaks=breaks, labels=levels, right=F)) matched <- relevant %>% left_join(lookup, by=c("E_HVM"="category")) diff --git a/src/main/python/calibrate.py b/src/main/python/calibrate.py index b94d25ac..a886d4ef 100644 --- a/src/main/python/calibrate.py +++ b/src/main/python/calibrate.py @@ -17,11 +17,11 @@ # Target from SrV target = { - "walk": 0.272564, - "bike": 0.190819, - "pt": 0.170200, - "car": 0.284754, - "ride": 0.081662 + "walk": 0.270794, + "bike": 0.196723, + "pt": 0.166204, + "car": 0.286468, + "ride": 0.079811 } city = gpd.read_file("../scenarios/input/leipzig-utm32n/leipzig-utm32n.shp") diff --git a/src/main/python/create_ref.py b/src/main/python/create_ref.py index cfbcaccb..f14677da 100644 --- a/src/main/python/create_ref.py +++ b/src/main/python/create_ref.py @@ -14,12 +14,13 @@ def trip_filter(df): df.loc[df.main_mode == TripMode.MOTORCYCLE, "main_mode"] = TripMode.CAR # Other modes are ignored in the total share - return df[df.main_mode != "other"] + # Long distance mode are ignored as well + return df[(df.main_mode != "other") & (df.gis_length < 100)] if __name__ == "__main__": person, trips, share = run_create_ref_data.create("../../../../shared-svn/projects/NaMAV/data/SrV_2018", person_filter, trip_filter, - run_create_ref_data.InvalidHandling.REMOVE_TRIPS) + run_create_ref_data.InvalidHandling.REMOVE_PERSONS) print(share) diff --git a/src/main/resources/mode_share_per_dist_ref.csv b/src/main/resources/mode_share_per_dist_ref.csv index 50d37348..7647d343 100644 --- a/src/main/resources/mode_share_per_dist_ref.csv +++ b/src/main/resources/mode_share_per_dist_ref.csv @@ -1,31 +1,31 @@ dist_group,main_mode,mean_dist,share -0 - 1000,bike,647.1320211664759,0.18648889960816117 -0 - 1000,car,715.5733490038705,0.05234787770685268 -0 - 1000,pt,802.3537970116731,0.011283809577320864 -0 - 1000,ride,729.0617251936227,0.030663712677327124 -0 - 1000,walk,499.1497285817478,0.7192157004303381 -1000 - 2000,bike,1410.8376133243098,0.32720226728480256 -1000 - 2000,car,1500.735907403122,0.16968403169776486 -1000 - 2000,pt,1533.2653903592868,0.11751538922105827 -1000 - 2000,ride,1487.1708306254736,0.09512585828066737 -1000 - 2000,walk,1302.3249718162533,0.2904724535157069 -2000 - 5000,bike,3202.914928652807,0.32502884429891704 -2000 - 5000,car,3405.7134444946573,0.2673536802459596 -2000 - 5000,pt,3498.796011328994,0.25034686219019325 -2000 - 5000,ride,3493.776275422576,0.11424498215353364 -2000 - 5000,walk,2618.6861353776153,0.043025631111396456 -5000 - 10000,bike,6488.004797551106,0.13390529274426183 -5000 - 10000,car,7286.224192866411,0.46657794968496363 -5000 - 10000,pt,6911.388240153839,0.2673883884644953 -5000 - 10000,ride,7091.697700844432,0.12398400299689334 -5000 - 10000,walk,8512.500292899185,0.008144366109385872 -10000 - 20000,bike,11870.106118880658,0.05597065369100367 -10000 - 20000,car,13314.789717119513,0.6443810864079137 -10000 - 20000,pt,13268.066606129496,0.21199175918221977 -10000 - 20000,ride,12510.881840420803,0.08765650071886284 -10000 - 20000,walk,,0.0 -20000+,bike,39557.16129260366,0.008270651122581562 -20000+,car,45145.502240349495,0.7529385110988895 -20000+,pt,39740.679248392946,0.1367767540286853 -20000+,ride,38947.640630839625,0.10201408374984364 +0 - 1000,bike,625.6709239066038,0.1498342300634102 +0 - 1000,car,673.6384671092165,0.07636039086702505 +0 - 1000,pt,670.1827034315171,0.014101280024101288 +0 - 1000,ride,707.110835224723,0.024163955654332738 +0 - 1000,walk,476.61610403606227,0.7355401433911307 +1000 - 2000,bike,1413.529565635515,0.2976757821440052 +1000 - 2000,car,1516.4289451983675,0.18314890770726613 +1000 - 2000,pt,1518.669203788909,0.13345080823082492 +1000 - 2000,ride,1521.2388298001295,0.0799250711582756 +1000 - 2000,walk,1315.3774223589255,0.30579943075962823 +2000 - 5000,bike,3225.5356519111447,0.30772535018665886 +2000 - 5000,car,3428.1135956590256,0.2767741787199647 +2000 - 5000,pt,3500.8253179738435,0.2595885822887284 +2000 - 5000,ride,3486.3862346869414,0.10835558269878572 +2000 - 5000,walk,2801.165158479957,0.04755630610586222 +5000 - 10000,bike,6456.922399742452,0.115790702653595 +5000 - 10000,car,7169.54499240835,0.47486333861330693 +5000 - 10000,pt,6940.885272741965,0.2842637177313413 +5000 - 10000,ride,7038.083385646529,0.11736304734076954 +5000 - 10000,walk,8068.343472517595,0.00771919366098719 +10000 - 20000,bike,11501.183789143368,0.05251179348646703 +10000 - 20000,car,13226.472750426878,0.6472724582139662 +10000 - 20000,pt,12902.874110830719,0.2016743449840846 +10000 - 20000,ride,13106.119597514538,0.09771615235870768 +10000 - 20000,walk,13810.0,0.0008252509567746149 +20000+,bike,26308.486401621365,0.00416730209981478 +20000+,car,45398.01656256677,0.7394682209830556 +20000+,pt,49455.60522417894,0.1698405150810158 +20000+,ride,39425.596734908366,0.08652396183611383 20000+,walk,,0.0 diff --git a/src/main/resources/mode_share_ref.csv b/src/main/resources/mode_share_ref.csv index 7f9bdd64..febdad6e 100644 --- a/src/main/resources/mode_share_ref.csv +++ b/src/main/resources/mode_share_ref.csv @@ -1,31 +1,31 @@ dist_group,main_mode,mean_dist,share -0 - 1000,bike,647.1320211664759,0.04360576137790145 -0 - 1000,car,715.5733490038705,0.012240240940456974 -0 - 1000,pt,802.3537970116731,0.002638436437215161 -0 - 1000,ride,729.0617251936227,0.00716994170043107 -0 - 1000,walk,499.1497285817478,0.16817058966030335 -1000 - 2000,bike,1410.8376133243098,0.05161256124243341 -1000 - 2000,car,1500.735907403122,0.026765790929684893 -1000 - 2000,pt,1533.2653903592868,0.018536760987114285 -1000 - 2000,ride,1487.1708306254736,0.015005058574293151 -1000 - 2000,walk,1302.3249718162533,0.04581883683363964 -2000 - 5000,bike,3202.914928652807,0.09292657641457054 -2000 - 5000,car,3405.7134444946573,0.07643709976165831 -2000 - 5000,pt,3498.796011328994,0.07157480705949293 -2000 - 5000,ride,3493.776275422576,0.032662932075985494 -2000 - 5000,walk,2618.6861353776153,0.012301137783270974 -5000 - 10000,bike,6488.004797551106,0.025021931407638667 -5000 - 10000,car,7286.224192866411,0.08718610903327519 -5000 - 10000,pt,6911.388240153839,0.04996496985474348 -5000 - 10000,ride,7091.697700844432,0.023168010427770593 -5000 - 10000,walk,8512.500292899185,0.0015218798747332046 -10000 - 20000,bike,11870.106118880658,0.0042008516746133645 -10000 - 20000,car,13314.789717119513,0.04836372612101472 -10000 - 20000,pt,13268.066606129496,0.015910944000784487 -10000 - 20000,ride,12510.881840420803,0.0065790183525187155 -10000 - 20000,walk,,0.0 -20000+,bike,39557.16129260366,0.000501334015678044 -20000+,car,45145.502240349495,0.04564014147534626 -20000+,pt,39740.679248392946,0.008290863480069695 -20000+,ride,38947.640630839625,0.006183688503361974 +0 - 1000,bike,625.6709239066038,0.042427666982270844 +0 - 1000,car,673.6384671092165,0.021622517317779033 +0 - 1000,pt,670.1827034315171,0.003992975521235183 +0 - 1000,ride,707.110835224723,0.006842363477574613 +0 - 1000,walk,476.61610403606227,0.20827852382385292 +1000 - 2000,bike,1413.529565635515,0.046904293446247414 +1000 - 2000,car,1516.4289451983675,0.028858478340389557 +1000 - 2000,pt,1518.669203788909,0.021027628867938655 +1000 - 2000,ride,1521.2388298001295,0.012593664705670988 +1000 - 2000,walk,1315.3774223589255,0.04818432367167251 +2000 - 5000,bike,3225.5356519111447,0.08381183999633887 +2000 - 5000,car,3428.1135956590256,0.07538200271094032 +2000 - 5000,pt,3500.8253179738435,0.07070134686811573 +2000 - 5000,ride,3486.3862346869414,0.02951164327005261 +2000 - 5000,walk,2801.165158479957,0.012952399000419555 +5000 - 10000,bike,6456.922399742452,0.019847375312634377 +5000 - 10000,car,7169.54499240835,0.081395057527758 +5000 - 10000,pt,6940.885272741965,0.04872488519615632 +5000 - 10000,ride,7038.083385646529,0.020116886719094527 +5000 - 10000,walk,8068.343472517595,0.0013231263839797098 +10000 - 20000,bike,11501.183789143368,0.003530790840523247 +10000 - 20000,car,13226.472750426878,0.0435213409226598 +10000 - 20000,pt,12902.874110830719,0.013560190630735951 +10000 - 20000,ride,13106.119597514538,0.006570243993060608 +10000 - 20000,walk,13810.0,5.548826893645858e-05 +20000+,bike,26308.486401621365,0.00020112627705902024 +20000+,car,45398.01656256677,0.035688914968844986 +20000+,pt,49455.60522417894,0.008197003642608329 +20000+,ride,39425.596734908366,0.004175901315450057 20000+,walk,,0.0 diff --git a/src/main/resources/mode_users_ref.csv b/src/main/resources/mode_users_ref.csv index 5362f73a..b9fd3cfc 100644 --- a/src/main/resources/mode_users_ref.csv +++ b/src/main/resources/mode_users_ref.csv @@ -1,6 +1,6 @@ main_mode,user -walk,0.34795408095941294 -car,0.3507092997070994 -ride,0.16356884852805617 -bike,0.26412581692795317 -pt,0.2773855049775048 +car,0.3114662795761375 +pt,0.2730115678797592 +ride,0.1377769897765719 +walk,0.3812113445425378 +bike,0.22968769172366532 From e8a1e059e681436074530a4eefcf7de565f73197 Mon Sep 17 00:00:00 2001 From: rakow Date: Mon, 23 Oct 2023 14:03:22 +0200 Subject: [PATCH 096/106] update initial asc --- src/main/python/calibrate.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/python/calibrate.py b/src/main/python/calibrate.py index a886d4ef..ca498859 100644 --- a/src/main/python/calibrate.py +++ b/src/main/python/calibrate.py @@ -9,10 +9,10 @@ modes = ["walk", "car", "ride", "pt", "bike"] fixed_mode = "walk" initial = { - "bike": -0.56, - "pt": 0.01, - "car": -0.799, - "ride": -1.30 + "bike": 0.30, + "pt": -0.16, + "car": 0.39, + "ride": -1.42 } # Target from SrV From a401753f5556a8c569279973b97d34af30d32887 Mon Sep 17 00:00:00 2001 From: rakow Date: Mon, 23 Oct 2023 14:14:49 +0200 Subject: [PATCH 097/106] update build scripts --- .github/workflows/build.yaml | 2 +- .github/workflows/publish.yaml | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 9fa228d5..eca85b37 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -103,4 +103,4 @@ jobs: path: staging env: - MAVEN_OPTS: -Xmx10G \ No newline at end of file + MAVEN_OPTS: -Xmx512m \ No newline at end of file diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 4ef94b1d..55ba46d1 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -17,4 +17,4 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} env: - MAVEN_OPTS: -Xmx10G \ No newline at end of file + MAVEN_OPTS: -Xmx512m \ No newline at end of file diff --git a/pom.xml b/pom.xml index 0adbfd91..33211c13 100644 --- a/pom.xml +++ b/pom.xml @@ -278,7 +278,7 @@ 1 false - @{argLine} -Xmx9500m -Djava.awt.headless=true -Dmatsim.preferLocalDtds=true + @{argLine} -Xmx6500m -Djava.awt.headless=true -Dmatsim.preferLocalDtds=true From 849f0149e5768df68d29c0462c9fe3a8a2af1aee Mon Sep 17 00:00:00 2001 From: rakow Date: Mon, 23 Oct 2023 14:41:13 +0200 Subject: [PATCH 098/106] update config, small cleanup --- input/v1.2/leipzig-v1.2-25pct.config.xml | 55 ++-- .../leipzig-v1.2-25pct_prices2021.config.xml | 311 ------------------ input/v1.2/leipzig-v1.2-drt.config.xml | 114 +++++++ .../org/matsim/run/RunLeipzigScenario.java | 5 +- 4 files changed, 143 insertions(+), 342 deletions(-) delete mode 100644 input/v1.2/leipzig-v1.2-25pct_prices2021.config.xml create mode 100644 input/v1.2/leipzig-v1.2-drt.config.xml diff --git a/input/v1.2/leipzig-v1.2-25pct.config.xml b/input/v1.2/leipzig-v1.2-25pct.config.xml index 3125f7bd..db3dd3de 100644 --- a/input/v1.2/leipzig-v1.2-25pct.config.xml +++ b/input/v1.2/leipzig-v1.2-25pct.config.xml @@ -45,9 +45,9 @@ - - - + + + @@ -61,12 +61,11 @@ - - - - - - + + + + + @@ -75,24 +74,24 @@ - + - - - - - - - - - - - - - - + + + + + + + + + + + + + + @@ -115,7 +114,7 @@ - + @@ -150,8 +149,8 @@ - - + + @@ -162,7 +161,7 @@ - + diff --git a/input/v1.2/leipzig-v1.2-25pct_prices2021.config.xml b/input/v1.2/leipzig-v1.2-25pct_prices2021.config.xml deleted file mode 100644 index 0bfe9fcc..00000000 --- a/input/v1.2/leipzig-v1.2-25pct_prices2021.config.xml +++ /dev/null @@ -1,311 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/input/v1.2/leipzig-v1.2-drt.config.xml b/input/v1.2/leipzig-v1.2-drt.config.xml new file mode 100644 index 00000000..f003057a --- /dev/null +++ b/input/v1.2/leipzig-v1.2-drt.config.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index 76daa053..f3583be0 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -203,7 +203,7 @@ protected Config prepareConfig(Config config) { // We need to use coordinates only, otherwise subtour constraints will be violated by the parking re-routing, because it may change link/facility ids config.facilities().setFacilitiesSource(FacilitiesConfigGroup.FacilitiesSource.none); - switch ((bike)) { + switch (bike) { case onNetworkWithStandardMatsim -> { // bike is routed on the network per the xml config. @@ -259,7 +259,7 @@ protected Config prepareConfig(Config config) { } } - default -> throw new IllegalStateException("Unexpected value: " + (bike)); + default -> throw new IllegalStateException("Unexpected value: " + bike); } if (networkOpt.hasParkingCostArea()) { @@ -296,7 +296,6 @@ protected void prepareControler(Controler controler) { @Override public void install() { install(new LeipzigPtFareModule()); - install(new SwissRailRaptorModule()); addTravelTimeBinding(TransportMode.ride).to(networkTravelTime()); addTravelDisutilityFactoryBinding(TransportMode.ride).to(carTravelDisutilityFactoryKey()); From 0263af8cda9f8c8f6ca95655dda307b3c8d68e87 Mon Sep 17 00:00:00 2001 From: rakow Date: Mon, 23 Oct 2023 14:41:58 +0200 Subject: [PATCH 099/106] remove import --- src/main/java/org/matsim/run/RunLeipzigScenario.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index f3583be0..061684a2 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -1,6 +1,5 @@ package org.matsim.run; -import ch.sbb.matsim.routing.pt.raptor.SwissRailRaptorModule; import com.google.common.collect.Sets; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; From e7519ad8141796a4ce22ec1ac0ffd356e680c5d1 Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 31 Oct 2023 16:42:06 +0100 Subject: [PATCH 100/106] update calibrated parameters --- input/v1.2/leipzig-v1.2-25pct.config.xml | 56 +++++++++++++----------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/input/v1.2/leipzig-v1.2-25pct.config.xml b/input/v1.2/leipzig-v1.2-25pct.config.xml index db3dd3de..f878de83 100644 --- a/input/v1.2/leipzig-v1.2-25pct.config.xml +++ b/input/v1.2/leipzig-v1.2-25pct.config.xml @@ -19,7 +19,7 @@ - + @@ -153,44 +153,50 @@ - - - - - - + + + + + + - - - - - - - + + + + + + + - - - - - + + + + + - - - + + + + + - + + + + + - - + + From e484be76962a3d6f844a088b60af2c2a559971a7 Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Fri, 3 Nov 2023 12:29:15 +0100 Subject: [PATCH 101/106] update values --- input/v1.2/leipzig-v1.2-25pct.config.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/input/v1.2/leipzig-v1.2-25pct.config.xml b/input/v1.2/leipzig-v1.2-25pct.config.xml index f878de83..ddd5b548 100644 --- a/input/v1.2/leipzig-v1.2-25pct.config.xml +++ b/input/v1.2/leipzig-v1.2-25pct.config.xml @@ -153,14 +153,14 @@ - + - + @@ -168,7 +168,7 @@ - + @@ -176,7 +176,7 @@ - + From f4b8b49e38f818d0609388c1c6c7b03e88fa3445 Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Sun, 5 Nov 2023 01:13:21 +0100 Subject: [PATCH 102/106] add monetaryDistanceRate for ride --- input/v1.2/leipzig-v1.2-25pct.config.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/input/v1.2/leipzig-v1.2-25pct.config.xml b/input/v1.2/leipzig-v1.2-25pct.config.xml index ddd5b548..6c556cd0 100644 --- a/input/v1.2/leipzig-v1.2-25pct.config.xml +++ b/input/v1.2/leipzig-v1.2-25pct.config.xml @@ -165,6 +165,7 @@ + From 47b66176ee69a78bc272adb4a4971fde165f2db0 Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 21 Nov 2023 09:53:30 +0100 Subject: [PATCH 103/106] use snz activity types for parking logic and tests --- .../run/LeipzigRouterPlanAlgorithm.java | 8 +- .../org/matsim/run/ChessboardParkingTest.java | 347 +++++++++--------- .../matsim/run/RunModalExperimentTest.java | 8 - 3 files changed, 180 insertions(+), 183 deletions(-) diff --git a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java index b6272430..c3bd8ee1 100644 --- a/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java +++ b/src/main/java/org/matsim/run/LeipzigRouterPlanAlgorithm.java @@ -6,6 +6,7 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.population.*; +import org.matsim.contrib.vsp.scenario.SnzActivities; import org.matsim.core.controler.PersonPrepareForSimAlgorithm; import org.matsim.core.network.filter.NetworkFilterManager; import org.matsim.core.population.algorithms.PlanAlgorithm; @@ -19,7 +20,6 @@ import org.matsim.facilities.ActivityFacilities; import org.matsim.facilities.FacilitiesUtils; import org.matsim.facilities.Facility; -import playground.vsp.openberlinscenario.cemdap.output.ActivityTypes; import java.util.ArrayList; import java.util.List; @@ -57,7 +57,7 @@ final class LeipzigRouterPlanAlgorithm implements PlanAlgorithm, PersonPrepareFo // keep all nodes that have no in and out links inside parking area // otherwise nearest link might crash if it finds an empty node networkFilterManager.addNodeFilter(n -> - n.getInLinks().values().stream().noneMatch(LeipzigUtils::isLinkParkingTypeInsideResidentialArea) && + n.getInLinks().values().stream().noneMatch(LeipzigUtils::isLinkParkingTypeInsideResidentialArea) && n.getOutLinks().values().stream().noneMatch(LeipzigUtils::isLinkParkingTypeInsideResidentialArea)); this.reducedNetwork = networkFilterManager.applyFilters(); @@ -72,7 +72,9 @@ private static LeipzigUtils.PersonParkingBehaviour getParkingBehaviour(Network f // an dieser stelle waere es besser abzufragen, ob die person in der naehe wohnt anstatt nur die home act -> residential parking zuzuordnen // check if non-home activity (since otherwise we assume that there is no parking restriction): - if (!originActivity.getType().contains(ActivityTypes.HOME) && !originActivity.getType().contains(ActivityTypes.SHOPPING)) { + if (!originActivity.getType().startsWith(SnzActivities.home.name()) && + !originActivity.getType().startsWith(SnzActivities.shop_daily.name()) && + !originActivity.getType().startsWith(SnzActivities.shop_other.name())) { Link link = fullModalNetwork.getLinks().get(originActivity.getLinkId()); if (isLinkParkingTypeInsideResidentialArea(link)) { diff --git a/src/test/java/org/matsim/run/ChessboardParkingTest.java b/src/test/java/org/matsim/run/ChessboardParkingTest.java index 71fd000a..bedc7eb5 100644 --- a/src/test/java/org/matsim/run/ChessboardParkingTest.java +++ b/src/test/java/org/matsim/run/ChessboardParkingTest.java @@ -13,153 +13,41 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.population.*; +import org.matsim.contrib.vsp.scenario.SnzActivities; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; -import org.matsim.core.config.groups.*; +import org.matsim.core.config.groups.FacilitiesConfigGroup; +import org.matsim.core.config.groups.PlansCalcRouteConfigGroup; +import org.matsim.core.config.groups.StrategyConfigGroup; +import org.matsim.core.config.groups.VspExperimentalConfigGroup; import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; import org.matsim.core.controler.OutputDirectoryHierarchy; import org.matsim.core.population.PopulationUtils; -import org.matsim.core.population.algorithms.ParallelPersonAlgorithmUtils; -import org.matsim.core.population.algorithms.PersonPrepareForSim; -import org.matsim.core.router.*; +import org.matsim.core.router.TripStructureUtils; import org.matsim.core.scenario.MutableScenario; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.utils.io.IOUtils; import org.matsim.examples.ExamplesUtils; import org.matsim.testcases.MatsimTestUtils; -import playground.vsp.openberlinscenario.cemdap.output.ActivityTypes; import java.net.URL; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; -import static org.matsim.core.config.groups.PlanCalcScoreConfigGroup.*; +import static org.matsim.core.config.groups.PlanCalcScoreConfigGroup.ActivityParams; /** * Test class to test a parking logic, which included several areas where only residents are allowed to park */ public class ChessboardParkingTest { private static final Logger log = LogManager.getLogger(ChessboardParkingTest.class); + // private static final String HOME_ZONE_ID = "homeLinkId"; + final String RE_ROUTE_LEIPZIG = "ReRouteLeipzig"; @Rule public MatsimTestUtils utils = new MatsimTestUtils(); -// private static final String HOME_ZONE_ID = "homeLinkId"; - final String RE_ROUTE_LEIPZIG = "ReRouteLeipzig"; - - enum Situation { - //1) defaultLogic to defaultLogic - //1.1) defaultLogic linkInResidentialArea (=home act) to defaultLogic linkOutsideResidentialArea - defaultLogicLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea, - //1.2) defaultLogic linkOutsideResidentialArea to defaultLogic linkInResidentialArea (=home act) - defaultLogicLinkOutsideResidentialAreaToDefaultLogicLinkInResidentialArea, - //1.3) defaultLogic linkOutsideResidentialArea to defaultLogic linkOutsideResidentialArea - defaultLogicLinkOutsideResidentialAreaToDefaultLogicLinkOutsideResidentialArea, - - //2) parkingSearchLogicLeipzig to defaultLogic - //2.1) parkingSearchLogicLeipzig linkInResidentialArea (!=home act) to defaultLogic linkOutsideResidentialArea - parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea, - //2.2) parkingSearchLogicLeipzig linkInResidentialArea (!=home act) to defaultLogic linkInResidentialArea (=home act) - parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkInResidentialArea, - - //3) defaultLogic to parkingSearchLogicLeipzig - //3.1) defaultLogic linkOutsideResidentialArea to parkingSearchLogicLeipzig linkInResidentialArea (!=home act) - defaultLogicLinkOutsideResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea, - //3.2) defaultLogic linkInResidentialArea (=home act) to parkingSearchLogicLeipzig linkInResidentialArea (!=home act) - defaultLogicLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea, - - //4) parkingSearchLogicLeipzig to parkingSearchLogicLeipzig - //4.1) parkingSearchLogicLeipzig linkInResidentialArea (!=home act) to parkingSearchLogicLeipzig linkInResidentialArea (!=home act) - parkingSearchLogicLeipzigLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea - } - - @Test - public final void runChessboardParkingTest1() { - - URL url = IOUtils.extendUrl(ExamplesUtils.getTestScenarioURL("chessboard"), "config.xml"); - - for (Situation situation : Situation.values()) { - Config config = ConfigUtils.loadConfig(url); - config.controler().setLastIteration(1); - config.controler().setOutputDirectory(utils.getOutputDirectory() + "/" + situation.toString()); - config.global().setNumberOfThreads(0); - config.qsim().setNumberOfThreads(1); - config.controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - config.plansCalcRoute().setAccessEgressType(PlansCalcRouteConfigGroup.AccessEgressType.accessEgressModeToLink); - - config.strategy().clearStrategySettings(); - { - StrategyConfigGroup.StrategySettings stratSets = new StrategyConfigGroup.StrategySettings(); - stratSets.setWeight(1.); - stratSets.setStrategyName(RE_ROUTE_LEIPZIG); - config.strategy().addStrategySettings(stratSets); - } - - config.facilities().setFacilitiesSource(FacilitiesConfigGroup.FacilitiesSource.onePerActivityLinkInPlansFile); - - config.planCalcScore().addActivityParams(new ActivityParams(TripStructureUtils.createStageActivityType("parking")).setScoringThisActivityAtAll(false)); - - config.vspExperimental().setVspDefaultsCheckingLevel(VspExperimentalConfigGroup.VspDefaultsCheckingLevel.warn); - - MutableScenario scenario = (MutableScenario) ScenarioUtils.loadScenario(config); - - Population population = PopulationUtils.createPopulation(config); - createExampleParkingPopulation(population, scenario.getNetwork(), situation); - scenario.setPopulation(population); - log.warn("population size=" + scenario.getPopulation().getPersons().size() +" for case" + situation); - -// System.exit(-1); - //why ist the above here? Doesn't it end the test / sim so it is of no use here?! -sme0523 - - - - Controler controler = new Controler(scenario); - controler.addOverridingModule(new AbstractModule() { - @Override - public void install() { -// this.bind( MultimodalLinkChooser.class ).toInstance( new LeipzigMultimodalLinkChooser() ); - this.addPlanStrategyBinding(RE_ROUTE_LEIPZIG).toProvider(LeipzigRoutingStrategyProvider.class); - // yyyy this only uses it during replanning!!! kai, apr'23 - } - }); - - TestParkingListener testParkingListener = new TestParkingListener(); - controler.addOverridingModule(new AbstractModule() { - @Override - public void install() { - addEventHandlerBinding().toInstance(testParkingListener); - } - }); - controler.run(); - - getAssertions(situation, testParkingListener); - } - - - } - - -// static final class LeipzigMultimodalLinkChooser implements MultimodalLinkChooser { -// -// private final MultimodalLinkChooser delegate; -// @Inject LeipzigMultimodalLinkChooser() { -// this.delegate = RouterUtils.getMultimodalLinkChooserDefault(); -// } -// -// @Override public Link decideOnLink( Facility facility, Network network, RoutingRequest routingRequest ){ -// Link result = null ; -// -//// Person person = routingRequest.getPerson(); -//// -//// String homeZoneId = (String) person.getAttributes().getAttribute( HOME_ZONE_ID ); -//// -//// if ( homeLinkId.equals( facility.getLinkId().toString() ) ) { -//// -//// fall back on default: -//// if ( result == null ){ -//// result = this.delegate.decideOnLink( facility, network, routingRequest ); -//// } -// return result; -// } -// } static void createExampleParkingPopulation(Population population, Network network, Situation situation) { @@ -174,8 +62,8 @@ static void createExampleParkingPopulation(Population population, Network networ switch (situation) { //1.1 case defaultLogicLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea -> { - Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.HOME, originLink.getId()); - Activity destinationActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, destinationLink.getId()); + Activity originActivity = factory.createActivityFromLinkId(SnzActivities.home.name(), originLink.getId()); + Activity destinationActivity = factory.createActivityFromLinkId(SnzActivities.leisure.name(), destinationLink.getId()); originActivity.setEndTime(3600.); LeipzigUtils.setLinkParkingTypeToInsideResidentialArea(originLink); @@ -186,8 +74,8 @@ static void createExampleParkingPopulation(Population population, Network networ } //1.2 case defaultLogicLinkOutsideResidentialAreaToDefaultLogicLinkInResidentialArea -> { - Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, originLink.getId()); - Activity destinationActivity = factory.createActivityFromLinkId(ActivityTypes.HOME, destinationLink.getId()); + Activity originActivity = factory.createActivityFromLinkId(SnzActivities.leisure.name(), originLink.getId()); + Activity destinationActivity = factory.createActivityFromLinkId(SnzActivities.home.name(), destinationLink.getId()); originActivity.setEndTime(3600.); LeipzigUtils.setLinkParkingTypeToInsideResidentialArea(destinationLink); @@ -198,8 +86,8 @@ static void createExampleParkingPopulation(Population population, Network networ } //1.3 case defaultLogicLinkOutsideResidentialAreaToDefaultLogicLinkOutsideResidentialArea -> { - Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, originLink.getId()); - Activity destinationActivity = factory.createActivityFromLinkId(ActivityTypes.WORK, destinationLink.getId()); + Activity originActivity = factory.createActivityFromLinkId(SnzActivities.leisure.name(), originLink.getId()); + Activity destinationActivity = factory.createActivityFromLinkId(SnzActivities.work.name(), destinationLink.getId()); originActivity.setEndTime(3600.); plan.addActivity(originActivity); @@ -208,8 +96,8 @@ static void createExampleParkingPopulation(Population population, Network networ } //2.1 case parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea -> { - Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, originLink.getId()); - Activity destinationActivity = factory.createActivityFromLinkId(ActivityTypes.HOME, destinationLink.getId()); + Activity originActivity = factory.createActivityFromLinkId(SnzActivities.leisure.name(), originLink.getId()); + Activity destinationActivity = factory.createActivityFromLinkId(SnzActivities.home.name(), destinationLink.getId()); originActivity.setEndTime(3600.); LeipzigUtils.setLinkParkingTypeToInsideResidentialArea(originLink); @@ -220,8 +108,8 @@ static void createExampleParkingPopulation(Population population, Network networ } //2.2 case parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkInResidentialArea -> { - Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, originLink.getId()); - Activity destinationActivity = factory.createActivityFromLinkId(ActivityTypes.HOME, destinationLink.getId()); + Activity originActivity = factory.createActivityFromLinkId(SnzActivities.leisure.name(), originLink.getId()); + Activity destinationActivity = factory.createActivityFromLinkId(SnzActivities.home.name(), destinationLink.getId()); originActivity.setEndTime(3600.); LeipzigUtils.setLinkParkingTypeToInsideResidentialArea(originLink); @@ -233,8 +121,8 @@ static void createExampleParkingPopulation(Population population, Network networ } //3.1 case defaultLogicLinkOutsideResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea -> { - Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, originLink.getId()); - Activity destinationActivity = factory.createActivityFromLinkId(ActivityTypes.WORK, destinationLink.getId()); + Activity originActivity = factory.createActivityFromLinkId(SnzActivities.leisure.name(), originLink.getId()); + Activity destinationActivity = factory.createActivityFromLinkId(SnzActivities.work.name(), destinationLink.getId()); originActivity.setEndTime(3600.); LeipzigUtils.setLinkParkingTypeToInsideResidentialArea(destinationLink); @@ -245,8 +133,8 @@ static void createExampleParkingPopulation(Population population, Network networ } //3.2 case defaultLogicLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea -> { - Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.HOME, originLink.getId()); - Activity destinationActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, destinationLink.getId()); + Activity originActivity = factory.createActivityFromLinkId(SnzActivities.home.name(), originLink.getId()); + Activity destinationActivity = factory.createActivityFromLinkId(SnzActivities.leisure.name(), destinationLink.getId()); originActivity.setEndTime(3600.); LeipzigUtils.setLinkParkingTypeToInsideResidentialArea(originLink); @@ -258,8 +146,8 @@ static void createExampleParkingPopulation(Population population, Network networ } //4.1 case parkingSearchLogicLeipzigLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea -> { - Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, originLink.getId()); - Activity destinationActivity = factory.createActivityFromLinkId(ActivityTypes.WORK, destinationLink.getId()); + Activity originActivity = factory.createActivityFromLinkId(SnzActivities.leisure.name(), originLink.getId()); + Activity destinationActivity = factory.createActivityFromLinkId(SnzActivities.work.name(), destinationLink.getId()); originActivity.setEndTime(3600.); LeipzigUtils.setLinkParkingTypeToInsideResidentialArea(originLink); @@ -277,25 +165,95 @@ static void createExampleParkingPopulation(Population population, Network networ population.addPerson(person); } - class TestParkingListener implements ActivityStartEventHandler { + @Test + public final void runChessboardParkingTest1() { - HashMap, List> parkingActivities = new HashMap<>(); + URL url = IOUtils.extendUrl(ExamplesUtils.getTestScenarioURL("chessboard"), "config.xml"); - @Override - public void handleEvent(ActivityStartEvent activityStartEvent) { - if (activityStartEvent.getActType().equals("parking interaction")) { - if (!parkingActivities.containsKey(activityStartEvent.getPersonId())) { - parkingActivities.put(activityStartEvent.getPersonId(), new ArrayList<>(Arrays.asList(activityStartEvent))); - } else parkingActivities.get(activityStartEvent.getPersonId()).add(activityStartEvent); + for (Situation situation : Situation.values()) { + Config config = ConfigUtils.loadConfig(url); + config.controler().setLastIteration(1); + config.controler().setOutputDirectory(utils.getOutputDirectory() + "/" + situation.toString()); + config.global().setNumberOfThreads(0); + config.qsim().setNumberOfThreads(1); + config.controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + config.plansCalcRoute().setAccessEgressType(PlansCalcRouteConfigGroup.AccessEgressType.accessEgressModeToLink); + + config.strategy().clearStrategySettings(); + { + StrategyConfigGroup.StrategySettings stratSets = new StrategyConfigGroup.StrategySettings(); + stratSets.setWeight(1.); + stratSets.setStrategyName(RE_ROUTE_LEIPZIG); + config.strategy().addStrategySettings(stratSets); } - } - @Override - public void reset(int iteration) { - ActivityStartEventHandler.super.reset(iteration); + config.facilities().setFacilitiesSource(FacilitiesConfigGroup.FacilitiesSource.onePerActivityLinkInPlansFile); + + config.planCalcScore().addActivityParams(new ActivityParams(TripStructureUtils.createStageActivityType("parking")).setScoringThisActivityAtAll(false)); + + config.vspExperimental().setVspDefaultsCheckingLevel(VspExperimentalConfigGroup.VspDefaultsCheckingLevel.warn); + + MutableScenario scenario = (MutableScenario) ScenarioUtils.loadScenario(config); + + Population population = PopulationUtils.createPopulation(config); + createExampleParkingPopulation(population, scenario.getNetwork(), situation); + scenario.setPopulation(population); + log.warn("population size=" + scenario.getPopulation().getPersons().size() + " for case" + situation); + +// System.exit(-1); + //why ist the above here? Doesn't it end the test / sim so it is of no use here?! -sme0523 + + + Controler controler = new Controler(scenario); + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { +// this.bind( MultimodalLinkChooser.class ).toInstance( new LeipzigMultimodalLinkChooser() ); + this.addPlanStrategyBinding(RE_ROUTE_LEIPZIG).toProvider(LeipzigRoutingStrategyProvider.class); + // yyyy this only uses it during replanning!!! kai, apr'23 + } + }); + + TestParkingListener testParkingListener = new TestParkingListener(); + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { + addEventHandlerBinding().toInstance(testParkingListener); + } + }); + controler.run(); + + getAssertions(situation, testParkingListener); } + + } + +// static final class LeipzigMultimodalLinkChooser implements MultimodalLinkChooser { +// +// private final MultimodalLinkChooser delegate; +// @Inject LeipzigMultimodalLinkChooser() { +// this.delegate = RouterUtils.getMultimodalLinkChooserDefault(); +// } +// +// @Override public Link decideOnLink( Facility facility, Network network, RoutingRequest routingRequest ){ +// Link result = null ; +// +//// Person person = routingRequest.getPerson(); +//// +//// String homeZoneId = (String) person.getAttributes().getAttribute( HOME_ZONE_ID ); +//// +//// if ( homeLinkId.equals( facility.getLinkId().toString() ) ) { +//// +//// fall back on default: +//// if ( result == null ){ +//// result = this.delegate.decideOnLink( facility, network, routingRequest ); +//// } +// return result; +// } +// } + private void getAssertions(Situation situation, TestParkingListener listener) { switch (situation) { @@ -303,65 +261,110 @@ private void getAssertions(Situation situation, TestParkingListener listener) { //1.1 case defaultLogicLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea -> { Assert.assertFalse(listener.parkingActivities.containsKey(Id.createPersonId(String.valueOf( - Situation.defaultLogicLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea)))); + Situation.defaultLogicLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea)))); } //1.2 case defaultLogicLinkOutsideResidentialAreaToDefaultLogicLinkInResidentialArea -> { Assert.assertFalse(listener.parkingActivities.containsKey(Id.createPersonId(String.valueOf( - Situation.defaultLogicLinkOutsideResidentialAreaToDefaultLogicLinkInResidentialArea)))); + Situation.defaultLogicLinkOutsideResidentialAreaToDefaultLogicLinkInResidentialArea)))); } //1.3 case defaultLogicLinkOutsideResidentialAreaToDefaultLogicLinkOutsideResidentialArea -> { Assert.assertFalse(listener.parkingActivities.containsKey(Id.createPersonId(String.valueOf( - Situation.defaultLogicLinkOutsideResidentialAreaToDefaultLogicLinkOutsideResidentialArea)))); + Situation.defaultLogicLinkOutsideResidentialAreaToDefaultLogicLinkOutsideResidentialArea)))); } //2.1 case parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea -> { Assert.assertTrue(listener.parkingActivities.containsKey(Id.createPersonId(String.valueOf - (Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea)))); + (Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea)))); Assert.assertEquals("wrong number of parking activites!", 1, listener.parkingActivities.get(Id.createPersonId(String.valueOf( - Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea))).size()); + Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea))).size()); Assert.assertEquals("wrong origin parking link", Id.createLinkId("169"), listener.parkingActivities.get(Id.createPersonId(String.valueOf( - Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea))).get(0).getLinkId()); + Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea))).get(0).getLinkId()); } //2.2 case parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkInResidentialArea -> { Assert.assertTrue(listener.parkingActivities.containsKey(Id.createPersonId(String.valueOf - (Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkInResidentialArea)))); + (Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkInResidentialArea)))); Assert.assertEquals("wrong number of parking activites!", 1, listener.parkingActivities.get(Id.createPersonId(String.valueOf( - Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkInResidentialArea))).size()); + Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkInResidentialArea))).size()); Assert.assertEquals("wrong origin parking link", Id.createLinkId("169"), listener.parkingActivities.get(Id.createPersonId(String.valueOf( - Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkInResidentialArea))).get(0).getLinkId()); + Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkInResidentialArea))).get(0).getLinkId()); } //3.1 case defaultLogicLinkOutsideResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea -> { Assert.assertTrue(listener.parkingActivities.containsKey(Id.createPersonId(String.valueOf - (Situation.defaultLogicLinkOutsideResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea)))); + (Situation.defaultLogicLinkOutsideResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea)))); Assert.assertEquals("wrong number of parking activites!", 1, listener.parkingActivities.get(Id.createPersonId(String.valueOf( - Situation.defaultLogicLinkOutsideResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea))).size()); + Situation.defaultLogicLinkOutsideResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea))).size()); Assert.assertEquals("wrong destination parking link", Id.createLinkId("133"), listener.parkingActivities.get(Id.createPersonId(String.valueOf( - Situation.defaultLogicLinkOutsideResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea))).get(0).getLinkId()); + Situation.defaultLogicLinkOutsideResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea))).get(0).getLinkId()); } //3.2 case defaultLogicLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea -> { Assert.assertTrue(listener.parkingActivities.containsKey(Id.createPersonId(String.valueOf - (Situation.defaultLogicLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea)))); + (Situation.defaultLogicLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea)))); Assert.assertEquals("wrong number of parking activites!", 1, listener.parkingActivities.get(Id.createPersonId(String.valueOf( - Situation.defaultLogicLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea))).size()); + Situation.defaultLogicLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea))).size()); Assert.assertEquals("wrong destination parking link", Id.createLinkId("133"), listener.parkingActivities.get(Id.createPersonId(String.valueOf( - Situation.defaultLogicLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea))).get(0).getLinkId()); + Situation.defaultLogicLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea))).get(0).getLinkId()); } //4.1 case parkingSearchLogicLeipzigLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea -> { Assert.assertTrue(listener.parkingActivities.containsKey(Id.createPersonId(String.valueOf - (Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea)))); + (Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea)))); Assert.assertEquals("wrong number of parking activites!", 2, listener.parkingActivities.get(Id.createPersonId(String.valueOf( - Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea))).size()); + Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea))).size()); Assert.assertEquals("wrong origin parking link", Id.createLinkId("169"), listener.parkingActivities.get(Id.createPersonId(String.valueOf( - Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea))).get(0).getLinkId()); + Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea))).get(0).getLinkId()); Assert.assertEquals("wrong destination parking link", Id.createLinkId("133"), listener.parkingActivities.get(Id.createPersonId(String.valueOf( - Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea))).get(1).getLinkId()); + Situation.parkingSearchLogicLeipzigLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea))).get(1).getLinkId()); } } } + + enum Situation { + //1) defaultLogic to defaultLogic + //1.1) defaultLogic linkInResidentialArea (=home act) to defaultLogic linkOutsideResidentialArea + defaultLogicLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea, + //1.2) defaultLogic linkOutsideResidentialArea to defaultLogic linkInResidentialArea (=home act) + defaultLogicLinkOutsideResidentialAreaToDefaultLogicLinkInResidentialArea, + //1.3) defaultLogic linkOutsideResidentialArea to defaultLogic linkOutsideResidentialArea + defaultLogicLinkOutsideResidentialAreaToDefaultLogicLinkOutsideResidentialArea, + + //2) parkingSearchLogicLeipzig to defaultLogic + //2.1) parkingSearchLogicLeipzig linkInResidentialArea (!=home act) to defaultLogic linkOutsideResidentialArea + parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkOutsideResidentialArea, + //2.2) parkingSearchLogicLeipzig linkInResidentialArea (!=home act) to defaultLogic linkInResidentialArea (=home act) + parkingSearchLogicLeipzigLinkInResidentialAreaToDefaultLogicLinkInResidentialArea, + + //3) defaultLogic to parkingSearchLogicLeipzig + //3.1) defaultLogic linkOutsideResidentialArea to parkingSearchLogicLeipzig linkInResidentialArea (!=home act) + defaultLogicLinkOutsideResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea, + //3.2) defaultLogic linkInResidentialArea (=home act) to parkingSearchLogicLeipzig linkInResidentialArea (!=home act) + defaultLogicLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea, + + //4) parkingSearchLogicLeipzig to parkingSearchLogicLeipzig + //4.1) parkingSearchLogicLeipzig linkInResidentialArea (!=home act) to parkingSearchLogicLeipzig linkInResidentialArea (!=home act) + parkingSearchLogicLeipzigLinkInResidentialAreaToParkingSearchLogicLeipzigLinkInResidentialArea + } + + class TestParkingListener implements ActivityStartEventHandler { + + HashMap, List> parkingActivities = new HashMap<>(); + + @Override + public void handleEvent(ActivityStartEvent activityStartEvent) { + if (activityStartEvent.getActType().equals("parking interaction")) { + if (!parkingActivities.containsKey(activityStartEvent.getPersonId())) { + parkingActivities.put(activityStartEvent.getPersonId(), new ArrayList<>(Arrays.asList(activityStartEvent))); + } else parkingActivities.get(activityStartEvent.getPersonId()).add(activityStartEvent); + } + } + + @Override + public void reset(int iteration) { + ActivityStartEventHandler.super.reset(iteration); + } + } } diff --git a/src/test/java/org/matsim/run/RunModalExperimentTest.java b/src/test/java/org/matsim/run/RunModalExperimentTest.java index 5435e020..182f36bb 100644 --- a/src/test/java/org/matsim/run/RunModalExperimentTest.java +++ b/src/test/java/org/matsim/run/RunModalExperimentTest.java @@ -1,13 +1,11 @@ package org.matsim.run; import com.google.common.collect.Lists; -import org.checkerframework.checker.units.qual.C; import org.junit.Assert; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.TransportMode; import org.matsim.api.core.v01.population.*; import org.matsim.application.MATSimApplication; @@ -16,21 +14,15 @@ import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.config.groups.VspExperimentalConfigGroup; -import org.matsim.core.controler.Controler; -import org.matsim.core.controler.ControlerUtils; import org.matsim.core.controler.OutputDirectoryHierarchy; import org.matsim.core.population.PopulationUtils; -import org.matsim.core.scenario.ScenarioUtils; -import org.matsim.core.utils.io.IOUtils; import org.matsim.testcases.MatsimTestUtils; -import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; public class RunModalExperimentTest { From c90772a5b6cf5f7ba6d9382dd5122e3fb07c23e8 Mon Sep 17 00:00:00 2001 From: rakow Date: Tue, 21 Nov 2023 10:10:24 +0100 Subject: [PATCH 104/106] also update parking cost handler test --- .../run/TimeRestrictedParkingCostHandlerTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/java/org/matsim/run/TimeRestrictedParkingCostHandlerTest.java b/src/test/java/org/matsim/run/TimeRestrictedParkingCostHandlerTest.java index c5303636..016f9df9 100644 --- a/src/test/java/org/matsim/run/TimeRestrictedParkingCostHandlerTest.java +++ b/src/test/java/org/matsim/run/TimeRestrictedParkingCostHandlerTest.java @@ -13,6 +13,7 @@ import org.matsim.api.core.v01.events.handler.PersonMoneyEventHandler; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.*; +import org.matsim.contrib.vsp.scenario.SnzActivities; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.config.groups.VspExperimentalConfigGroup; @@ -24,7 +25,6 @@ import org.matsim.core.utils.io.IOUtils; import org.matsim.examples.ExamplesUtils; import org.matsim.testcases.MatsimTestUtils; -import playground.vsp.openberlinscenario.cemdap.output.ActivityTypes; import playground.vsp.simpleParkingCostHandler.ParkingCostConfigGroup; import java.net.URL; @@ -57,8 +57,8 @@ void createExamplePopulation(Population population, Scenario scenario, Situation destinationLink.getAttributes().putAttribute(LeipzigUtils.getExtraHourParkingCostLinkAttributeName(), 1.); destinationLink.getAttributes().putAttribute(LeipzigUtils.getResidentialParkingFeeAttributeName(), 1.); - Activity originActivity = factory.createActivityFromLinkId(ActivityTypes.WORK, startLink.getId()); - Activity destinationActivity = factory.createActivityFromLinkId(ActivityTypes.LEISURE, destinationLink.getId()); + Activity originActivity = factory.createActivityFromLinkId(SnzActivities.work.name(), startLink.getId()); + Activity destinationActivity = factory.createActivityFromLinkId(SnzActivities.leisure.name(), destinationLink.getId()); Leg carLeg = factory.createLeg(TransportMode.car); @@ -87,7 +87,7 @@ void createExamplePopulation(Population population, Scenario scenario, Situation // 2.2) eventime outside parking period -> no charging // 3.2) eventTime is later than end of period -> no charging - Activity originActivity2 = factory.createActivityFromLinkId(ActivityTypes.WORK, startLink.getId()); + Activity originActivity2 = factory.createActivityFromLinkId(SnzActivities.work.name(), startLink.getId()); originActivity2.setEndTime(75700.); plan2.addActivity(originActivity2); plan2.addLeg(carLeg); @@ -106,7 +106,7 @@ void createExamplePopulation(Population population, Scenario scenario, Situation plan.addActivity(destinationActivity); // 4.2) eventTime is equal or later than start of period -> charging - Activity originActivity2 = factory.createActivityFromLinkId(ActivityTypes.WORK, startLink.getId()); + Activity originActivity2 = factory.createActivityFromLinkId(SnzActivities.work.name(), startLink.getId()); originActivity2.setEndTime(28900.); plan2.addActivity(originActivity2); plan2.addLeg(carLeg); From ce85ca3bf0c060ded9cc2a64576acccdce032ffb Mon Sep 17 00:00:00 2001 From: GregorRyb Date: Wed, 22 Nov 2023 22:51:58 +0100 Subject: [PATCH 105/106] add parking test with leipzig run class --- .../org/matsim/run/ParkingLeipzigTest.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/test/java/org/matsim/run/ParkingLeipzigTest.java diff --git a/src/test/java/org/matsim/run/ParkingLeipzigTest.java b/src/test/java/org/matsim/run/ParkingLeipzigTest.java new file mode 100644 index 00000000..9e4fd243 --- /dev/null +++ b/src/test/java/org/matsim/run/ParkingLeipzigTest.java @@ -0,0 +1,44 @@ +package org.matsim.run; + +import org.junit.Test; +import org.matsim.analysis.ParkingLocation; +import org.matsim.api.core.v01.population.PlanElement; +import org.matsim.api.core.v01.population.Population; +import org.matsim.application.MATSimApplication; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.controler.OutputDirectoryHierarchy; +import org.matsim.core.population.PopulationUtils; +import org.matsim.simwrapper.SimWrapperConfigGroup; +import java.nio.file.Path; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; + + +public class ParkingLeipzigTest { + + private static final String URL = "https://svn.vsp.tu-berlin.de/repos/public-svn/matsim/scenarios/countries/de/leipzig/leipzig-v1.2/input/"; + private static final String exampleShp = "input/v1.2/drtServiceArea/Leipzig_stadt.shp"; + + @Test + public final void runPoint1pctIntegrationTest() { + Path output = Path.of("output-parking-test/it-1pct"); + Config config = ConfigUtils.loadConfig("input/v1.2/leipzig-v1.2-25pct.config.xml"); + config.global().setNumberOfThreads(1); + config.qsim().setNumberOfThreads(1); + config.controler().setLastIteration(0); + config.controler().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + config.controler().setOutputDirectory(output.toString()); + ConfigUtils.addOrGetModule(config, SimWrapperConfigGroup.class).defaultDashboards = SimWrapperConfigGroup.Mode.disabled; + config.plans().setInputFile(URL + "leipzig-v1.2-0.1pct.plans-initial.xml.gz"); + + MATSimApplication.execute(RunLeipzigScenario.class, config, "run", "--1pct","--drt-area", exampleShp, "--post-processing", "disabled", + "--parking-cost-area", "input/v" + RunLeipzigScenario.VERSION + "/parkingCostArea/Bewohnerparken_2020.shp", + "--intermodality", "drtAsAccessEgressForPt"); + + assertThat(output) + .exists() + .isNotEmptyDirectory(); + new ParkingLocation().execute("--directory", output.toString()); + } +} From 779246c7bb9507c5854fe628630b2009396aae38 Mon Sep 17 00:00:00 2001 From: tschlenther Date: Fri, 24 Nov 2023 12:23:39 +0100 Subject: [PATCH 106/106] flag for initialReroute --- src/main/java/org/matsim/run/RunLeipzigScenario.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/matsim/run/RunLeipzigScenario.java b/src/main/java/org/matsim/run/RunLeipzigScenario.java index 061684a2..cceec884 100644 --- a/src/main/java/org/matsim/run/RunLeipzigScenario.java +++ b/src/main/java/org/matsim/run/RunLeipzigScenario.java @@ -96,6 +96,9 @@ public class RunLeipzigScenario extends MATSimApplication { @CommandLine.Option(names = "--intermodality", defaultValue = "drtAndPtSeparateFromEachOther", description = "Define if drt should be used as access and egress mode for pt.") private DrtCaseSetup.PtDrtIntermodality ptDrtIntermodality; + @CommandLine.Option(names = "--initial-reroute", defaultValue = "false", description = "Define whether ALL trips should be rerouted prior to the simulation. use for base case calibration only.") + private boolean initialReroute; + public RunLeipzigScenario(@Nullable Config config) { super(config); } @@ -307,7 +310,12 @@ public void install() { addControlerListenerBinding().to(ModeChoiceCoverageControlerListener.class); // Leipzig specific planning strategies - this.addPersonPrepareForSimAlgorithm().to(LeipzigRouterPlanAlgorithm.class); + + if(initialReroute){ + //re-route prior to simulation + this.addPersonPrepareForSimAlgorithm().to(LeipzigRouterPlanAlgorithm.class); + } + this.addPlanStrategyBinding(LeipzigRoutingStrategyProvider.STRATEGY_NAME).toProvider(LeipzigRoutingStrategyProvider.class); this.addPlanStrategyBinding(LeipzigSubtourModeChoice.STRATEGY_NAME).toProvider(LeipzigSubtourModeChoice.class);