diff --git a/src/main/java/org/matsim/run/RunGladbeckScenario.java b/src/main/java/org/matsim/run/RunGladbeckScenario.java index cbf0f83..202827e 100644 --- a/src/main/java/org/matsim/run/RunGladbeckScenario.java +++ b/src/main/java/org/matsim/run/RunGladbeckScenario.java @@ -8,7 +8,9 @@ import org.matsim.api.core.v01.TransportMode; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.Activity; +import org.matsim.api.core.v01.population.Person; import org.matsim.application.MATSimApplication; +import org.matsim.application.analysis.HomeLocationFilter; import org.matsim.application.analysis.emissions.AirPollutionByVehicleCategory; import org.matsim.application.analysis.emissions.AirPollutionSpatialAggregation; import org.matsim.application.analysis.noise.NoiseAnalysis; @@ -21,6 +23,7 @@ import org.matsim.core.config.groups.PlansCalcRouteConfigGroup; import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; +import org.matsim.core.gbl.MatsimRandom; import org.matsim.core.population.PopulationUtils; import org.matsim.core.router.MultimodalLinkChooser; import org.matsim.core.utils.io.IOUtils; @@ -29,15 +32,15 @@ import org.matsim.prepare.PrepareOpenPopulation; import org.matsim.prepare.ScenarioCutOut; import org.matsim.run.policies.KlimaTaler; +import org.matsim.run.policies.PtFlatrate; import org.matsim.run.policies.ReduceSpeed; import org.matsim.run.policies.SchoolRoadsClosure; import org.matsim.utils.gis.shp2matsim.ShpGeometryUtils; import picocli.CommandLine; import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.io.BufferedWriter; +import java.io.IOException; +import java.util.*; @CommandLine.Command(header = ":: Gladbeck Scenario ::", version = RunGladbeckScenario.VERSION) @MATSimApplication.Prepare({ScenarioCutOut.class, DownSamplePopulation.class, FixSubtourModes.class, XYToLinks.class, ExtractHomeCoordinates.class, BicyclePolicies.class, PrepareOpenPopulation.class}) @@ -51,17 +54,23 @@ public class RunGladbeckScenario extends RunMetropoleRuhrScenario { @CommandLine.Option(names = "--schoolClosure", defaultValue = "false", description = "measures to ban car on certain links") boolean schoolClosure; - @CommandLine.Option(names = "--tempo30Zone", defaultValue = "false", description = "measures to reduce car speed to 30 km/h") + @CommandLine.Option(names = "--tempo30Zone", defaultValue = "false", description = "measures to reduce car speed to 30 km/h in a zone") boolean slowSpeedZone; - @CommandLine.Option(names = "--tempo30Streets", defaultValue = "false", description = "measures to reduce car speed to 30 km/h") + @CommandLine.Option(names = "--tempo30Streets", defaultValue = "false", description = "measures to reduce car speed to 30 km/h on links definded by a shape file") boolean slowSpeedOnDefinedLinks; @CommandLine.Mixin private ShpOptions shp; @CommandLine.Option(names = "--simplePtFlat", defaultValue = "false", description = "measures to allow everyone to have free pt") - boolean simplePtFlat; + boolean scenarioWidePtFlat; + + @CommandLine.Option(names = "--ptFlat", defaultValue = "0", description = "measures to allow people in Gladbeck to have free pt, if set to zero no agent will have free pt") + int ptFlat; + + @CommandLine.Option(names = "--cityWidePtFlat", defaultValue = "false", description = "measures to allow every resident in Gladbeck to have free pt") + boolean cityWidePtFlat; @CommandLine.Option(names = "--cyclingCourse", defaultValue = "false", description = "measures to increase the ") boolean cyclingCourse; @@ -101,7 +110,7 @@ protected Config prepareConfig(Config config) { // so we donĀ“t use the rvr accessEgressModeToLinkPlusTimeConstant config.plansCalcRoute().setAccessEgressType(PlansCalcRouteConfigGroup.AccessEgressType.accessEgressModeToLink); - if (simplePtFlat) { + if (scenarioWidePtFlat) { config.planCalcScore().getModes().get(TransportMode.pt).setDailyMonetaryConstant(0.0); } @@ -122,7 +131,6 @@ protected void prepareScenario(Scenario scenario) { ReduceSpeed.implementPushMeasuresByModifyingNetworkInArea(scenario.getNetwork(), ShpGeometryUtils.loadPreparedGeometries(IOUtils.resolveFileOrResource(shp.getShapeFile().toString()))); } - if (schoolClosure) { List> listOfSchoolLinks = new ArrayList<>(); //TODO switch to shp file @@ -190,6 +198,41 @@ public void install() { bind(MultimodalLinkChooser.class).to(NearestLinkChooser.class); } }); + + + if (ptFlat !=0 || cityWidePtFlat) { + + List> agentsLivingInGladbeck = new ArrayList<>(); + List> agentsWithPtFlat = new ArrayList<>(); + HomeLocationFilter homeLocationFilter = new HomeLocationFilter(shp, controler.getScenario().getConfig().global().getCoordinateSystem(), controler.getScenario().getPopulation()); + + for (Person person: controler.getScenario().getPopulation().getPersons().values()) { + if (homeLocationFilter.test(controler.getScenario().getPopulation().getPersons().get(person.getId()))) { + agentsLivingInGladbeck.add(person.getId()); + } + } + + + if (cityWidePtFlat) { + agentsWithPtFlat.addAll(agentsLivingInGladbeck); + } else { + for (int ii= 0; ii < ptFlat; ii++) { + Random generator = MatsimRandom.getRandom(); + Object[] values = agentsLivingInGladbeck.toArray(); + var randomPerson = (Id) values[generator.nextInt(values.length)]; + agentsWithPtFlat.add(randomPerson); + agentsLivingInGladbeck.remove(randomPerson); + } + } + log.info("adding pt flat." + agentsWithPtFlat.size() +" agents will pay no pt cost"); + try { + writeOutAgents(agentsWithPtFlat); + } catch (IOException e) { + throw new RuntimeException(e); + } + addPtFlat(controler, new PtFlatrate(agentsWithPtFlat, controler.getConfig().planCalcScore().getModes().get(TransportMode.pt).getDailyMonetaryConstant())); + + } super.prepareControler(controler); } @@ -203,4 +246,27 @@ public void install() { } }); } + + public static void addPtFlat(Controler controler, PtFlatrate ptFlatrate) { + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { + addEventHandlerBinding().toInstance(ptFlatrate); + addControlerListenerBinding().toInstance(ptFlatrate); + new PersonMoneyEventsAnalysisModule(); + } + }); + } + + private static void writeOutAgents(List> listOfIds) throws IOException { + BufferedWriter writer = IOUtils.getBufferedWriter("agentsWithFreePt.tsv"); + + writer.write("Id"); + writer.newLine(); + for (Id listOfId : listOfIds) { + writer.write(listOfId.toString()); + writer.newLine(); + } + writer.close(); + } } diff --git a/src/main/java/org/matsim/run/policies/PtFlatrate.java b/src/main/java/org/matsim/run/policies/PtFlatrate.java index 775268e..99e9682 100644 --- a/src/main/java/org/matsim/run/policies/PtFlatrate.java +++ b/src/main/java/org/matsim/run/policies/PtFlatrate.java @@ -10,45 +10,41 @@ import org.matsim.core.controler.listener.AfterMobsimListener; import org.matsim.core.utils.misc.Time; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; public class PtFlatrate implements PersonDepartureEventHandler, AfterMobsimListener { - private final Map, Integer> personsEligibleForPtFiltrate; - private final Map, Integer> currentIterationForPtFlatrate = new HashMap<>(); - private final double ptConstant; + private final List> personsEligibleForPtFlatrate; + private final List> currentIterationForPtFlatrate = new ArrayList<>(); private final double ptDailyMonetaryConstant; - public PtFlatrate(Map, Integer> personsEligibleForPtFiltrate, double ptConstant, double ptDailyMonetaryConstant) { - this.personsEligibleForPtFiltrate = personsEligibleForPtFiltrate; - this.ptConstant = ptConstant; + public PtFlatrate(List> personsEligibleForPtFlatrate, double ptDailyMonetaryConstant) { + this.personsEligibleForPtFlatrate = personsEligibleForPtFlatrate; this.ptDailyMonetaryConstant = ptDailyMonetaryConstant; } @Override public void handleEvent(PersonDepartureEvent personDepartureEvent) { - if (currentIterationForPtFlatrate.containsKey(personDepartureEvent.getPersonId()) + if (currentIterationForPtFlatrate.contains(personDepartureEvent.getPersonId()) && personDepartureEvent.getLegMode().equals(TransportMode.pt)) { - int numberOfPtTrips = currentIterationForPtFlatrate.get(personDepartureEvent.getPersonId()); - numberOfPtTrips++; - currentIterationForPtFlatrate.replace(personDepartureEvent.getPersonId(),numberOfPtTrips); + currentIterationForPtFlatrate.add(personDepartureEvent.getPersonId()); } } @Override public void notifyAfterMobsim(AfterMobsimEvent afterMobsimEvent) { - for (Map.Entry, Integer> idDoubleEntry : currentIterationForPtFlatrate.entrySet()) { - Id person = idDoubleEntry.getKey(); - Integer numberOfPtTrips = idDoubleEntry.getValue(); - double ptFlat = numberOfPtTrips * ptConstant + ptDailyMonetaryConstant; - afterMobsimEvent.getServices().getEvents().processEvent(new PersonMoneyEvent(Time.MIDNIGHT, person, ptFlat, "ptFlat",null, null )); + for (Id person: currentIterationForPtFlatrate) { + // multiplied by -1 to throw positiv person money event + afterMobsimEvent.getServices().getEvents().processEvent(new PersonMoneyEvent(Time.MIDNIGHT, person, (-1) * ptDailyMonetaryConstant, "ptFlat",null, null )); } } @Override public void reset(int iteration) { currentIterationForPtFlatrate.clear(); - currentIterationForPtFlatrate.putAll(personsEligibleForPtFiltrate); + currentIterationForPtFlatrate.addAll(personsEligibleForPtFlatrate); } } diff --git a/src/test/java/org/matsim/run/GladbeckIntegrationTest.java b/src/test/java/org/matsim/run/GladbeckIntegrationTest.java index 9ea498a..57ef5d7 100644 --- a/src/test/java/org/matsim/run/GladbeckIntegrationTest.java +++ b/src/test/java/org/matsim/run/GladbeckIntegrationTest.java @@ -20,7 +20,7 @@ import playground.vsp.openberlinscenario.cemdap.output.ActivityTypes; import playground.vsp.scoring.IncomeDependentUtilityOfMoneyPersonScoringParameters; -@Ignore + public class GladbeckIntegrationTest { @Rule public MatsimTestUtils utils = new MatsimTestUtils(); diff --git a/src/test/java/org/matsim/run/TestPtFlat.java b/src/test/java/org/matsim/run/TestPtFlat.java index a8b4128..cf123da 100644 --- a/src/test/java/org/matsim/run/TestPtFlat.java +++ b/src/test/java/org/matsim/run/TestPtFlat.java @@ -18,11 +18,12 @@ import static org.junit.Assert.assertTrue; +//test runs too long... @Ignore public class TestPtFlat { private static final Id personId = Id.createPersonId("test-person"); - private static final String inputNetworkFile = "https://svn.vsp.tu-berlin.de/repos/public-svn/matsim/scenarios/countries/de/metropole-ruhr/metropole-ruhr-v1.0/input/metropole-ruhr-v1.0.network_resolutionHigh-with-pt.xml.gz"; + private static final String inputNetworkFile = "https://svn.vsp.tu-berlin.de/repos/public-svn/matsim/scenarios/countries/de/gladbeck/glamobi/input/gladbeck-v1.0-10pct.network.xml.gz"; @Rule public MatsimTestUtils testUtils = new MatsimTestUtils(); @@ -32,20 +33,33 @@ public void testPtFlat() { var outputDir = testUtils.getOutputDirectory(); - MATSimApplication.execute(TestApplication.class, "--output=" + outputDir + "withPtFlat", "--simplePtFlat=true", "--download-input", "--1pct", "--config:network.inputNetworkFile=" + inputNetworkFile); - MATSimApplication.execute(TestApplication.class, "--output=" + outputDir + "withoutPtFlat", "--simplePtFlat=false", "--download-input", "--1pct", "--config:network.inputNetworkFile=" + inputNetworkFile); + MATSimApplication.execute(TestApplication.class, "--output=" + outputDir + "withPtFlat", "--ptFlat", "1", "--1pct", "--config:network.inputNetworkFile=" + inputNetworkFile, + "--shp", "/Users/gregorr/Documents/work/respos/shared-svn/projects/GlaMoBi/data/shp-files/Gladbeck.shp", + "--shp-crs", "EPSG:25832" + ); + MATSimApplication.execute(TestApplication.class, "--output=" + outputDir + "withoutPtFlat", "--1pct", "--config:network.inputNetworkFile=" + inputNetworkFile, + "--shp", "/Users/gregorr/Documents/work/respos/shared-svn/projects/GlaMoBi/data/shp-files/Gladbeck.shp", + "--shp-crs", "EPSG:25832" + ); // load output of both runs var scenarioWithPtFlat = ScenarioUtils.createScenario(ConfigUtils.createConfig()); new PopulationReader(scenarioWithPtFlat).readFile(outputDir + "withPtFlat/" + TestApplication.RUN_ID + ".output_plans.xml.gz"); var scenarioWithoutPtFlat = ScenarioUtils.createScenario(ConfigUtils.createConfig()); new PopulationReader(scenarioWithoutPtFlat).readFile(outputDir + "withoutPtFlat/" + TestApplication.RUN_ID + ".output_plans.xml.gz"); - // somehow compare the two routes - var personWithPtFlat = scenarioWithPtFlat.getPopulation().getPersons().get(personId); - var personWithoutPtFlat = scenarioWithoutPtFlat.getPopulation().getPersons().get(personId); + // somehow compare the two plans + var unaffectedAgentScenario1 = scenarioWithPtFlat.getPopulation().getPersons().get(personId); + var unaffectedAgentScenario2 = scenarioWithoutPtFlat.getPopulation().getPersons().get(personId); - assertTrue(personWithPtFlat.getSelectedPlan().getScore() > personWithoutPtFlat.getSelectedPlan().getScore()); + assertTrue(unaffectedAgentScenario1.getSelectedPlan().getScore().equals(unaffectedAgentScenario2.getSelectedPlan().getScore())); + System.out.printf(scenarioWithPtFlat.getPopulation().getPersons().toString() + "\n"); + var agentWithPtFlat = scenarioWithPtFlat.getPopulation().getPersons().get(Id.createPersonId(personId+"inside")); + var agentWithoutPtFlat = scenarioWithoutPtFlat.getPopulation().getPersons().get(Id.createPersonId(personId+"inside")); + + + + assertTrue(agentWithPtFlat.getSelectedPlan().getScore() > agentWithoutPtFlat.getSelectedPlan().getScore()); } public static class TestApplication extends RunGladbeckScenario { @@ -54,8 +68,8 @@ public static class TestApplication extends RunGladbeckScenario { @Override public Config prepareConfig(Config config) { Config preparedConfig = super.prepareConfig(config); - preparedConfig.global().setNumberOfThreads(1); - preparedConfig.qsim().setNumberOfThreads(1); + //preparedConfig.global().setNumberOfThreads(1); + //preparedConfig.qsim().setNumberOfThreads(1); preparedConfig.plans().setInputFile(null); preparedConfig.controler().setLastIteration(0); preparedConfig.controler().setRunId(RUN_ID); @@ -66,24 +80,43 @@ public Config prepareConfig(Config config) { protected void prepareScenario(Scenario scenario) { // Other agents are not needed for the test scenario.getPopulation().getPersons().clear(); - // add single person with two activities + // add single person with two activities living outside gladbeck var factory = scenario.getPopulation().getFactory(); var plan = factory.createPlan(); var homeCoord = scenario.getNetwork().getLinks().get( Id.createLinkId("pt_65711")).getCoord(); - var home = factory.createActivityFromCoord("home_600.0", homeCoord); + var home = factory.createActivityFromCoord("home_600", homeCoord); home.setEndTime(50400); plan.addActivity(home); var leg = factory.createLeg(TransportMode.pt); leg.setMode(TransportMode.pt); plan.addLeg(leg); var otherCoord = scenario.getNetwork().getLinks().get( Id.createLinkId("pt_65377")).getCoord(); - var other = factory.createActivityFromCoord("other_3600.0",otherCoord); + var other = factory.createActivityFromCoord("other_3600",otherCoord); other.setEndTime(54000); plan.addActivity(other); var person = factory.createPerson(personId); person.addPlan(plan); person.getAttributes().putAttribute("subpopulation", "person"); person.getAttributes().putAttribute(IncomeDependentUtilityOfMoneyPersonScoringParameters.PERSONAL_INCOME_ATTRIBUTE_NAME, 1.0); + + // add single person with two activities living inside gladbeck + var planInsider = factory.createPlan(); + var homeCoordInsider = scenario.getNetwork().getLinks().get(Id.createLinkId("pt_65455")).getCoord(); + var homeInsider = factory.createActivityFromCoord("home_600", homeCoordInsider); + homeInsider.setEndTime(50400); + planInsider.addActivity(homeInsider); + var legInsider = factory.createLeg(TransportMode.pt); + legInsider.setMode(TransportMode.pt); + planInsider.addLeg(legInsider); + var otherCoordInsider = scenario.getNetwork().getLinks().get( Id.createLinkId("pt_65377")).getCoord(); + var otherInsider = factory.createActivityFromCoord("other_3600",otherCoordInsider); + otherInsider.setEndTime(54000); + planInsider.addActivity(otherInsider); + var personInsider = factory.createPerson(Id.createPersonId(personId+"inside")); + personInsider.addPlan(planInsider); + personInsider.getAttributes().putAttribute("subpopulation", "person"); + personInsider.getAttributes().putAttribute(IncomeDependentUtilityOfMoneyPersonScoringParameters.PERSONAL_INCOME_ATTRIBUTE_NAME, 1.0); + scenario.getPopulation().addPerson(personInsider); scenario.getPopulation().addPerson(person); super.prepareScenario(scenario);