diff --git a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtils.java b/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtils.java index 0387c0d7873..3d8563424a1 100644 --- a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtils.java +++ b/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtils.java @@ -50,8 +50,10 @@ 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 org.matsim.vehicles.VehicleType; import org.matsim.vehicles.VehicleUtils; +import org.matsim.vehicles.Vehicles; import java.io.BufferedWriter; import java.io.IOException; @@ -169,7 +171,7 @@ private static void writeCSVWithCategoryHeader(HashMap idCounter = new HashMap<>(); Population populationFromCarrier = (Population) scenario.getScenarioElement("allpersons"); + Vehicles allVehicles = VehicleUtils.getOrCreateAllvehicles(scenario); + for (Person person : populationFromCarrier.getPersons().values()) { Plan plan = popFactory.createPlan(); @@ -238,11 +242,12 @@ else if (subpopulation.contains("goodsTraffic")) if (relatedCarrier.getAttributes().getAsMap().containsKey("tourStartArea")) newPerson.getAttributes().putAttribute("tourStartArea", relatedCarrier.getAttributes().getAttribute("tourStartArea")); - VehicleUtils.insertVehicleIdsIntoAttributes(newPerson, (new HashMap<>() { - { - put(mode, (Id.createVehicleId(person.getId().toString()))); - } - })); + + Id vehicleId = Id.createVehicleId(person.getId().toString()); + + VehicleUtils.insertVehicleIdsIntoAttributes(newPerson, Map.of(mode, vehicleId)); + VehicleUtils.insertVehicleTypesIntoAttributes(newPerson, Map.of(mode, allVehicles.getVehicles().get(vehicleId).getType().getId())); + population.addPerson(newPerson); } diff --git a/contribs/decongestion/src/test/java/org/matsim/contrib/decongestion/DecongestionPricingTestIT.java b/contribs/decongestion/src/test/java/org/matsim/contrib/decongestion/DecongestionPricingTestIT.java index 81f84e13492..58477477f23 100644 --- a/contribs/decongestion/src/test/java/org/matsim/contrib/decongestion/DecongestionPricingTestIT.java +++ b/contribs/decongestion/src/test/java/org/matsim/contrib/decongestion/DecongestionPricingTestIT.java @@ -1,560 +1,561 @@ -/* *********************************************************************** * - * project: org.matsim.* - * * - * *********************************************************************** * - * * - * copyright : (C) 2013 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.contrib.decongestion; - -import org.junit.Assert; -import org.junit.Rule; -import org.junit.Test; -import org.matsim.analysis.ScoreStatsControlerListener.ScoreItem; -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.Scenario; -import org.matsim.contrib.decongestion.DecongestionConfigGroup.DecongestionApproach; -import org.matsim.contrib.decongestion.data.DecongestionInfo; -import org.matsim.contrib.decongestion.handler.DelayAnalysis; -import org.matsim.contrib.decongestion.handler.IntervalBasedTolling; -import org.matsim.contrib.decongestion.handler.IntervalBasedTollingAll; -import org.matsim.contrib.decongestion.handler.PersonVehicleTracker; -import org.matsim.contrib.decongestion.routing.TollTimeDistanceTravelDisutilityFactory; -import org.matsim.contrib.decongestion.tollSetting.DecongestionTollSetting; -import org.matsim.contrib.decongestion.tollSetting.DecongestionTollingBangBang; -import org.matsim.contrib.decongestion.tollSetting.DecongestionTollingPID; -import org.matsim.core.config.Config; -import org.matsim.core.config.ConfigUtils; -import org.matsim.core.controler.AbstractModule; -import org.matsim.core.controler.Controler; -import org.matsim.core.controler.OutputDirectoryHierarchy; -import org.matsim.core.scenario.ScenarioUtils; -import org.matsim.testcases.MatsimTestUtils; - -/** - * - * - * @author ikaddoura - * - */ -public class DecongestionPricingTestIT { - - @Rule - public MatsimTestUtils testUtils = new MatsimTestUtils(); - - /** - * Kp = 0.0123 - * - */ - @Test - public final void test0a() { - - System.out.println(testUtils.getPackageInputDirectory()); - - final String configFile = testUtils.getPackageInputDirectory() + "/config0.xml"; - - Config config = ConfigUtils.loadConfig(configFile); - - String outputDirectory = testUtils.getOutputDirectory() + "/"; - config.controller().setOutputDirectory(outputDirectory); - - final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); - decongestionSettings.setWriteOutputIteration(1); - decongestionSettings.setKp(0.0123); - decongestionSettings.setKd(0.0); - decongestionSettings.setKi(0.0); - decongestionSettings.setMsa(false); - decongestionSettings.setTollBlendFactor(1.0); - decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); - decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); - config.addModule(decongestionSettings); - - final Scenario scenario = ScenarioUtils.loadScenario(config); - Controler controler = new Controler(scenario); - - DecongestionInfo info = new DecongestionInfo(); - - // congestion toll computation - controler.addOverridingModule(new AbstractModule() { - @Override - public void install() { - - this.bind(DecongestionInfo.class).toInstance(info); - - this.bind(DecongestionTollSetting.class).to(DecongestionTollingPID.class); - this.bind(IntervalBasedTolling.class).to(IntervalBasedTollingAll.class); - - this.bind(IntervalBasedTollingAll.class).asEagerSingleton(); - this.bind(DelayAnalysis.class).asEagerSingleton(); - this.bind(PersonVehicleTracker.class).asEagerSingleton(); - - this.addEventHandlerBinding().to(IntervalBasedTollingAll.class); - this.addEventHandlerBinding().to(DelayAnalysis.class); - this.addEventHandlerBinding().to(PersonVehicleTracker.class); - - this.addControlerListenerBinding().to(DecongestionControlerListener.class); - - } - }); - - // toll-adjusted routing - - final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); - - controler.addOverridingModule(new AbstractModule(){ - @Override - public void install() { - this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); - } - }); - - controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - controler.run(); - - double tt0 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 6 * 3600 + 50. * 60, null, null); - double tt1 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 63, null, null); - double tt2 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 15. * 60, null, null); - - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt0, MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 150.5, tt1, MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt2, MatsimTestUtils.EPSILON); - - final int index = config.controller().getLastIteration() - config.controller().getFirstIteration(); - double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index); - Assert.assertEquals("Wrong average executed score. The tolls seem to have changed.", -33.940316666666666, avgScore, MatsimTestUtils.EPSILON); - - System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().toString()); - System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().toString()); - - Assert.assertEquals("Wrong average delay (capacity is set in a way that one of the two agents has to wait 101 sec. Thus the average is 50.5", 50.5, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().get(84), MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong toll.", 50.5 * 0.0123, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(84), MatsimTestUtils.EPSILON); - - } - - /** - * Kp = 0.0123, other syntax - * - */ - @Test - public final void test0amodified() { - - System.out.println(testUtils.getPackageInputDirectory()); - - final String configFile = testUtils.getPackageInputDirectory() + "/config0.xml"; - - Config config = ConfigUtils.loadConfig(configFile); - - String outputDirectory = testUtils.getOutputDirectory() + "/"; - config.controller().setOutputDirectory(outputDirectory); - - final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); - decongestionSettings.setWriteOutputIteration(1); - decongestionSettings.setKp(0.0123); - decongestionSettings.setKd(0.0); - decongestionSettings.setKi(0.0); - decongestionSettings.setMsa(false); - decongestionSettings.setTollBlendFactor(1.0); - decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); - decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); - config.addModule(decongestionSettings); - - final Scenario scenario = ScenarioUtils.loadScenario(config); - Controler controler = new Controler(scenario); - - // congestion toll computation - controler.addOverridingModule(new DecongestionModule(scenario)); - - // toll-adjusted routing - - final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); - - controler.addOverridingModule(new AbstractModule(){ - @Override - public void install() { - this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); - } - }); - - controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - controler.run(); - - double tt0 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 6 * 3600 + 50. * 60, null, null); - double tt1 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 63, null, null); - double tt2 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 15. * 60, null, null); - - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt0, MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 150.5, tt1, MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt2, MatsimTestUtils.EPSILON); - - final int index = config.controller().getLastIteration() - config.controller().getFirstIteration(); - double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index); - Assert.assertEquals("Wrong average executed score. The tolls seem to have changed.", -33.940316666666666, avgScore, MatsimTestUtils.EPSILON); - } - - /** - * Kp = 2 - * - */ - @Test - public final void test0b() { - - System.out.println(testUtils.getPackageInputDirectory()); - - final String configFile = testUtils.getPackageInputDirectory() + "/config0.xml"; - - Config config = ConfigUtils.loadConfig(configFile); - - String outputDirectory = testUtils.getOutputDirectory() + "/"; - config.controller().setOutputDirectory(outputDirectory); - - final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); - decongestionSettings.setWriteOutputIteration(1); - decongestionSettings.setKp(2.0); - decongestionSettings.setKd(0.0); - decongestionSettings.setKi(0.0); - decongestionSettings.setMsa(false); - decongestionSettings.setTollBlendFactor(1.0); - decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); - decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); - config.addModule(decongestionSettings); - - final Scenario scenario = ScenarioUtils.loadScenario(config); - Controler controler = new Controler(scenario); - - DecongestionInfo info = new DecongestionInfo(); - - // congestion toll computation - controler.addOverridingModule(new AbstractModule() { - @Override - public void install() { - - this.bind(DecongestionInfo.class).toInstance(info); - - this.bind(DecongestionTollSetting.class).to(DecongestionTollingPID.class); - this.bind(IntervalBasedTolling.class).to(IntervalBasedTollingAll.class); - - this.bind(IntervalBasedTollingAll.class).asEagerSingleton(); - this.bind(DelayAnalysis.class).asEagerSingleton(); - this.bind(PersonVehicleTracker.class).asEagerSingleton(); - - this.addEventHandlerBinding().to(IntervalBasedTollingAll.class); - this.addEventHandlerBinding().to(DelayAnalysis.class); - this.addEventHandlerBinding().to(PersonVehicleTracker.class); - - this.addControlerListenerBinding().to(DecongestionControlerListener.class); - - } - }); - - // toll-adjusted routing - - final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); - - controler.addOverridingModule(new AbstractModule(){ - @Override - public void install() { - this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); - } - }); - - controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - controler.run(); - - double tt0 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 6 * 3600 + 50. * 60, null, null); - double tt1 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 63, null, null); - double tt2 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 15. * 60, null, null); - - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt0, MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 150.5, tt1, MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt2, MatsimTestUtils.EPSILON); - - final int index = config.controller().getLastIteration() - config.controller().getFirstIteration(); - double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index); - Assert.assertEquals("Wrong average executed score. The tolls seem to have changed.", -134.31916666666666, avgScore, MatsimTestUtils.EPSILON); - - System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().toString()); - System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().toString()); - - Assert.assertEquals("Wrong average delay (capacity is set in a way that one of the two agents has to wait 101 sec. Thus the average is 50.5", 50.5, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().get(84), MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong toll.", 50.5 * 2, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(84), MatsimTestUtils.EPSILON); - } - - /** - * Kp = 2 - * - */ - @Test - public final void test0bmodified() { - - System.out.println(testUtils.getPackageInputDirectory()); - - final String configFile = testUtils.getPackageInputDirectory() + "/config0.xml"; - - Config config = ConfigUtils.loadConfig(configFile); - - String outputDirectory = testUtils.getOutputDirectory() + "/"; - config.controller().setOutputDirectory(outputDirectory); - - final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); - decongestionSettings.setWriteOutputIteration(1); - decongestionSettings.setKp(2.0); - decongestionSettings.setKd(0.0); - decongestionSettings.setKi(0.0); - decongestionSettings.setMsa(false); - decongestionSettings.setTollBlendFactor(1.0); - decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); - decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); - config.addModule(decongestionSettings); - - final Scenario scenario = ScenarioUtils.loadScenario(config); - Controler controler = new Controler(scenario); - - // congestion toll computation - controler.addOverridingModule(new DecongestionModule(scenario)); - - // toll-adjusted routing - - final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); - - controler.addOverridingModule(new AbstractModule(){ - @Override - public void install() { - this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); - } - }); - - controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - controler.run(); - - double tt0 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 6 * 3600 + 50. * 60, null, null); - double tt1 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 63, null, null); - double tt2 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 15. * 60, null, null); - - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt0, MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 150.5, tt1, MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt2, MatsimTestUtils.EPSILON); - - final int index = config.controller().getLastIteration() - config.controller().getFirstIteration(); - double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index); - Assert.assertEquals("Wrong average executed score. The tolls seem to have changed.", -134.31916666666666, avgScore, MatsimTestUtils.EPSILON); - } - - /** - * Kp = 0 / no tolling - * - */ - @Test - public final void test0c() { - - System.out.println(testUtils.getPackageInputDirectory()); - - final String configFile = testUtils.getPackageInputDirectory() + "/config0.xml"; - - Config config = ConfigUtils.loadConfig(configFile); - - String outputDirectory = testUtils.getOutputDirectory() + "/"; - config.controller().setOutputDirectory(outputDirectory); - - final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); - decongestionSettings.setWriteOutputIteration(1); - decongestionSettings.setKp(0.0); - decongestionSettings.setKd(0.0); - decongestionSettings.setKi(0.0); - decongestionSettings.setMsa(false); - decongestionSettings.setTollBlendFactor(1.0); - decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); - decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); - config.addModule(decongestionSettings); - - final Scenario scenario = ScenarioUtils.loadScenario(config); - Controler controler = new Controler(scenario); - - DecongestionInfo info = new DecongestionInfo(); - - // congestion toll computation - controler.addOverridingModule(new AbstractModule() { - @Override - public void install() { - - this.bind(DecongestionInfo.class).toInstance(info); - - this.bind(DelayAnalysis.class).asEagerSingleton(); - this.addEventHandlerBinding().to(DelayAnalysis.class); - - this.addControlerListenerBinding().to(DecongestionControlerListener.class); - - } - }); - - // toll-adjusted routing - - final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); - - controler.addOverridingModule(new AbstractModule(){ - @Override - public void install() { - this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); - } - }); - - controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - controler.run(); - - double tt0 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 6 * 3600 + 50. * 60, null, null); - double tt1 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 63, null, null); - double tt2 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 15. * 60, null, null); - - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt0, MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 150.5, tt1, MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt2, MatsimTestUtils.EPSILON); - - final int index = config.controller().getLastIteration() - config.controller().getFirstIteration(); - double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index); - Assert.assertEquals("Wrong average executed score. The tolls seem to have changed.", -33.31916666666666, avgScore, MatsimTestUtils.EPSILON); - - System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().toString()); - System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().toString()); - - Assert.assertEquals("Wrong average delay (capacity is set in a way that one of the two agents has to wait 101 sec. Thus the average is 50.5", 50.5, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().get(84), MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong toll.", null, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(84)); - - } - - /** - * Tests the PID controller - * - */ - @Test - public final void test1() { - - System.out.println(testUtils.getPackageInputDirectory()); - - final String configFile = testUtils.getPackageInputDirectory() + "/config.xml"; - Config config = ConfigUtils.loadConfig(configFile); - - String outputDirectory = testUtils.getOutputDirectory() + "/"; - config.controller().setOutputDirectory(outputDirectory); - - final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); - decongestionSettings.setWriteOutputIteration(1); - decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); - decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); - decongestionSettings.setDecongestionApproach(DecongestionApproach.PID); - config.addModule(decongestionSettings); - - final Scenario scenario = ScenarioUtils.loadScenario(config); - Controler controler = new Controler(scenario); - - DecongestionInfo info = new DecongestionInfo(); - - // decongestion pricing - controler.addOverridingModule(new AbstractModule() { - @Override - public void install() { - - this.bind(DecongestionInfo.class).toInstance(info); - - this.bind(DecongestionTollSetting.class).to(DecongestionTollingPID.class); - this.bind(IntervalBasedTolling.class).to(IntervalBasedTollingAll.class); - - this.bind(IntervalBasedTollingAll.class).asEagerSingleton(); - this.bind(DelayAnalysis.class).asEagerSingleton(); - this.bind(PersonVehicleTracker.class).asEagerSingleton(); - - this.addEventHandlerBinding().to(IntervalBasedTollingAll.class); - this.addEventHandlerBinding().to(DelayAnalysis.class); - this.addEventHandlerBinding().to(PersonVehicleTracker.class); - - this.addControlerListenerBinding().to(DecongestionControlerListener.class); - } - }); - - controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - controler.run(); - - final int index = config.controller().getLastIteration() - config.controller().getFirstIteration(); - double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index) ; - Assert.assertEquals("Wrong average executed score. The run output seems to have changed.", -11749.431349675931, avgScore, MatsimTestUtils.EPSILON); - - System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().toString()); - Assert.assertEquals("Wrong toll in time bin 61.", 12.600000000000009, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(61), MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong toll in time bin 73.", 16.665000000000006, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(73), MatsimTestUtils.EPSILON); - } - - /** - * Tests the BangBang controller - * - */ - @Test - public final void test2() { - - System.out.println(testUtils.getPackageInputDirectory()); - - final String configFile = testUtils.getPackageInputDirectory() + "/config.xml"; - Config config = ConfigUtils.loadConfig(configFile); - - String outputDirectory = testUtils.getOutputDirectory() + "/"; - config.controller().setOutputDirectory(outputDirectory); - - final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); - decongestionSettings.setWriteOutputIteration(1); - decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); - decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); - config.addModule(decongestionSettings); - - DecongestionInfo info = new DecongestionInfo(); - - final Scenario scenario = ScenarioUtils.loadScenario(config); - Controler controler = new Controler(scenario); - - // decongestion pricing - controler.addOverridingModule(new AbstractModule() { - @Override - public void install() { - - this.bind(DecongestionInfo.class).toInstance(info); - - this.bind(DecongestionTollSetting.class).to(DecongestionTollingBangBang.class); - this.bind(IntervalBasedTolling.class).to(IntervalBasedTollingAll.class); - - this.bind(IntervalBasedTollingAll.class).asEagerSingleton(); - this.bind(DelayAnalysis.class).asEagerSingleton(); - this.bind(PersonVehicleTracker.class).asEagerSingleton(); - - this.addEventHandlerBinding().to(IntervalBasedTollingAll.class); - this.addEventHandlerBinding().to(DelayAnalysis.class); - this.addEventHandlerBinding().to(PersonVehicleTracker.class); - - this.addControlerListenerBinding().to(DecongestionControlerListener.class); - - } - }); - - controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); - controler.run(); - - final int index = config.controller().getLastIteration() - config.controller().getFirstIteration(); - double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get( index ) ; - Assert.assertEquals("Wrong average executed score. The run output seems to have changed.", -54.97929444444, avgScore, MatsimTestUtils.EPSILON); - - System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().toString()); - Assert.assertEquals("Wrong toll in time bin 61.", 13., info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(61), MatsimTestUtils.EPSILON); - Assert.assertEquals("Wrong toll in time bin 73.", 13., info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(73), MatsimTestUtils.EPSILON); - } - -} +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * copyright : (C) 2013 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.contrib.decongestion; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.matsim.analysis.ScoreStatsControlerListener.ScoreItem; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Scenario; +import org.matsim.contrib.decongestion.DecongestionConfigGroup.DecongestionApproach; +import org.matsim.contrib.decongestion.data.DecongestionInfo; +import org.matsim.contrib.decongestion.handler.DelayAnalysis; +import org.matsim.contrib.decongestion.handler.IntervalBasedTolling; +import org.matsim.contrib.decongestion.handler.IntervalBasedTollingAll; +import org.matsim.contrib.decongestion.handler.PersonVehicleTracker; +import org.matsim.contrib.decongestion.routing.TollTimeDistanceTravelDisutilityFactory; +import org.matsim.contrib.decongestion.tollSetting.DecongestionTollSetting; +import org.matsim.contrib.decongestion.tollSetting.DecongestionTollingBangBang; +import org.matsim.contrib.decongestion.tollSetting.DecongestionTollingPID; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.controler.AbstractModule; +import org.matsim.core.controler.Controler; +import org.matsim.core.controler.OutputDirectoryHierarchy; +import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.testcases.MatsimTestUtils; + +/** + * + * + * @author ikaddoura + * + */ +public class DecongestionPricingTestIT { + + @Rule + public MatsimTestUtils testUtils = new MatsimTestUtils(); + + /** + * Kp = 0.0123 + * + */ + @Test + public final void test0a() { + + System.out.println(testUtils.getPackageInputDirectory()); + + final String configFile = testUtils.getPackageInputDirectory() + "/config0.xml"; + + Config config = ConfigUtils.loadConfig(configFile); + + String outputDirectory = testUtils.getOutputDirectory() + "/"; + config.controller().setOutputDirectory(outputDirectory); + + final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); + decongestionSettings.setWriteOutputIteration(1); + decongestionSettings.setKp(0.0123); + decongestionSettings.setKd(0.0); + decongestionSettings.setKi(0.0); + decongestionSettings.setMsa(false); + decongestionSettings.setTollBlendFactor(1.0); + decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); + decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); + config.addModule(decongestionSettings); + + final Scenario scenario = ScenarioUtils.loadScenario(config); + Controler controler = new Controler(scenario); + + DecongestionInfo info = new DecongestionInfo(); + + // congestion toll computation + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { + + this.bind(DecongestionInfo.class).toInstance(info); + + this.bind(DecongestionTollSetting.class).to(DecongestionTollingPID.class); + this.bind(IntervalBasedTolling.class).to(IntervalBasedTollingAll.class); + + this.bind(IntervalBasedTollingAll.class).asEagerSingleton(); + this.bind(DelayAnalysis.class).asEagerSingleton(); + this.bind(PersonVehicleTracker.class).asEagerSingleton(); + + this.addEventHandlerBinding().to(IntervalBasedTollingAll.class); + this.addEventHandlerBinding().to(DelayAnalysis.class); + this.addEventHandlerBinding().to(PersonVehicleTracker.class); + + this.addControlerListenerBinding().to(DecongestionControlerListener.class); + + } + }); + + // toll-adjusted routing + + final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); + + controler.addOverridingModule(new AbstractModule(){ + @Override + public void install() { + this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); + } + }); + + controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + controler.run(); + + double tt0 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 6 * 3600 + 50. * 60, null, null); + double tt1 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 63, null, null); + double tt2 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 15. * 60, null, null); + + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt0, MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 150.5, tt1, MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt2, MatsimTestUtils.EPSILON); + + final int index = config.controller().getLastIteration() - config.controller().getFirstIteration(); + double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index); + Assert.assertEquals("Wrong average executed score. The tolls seem to have changed.", -33.940316666666666, avgScore, MatsimTestUtils.EPSILON); + + System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().toString()); + System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().toString()); + + Assert.assertEquals("Wrong average delay (capacity is set in a way that one of the two agents has to wait 101 sec. Thus the average is 50.5", 50.5, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().get(84), MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong toll.", 50.5 * 0.0123, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(84), MatsimTestUtils.EPSILON); + + } + + /** + * Kp = 0.0123, other syntax + * + */ + @Test + public final void test0amodified() { + + System.out.println(testUtils.getPackageInputDirectory()); + + final String configFile = testUtils.getPackageInputDirectory() + "/config0.xml"; + + Config config = ConfigUtils.loadConfig(configFile); + + String outputDirectory = testUtils.getOutputDirectory() + "/"; + config.controller().setOutputDirectory(outputDirectory); + + final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); + decongestionSettings.setWriteOutputIteration(1); + decongestionSettings.setKp(0.0123); + decongestionSettings.setKd(0.0); + decongestionSettings.setKi(0.0); + decongestionSettings.setMsa(false); + decongestionSettings.setTollBlendFactor(1.0); + decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); + decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); + config.addModule(decongestionSettings); + + final Scenario scenario = ScenarioUtils.loadScenario(config); + Controler controler = new Controler(scenario); + + // congestion toll computation + controler.addOverridingModule(new DecongestionModule(scenario)); + + // toll-adjusted routing + + final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); + + controler.addOverridingModule(new AbstractModule(){ + @Override + public void install() { + this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); + } + }); + + controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + controler.run(); + + double tt0 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 6 * 3600 + 50. * 60, null, null); + double tt1 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 63, null, null); + double tt2 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 15. * 60, null, null); + + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt0, MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 150.5, tt1, MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt2, MatsimTestUtils.EPSILON); + + final int index = config.controller().getLastIteration() - config.controller().getFirstIteration(); + double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index); + Assert.assertEquals("Wrong average executed score. The tolls seem to have changed.", -33.940316666666666, avgScore, MatsimTestUtils.EPSILON); + } + + /** + * Kp = 2 + * + */ + @Test + public final void test0b() { + + System.out.println(testUtils.getPackageInputDirectory()); + + final String configFile = testUtils.getPackageInputDirectory() + "/config0.xml"; + + Config config = ConfigUtils.loadConfig(configFile); + + String outputDirectory = testUtils.getOutputDirectory() + "/"; + config.controller().setOutputDirectory(outputDirectory); + + final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); + decongestionSettings.setWriteOutputIteration(1); + decongestionSettings.setKp(2.0); + decongestionSettings.setKd(0.0); + decongestionSettings.setKi(0.0); + decongestionSettings.setMsa(false); + decongestionSettings.setTollBlendFactor(1.0); + decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); + decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); + config.addModule(decongestionSettings); + + final Scenario scenario = ScenarioUtils.loadScenario(config); + Controler controler = new Controler(scenario); + + DecongestionInfo info = new DecongestionInfo(); + + // congestion toll computation + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { + + this.bind(DecongestionInfo.class).toInstance(info); + + this.bind(DecongestionTollSetting.class).to(DecongestionTollingPID.class); + this.bind(IntervalBasedTolling.class).to(IntervalBasedTollingAll.class); + + this.bind(IntervalBasedTollingAll.class).asEagerSingleton(); + this.bind(DelayAnalysis.class).asEagerSingleton(); + this.bind(PersonVehicleTracker.class).asEagerSingleton(); + + this.addEventHandlerBinding().to(IntervalBasedTollingAll.class); + this.addEventHandlerBinding().to(DelayAnalysis.class); + this.addEventHandlerBinding().to(PersonVehicleTracker.class); + + this.addControlerListenerBinding().to(DecongestionControlerListener.class); + + } + }); + + // toll-adjusted routing + + final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); + + controler.addOverridingModule(new AbstractModule(){ + @Override + public void install() { + this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); + } + }); + + controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + controler.run(); + + double tt0 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 6 * 3600 + 50. * 60, null, null); + double tt1 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 63, null, null); + double tt2 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 15. * 60, null, null); + + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt0, MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 150.5, tt1, MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt2, MatsimTestUtils.EPSILON); + + final int index = config.controller().getLastIteration() - config.controller().getFirstIteration(); + double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index); + Assert.assertEquals("Wrong average executed score. The tolls seem to have changed.", -134.31916666666666, avgScore, MatsimTestUtils.EPSILON); + + System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().toString()); + System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().toString()); + + Assert.assertEquals("Wrong average delay (capacity is set in a way that one of the two agents has to wait 101 sec. Thus the average is 50.5", 50.5, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().get(84), MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong toll.", 50.5 * 2, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(84), MatsimTestUtils.EPSILON); + } + + /** + * Kp = 2 + * + */ + @Test + public final void test0bmodified() { + + System.out.println(testUtils.getPackageInputDirectory()); + + final String configFile = testUtils.getPackageInputDirectory() + "/config0.xml"; + + Config config = ConfigUtils.loadConfig(configFile); + + String outputDirectory = testUtils.getOutputDirectory() + "/"; + config.controller().setOutputDirectory(outputDirectory); + + final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); + decongestionSettings.setWriteOutputIteration(1); + decongestionSettings.setKp(2.0); + decongestionSettings.setKd(0.0); + decongestionSettings.setKi(0.0); + decongestionSettings.setMsa(false); + decongestionSettings.setTollBlendFactor(1.0); + decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); + decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); + config.addModule(decongestionSettings); + + final Scenario scenario = ScenarioUtils.loadScenario(config); + Controler controler = new Controler(scenario); + + // congestion toll computation + controler.addOverridingModule(new DecongestionModule(scenario)); + + // toll-adjusted routing + + final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); + + controler.addOverridingModule(new AbstractModule(){ + @Override + public void install() { + this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); + } + }); + + controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + controler.run(); + + double tt0 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 6 * 3600 + 50. * 60, null, null); + double tt1 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 63, null, null); + double tt2 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 15. * 60, null, null); + + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt0, MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 150.5, tt1, MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt2, MatsimTestUtils.EPSILON); + + final int index = config.controller().getLastIteration() - config.controller().getFirstIteration(); + double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index); + Assert.assertEquals("Wrong average executed score. The tolls seem to have changed.", -134.31916666666666, avgScore, MatsimTestUtils.EPSILON); + } + + /** + * Kp = 0 / no tolling + * + */ + @Test + public final void test0c() { + + System.out.println(testUtils.getPackageInputDirectory()); + + final String configFile = testUtils.getPackageInputDirectory() + "/config0.xml"; + + Config config = ConfigUtils.loadConfig(configFile); + + String outputDirectory = testUtils.getOutputDirectory() + "/"; + config.controller().setOutputDirectory(outputDirectory); + + final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); + decongestionSettings.setWriteOutputIteration(1); + decongestionSettings.setKp(0.0); + decongestionSettings.setKd(0.0); + decongestionSettings.setKi(0.0); + decongestionSettings.setMsa(false); + decongestionSettings.setTollBlendFactor(1.0); + decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); + decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); + config.addModule(decongestionSettings); + + final Scenario scenario = ScenarioUtils.loadScenario(config); + Controler controler = new Controler(scenario); + + DecongestionInfo info = new DecongestionInfo(); + + // congestion toll computation + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { + + this.bind(DecongestionInfo.class).toInstance(info); + + this.bind(DelayAnalysis.class).asEagerSingleton(); + this.addEventHandlerBinding().to(DelayAnalysis.class); + + this.addControlerListenerBinding().to(DecongestionControlerListener.class); + + } + }); + + // toll-adjusted routing + + final TollTimeDistanceTravelDisutilityFactory travelDisutilityFactory = new TollTimeDistanceTravelDisutilityFactory(); + + controler.addOverridingModule(new AbstractModule(){ + @Override + public void install() { + this.bindCarTravelDisutilityFactory().toInstance( travelDisutilityFactory ); + } + }); + + controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + controler.run(); + + double tt0 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 6 * 3600 + 50. * 60, null, null); + double tt1 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 63, null, null); + double tt2 = controler.getLinkTravelTimes().getLinkTravelTime(scenario.getNetwork().getLinks().get(Id.createLinkId("link12")), 7 * 3600 + 15. * 60, null, null); + + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt0, MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 150.5, tt1, MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong travel time. The run output seems to have changed.", 100.0, tt2, MatsimTestUtils.EPSILON); + + final int index = config.controller().getLastIteration() - config.controller().getFirstIteration(); + double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index); + Assert.assertEquals("Wrong average executed score. The tolls seem to have changed.", -33.31916666666666, avgScore, MatsimTestUtils.EPSILON); + + System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().toString()); + System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().toString()); + + Assert.assertEquals("Wrong average delay (capacity is set in a way that one of the two agents has to wait 101 sec. Thus the average is 50.5", 50.5, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2avgDelay().get(84), MatsimTestUtils.EPSILON); + Assert.assertNull("Wrong toll.", info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(84)); + + } + + /** + * Tests the PID controller + * + */ + @Test + public final void test1() { + + System.out.println(testUtils.getPackageInputDirectory()); + + final String configFile = testUtils.getPackageInputDirectory() + "/config.xml"; + Config config = ConfigUtils.loadConfig(configFile); + + String outputDirectory = testUtils.getOutputDirectory() + "/"; + config.controller().setOutputDirectory(outputDirectory); + + final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); + decongestionSettings.setWriteOutputIteration(1); + decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); + decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); + decongestionSettings.setDecongestionApproach(DecongestionApproach.PID); + config.addModule(decongestionSettings); + + final Scenario scenario = ScenarioUtils.loadScenario(config); + Controler controler = new Controler(scenario); + + DecongestionInfo info = new DecongestionInfo(); + + // decongestion pricing + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { + + this.bind(DecongestionInfo.class).toInstance(info); + + this.bind(DecongestionTollSetting.class).to(DecongestionTollingPID.class); + this.bind(IntervalBasedTolling.class).to(IntervalBasedTollingAll.class); + + this.bind(IntervalBasedTollingAll.class).asEagerSingleton(); + this.bind(DelayAnalysis.class).asEagerSingleton(); + this.bind(PersonVehicleTracker.class).asEagerSingleton(); + + this.addEventHandlerBinding().to(IntervalBasedTollingAll.class); + this.addEventHandlerBinding().to(DelayAnalysis.class); + this.addEventHandlerBinding().to(PersonVehicleTracker.class); + + this.addControlerListenerBinding().to(DecongestionControlerListener.class); + } + }); + + controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + controler.run(); + + final int index = config.controller().getLastIteration() - config.controller().getFirstIteration(); + double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get(index) ; + Assert.assertEquals("Wrong average executed score. The run output seems to have changed.", -12036.177448472225, avgScore, MatsimTestUtils.EPSILON); + + System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().toString()); + Assert.assertEquals("Wrong toll in time bin 61.", 9.197000000000003, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(61), MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong toll in time bin 73.", 12.963999999999984, info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(73), MatsimTestUtils.EPSILON); + } + + /** + * Tests the BangBang controller + * + */ + @Test + public final void test2() { + + System.out.println(testUtils.getPackageInputDirectory()); + + final String configFile = testUtils.getPackageInputDirectory() + "/config.xml"; + Config config = ConfigUtils.loadConfig(configFile); + + String outputDirectory = testUtils.getOutputDirectory() + "/"; + config.controller().setOutputDirectory(outputDirectory); + + final DecongestionConfigGroup decongestionSettings = new DecongestionConfigGroup(); + decongestionSettings.setWriteOutputIteration(1); + decongestionSettings.setFractionOfIterationsToEndPriceAdjustment(1.0); + decongestionSettings.setFractionOfIterationsToStartPriceAdjustment(0.0); + config.addModule(decongestionSettings); + + DecongestionInfo info = new DecongestionInfo(); + + final Scenario scenario = ScenarioUtils.loadScenario(config); + Controler controler = new Controler(scenario); + + // decongestion pricing + controler.addOverridingModule(new AbstractModule() { + @Override + public void install() { + + this.bind(DecongestionInfo.class).toInstance(info); + + this.bind(DecongestionTollSetting.class).to(DecongestionTollingBangBang.class); + this.bind(IntervalBasedTolling.class).to(IntervalBasedTollingAll.class); + + this.bind(IntervalBasedTollingAll.class).asEagerSingleton(); + this.bind(DelayAnalysis.class).asEagerSingleton(); + this.bind(PersonVehicleTracker.class).asEagerSingleton(); + + this.addEventHandlerBinding().to(IntervalBasedTollingAll.class); + this.addEventHandlerBinding().to(DelayAnalysis.class); + this.addEventHandlerBinding().to(PersonVehicleTracker.class); + + this.addControlerListenerBinding().to(DecongestionControlerListener.class); + + } + }); + + controler.getConfig().controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + controler.run(); + + + final int index = config.controller().getLastIteration() - config.controller().getFirstIteration(); + double avgScore = controler.getScoreStats().getScoreHistory().get( ScoreItem.executed ).get( index ) ; + Assert.assertEquals("Wrong average executed score. The run output seems to have changed.", -55.215645833333184, avgScore, MatsimTestUtils.EPSILON); + + System.out.println(info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().toString()); + Assert.assertEquals("Wrong toll in time bin 61.", 13., info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(61), MatsimTestUtils.EPSILON); + Assert.assertEquals("Wrong toll in time bin 73.", 13., info.getlinkInfos().get(Id.createLinkId("link12")).getTime2toll().get(73), MatsimTestUtils.EPSILON); + } + +} diff --git a/contribs/discrete_mode_choice/src/test/java/org/matsim/contrib/discrete_mode_choice/examples/TestSiouxFalls.java b/contribs/discrete_mode_choice/src/test/java/org/matsim/contrib/discrete_mode_choice/examples/TestSiouxFalls.java index 567d5fb1fe4..7b71a32670e 100644 --- a/contribs/discrete_mode_choice/src/test/java/org/matsim/contrib/discrete_mode_choice/examples/TestSiouxFalls.java +++ b/contribs/discrete_mode_choice/src/test/java/org/matsim/contrib/discrete_mode_choice/examples/TestSiouxFalls.java @@ -56,14 +56,14 @@ public void install() { controller.run(); - assertEquals(44196, (int) listener.counts.get("pt")); - assertEquals(132316, (int) listener.counts.get("car")); - assertEquals(82140, (int) listener.counts.get("walk")); -// assertEquals(42520, (int) listener.counts.get("pt")); -// assertEquals(132100, (int) listener.counts.get("car")); -// assertEquals(79106, (int) listener.counts.get("walk")); - // ...setConstrainedModes(...) (inside configureAsSubtourModeChoiceReplacement(...)) used to ignore its arguments because of a typo. - // This is now corrected, but results are no longer backwards compatible. kai, jan'23 + + System.out.println((int) listener.counts.get("pt")); + System.out.println((int) listener.counts.get("car")); + System.out.println(listener.counts.get("walk")); + + assertEquals(44195, listener.counts.get("pt"), 2); + assertEquals(132316, listener.counts.get("car"), 2); + assertEquals(82139, listener.counts.get("walk"), 2); } diff --git a/contribs/drt-extensions/pom.xml b/contribs/drt-extensions/pom.xml index b56ab569782..2217f9ebdc7 100644 --- a/contribs/drt-extensions/pom.xml +++ b/contribs/drt-extensions/pom.xml @@ -24,6 +24,12 @@ 16.0-SNAPSHOT + + org.matsim.contrib + informed-mode-choice + 16.0-SNAPSHOT + + org.matsim.contrib simwrapper @@ -33,6 +39,7 @@ org.assertj assertj-core + test diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/BasicDrtEstimator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/BasicDrtEstimator.java new file mode 100644 index 00000000000..3d7555020e1 --- /dev/null +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/BasicDrtEstimator.java @@ -0,0 +1,172 @@ +package org.matsim.contrib.drt.extension.estimator; + +import org.apache.commons.math3.stat.descriptive.SummaryStatistics; +import org.apache.commons.math3.stat.regression.RegressionResults; +import org.apache.commons.math3.stat.regression.SimpleRegression; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.events.PersonMoneyEvent; +import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector; +import org.matsim.contrib.drt.extension.estimator.run.DrtEstimatorConfigGroup; +import org.matsim.contrib.drt.fare.DrtFareParams; +import org.matsim.contrib.drt.routing.DrtRoute; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.drt.speedup.DrtSpeedUp; +import org.matsim.core.controler.events.IterationEndsEvent; +import org.matsim.core.controler.listener.IterationEndsListener; +import org.matsim.core.utils.misc.OptionalTime; + +import java.util.SplittableRandom; + +/** + * Estimates drt trips based only daily averages. No spatial or temporal differentiation is taken into account for the estimate. + * This estimator is suited for small scenarios with few vehicles and trips and consequently few data points. + */ +public class BasicDrtEstimator implements DrtEstimator, IterationEndsListener { + + private static final Logger log = LogManager.getLogger(BasicDrtEstimator.class); + + private final DrtEventSequenceCollector collector; + private final DrtEstimatorConfigGroup config; + private final DrtConfigGroup drtConfig; + + private final SplittableRandom rnd = new SplittableRandom(); + /** + * Currently valid estimates. + */ + private GlobalEstimate currentEst; + private RegressionResults fare; + + public BasicDrtEstimator(DrtEventSequenceCollector collector, DrtEstimatorConfigGroup config, + DrtConfigGroup drtConfig) { + //zones = injector.getModal(DrtZonalSystem.class); + this.collector = collector; + this.config = config; + this.drtConfig = drtConfig; + } + + @Override + public void notifyIterationEnds(IterationEndsEvent event) { + + // Speed-up iteration need to be ignored for the estimates + if (drtConfig.getDrtSpeedUpParams().isPresent() && + DrtSpeedUp.isTeleportDrtUsers(drtConfig.getDrtSpeedUpParams().get(), + event.getServices().getConfig().controller(), event.getIteration())) { + return; + } + + GlobalEstimate est = new GlobalEstimate(); + + int n = 0; + + int nRejections = collector.getRejectedRequestSequences().size(); + int nSubmitted = collector.getRequestSubmissions().size(); + + for (DrtEventSequenceCollector.EventSequence seq : collector.getPerformedRequestSequences().values()) { + + if (seq.getPickedUp().isPresent() && seq.getDroppedOff().isPresent()) { + + double waitTime = seq.getPickedUp().get().getTime() - seq.getSubmitted().getTime(); + est.waitTime.addValue(waitTime); + + double unsharedTime = seq.getSubmitted().getUnsharedRideTime(); + double travelTime = seq.getDroppedOff().get().getTime() - seq.getPickedUp().get().getTime(); + + est.detour.addValue(travelTime / unsharedTime); + + double fare = seq.getDrtFares().stream().mapToDouble(PersonMoneyEvent::getAmount).sum(); + est.fare.addData(seq.getSubmitted().getUnsharedRideDistance(), fare); + n++; + } + } + + // At least some data points are required + if (n <= 3) + return; + + fare = est.fare.regress(); + + double rejectionRate = (double) nRejections / nSubmitted; + + if (currentEst == null) { + est.meanWait = est.waitTime.getMean(); + est.stdWait = est.waitTime.getStandardDeviation(); + est.meanDetour = est.detour.getMean(); + est.stdDetour = est.detour.getStandardDeviation(); + est.rejectionRate = rejectionRate; + } else { + est.meanWait = config.decayFactor * est.waitTime.getMean() + (1 - config.decayFactor) * currentEst.waitTime.getMean(); + est.stdWait = config.decayFactor * est.waitTime.getStandardDeviation() + (1 - config.decayFactor) * currentEst.waitTime.getStandardDeviation(); + est.meanDetour = config.decayFactor * est.detour.getMean() + (1 - config.decayFactor) * currentEst.detour.getMean(); + est.stdDetour = config.decayFactor * est.detour.getStandardDeviation() + (1 - config.decayFactor) * currentEst.detour.getStandardDeviation(); + est.rejectionRate = config.decayFactor * rejectionRate + (1 - config.decayFactor) * currentEst.rejectionRate; + } + + log.info("Calculated {}", est); + currentEst = est; + } + + @Override + public Estimate estimate(DrtRoute route, OptionalTime departureTime) { + + if (currentEst == null) { + // If not estimates are present, use travel time alpha as detour + // beta is not used, because estimates are supposed to be minimums and not worst cases + double travelTime = Math.min(route.getDirectRideTime() + drtConfig.maxAbsoluteDetour, + route.getDirectRideTime() * drtConfig.maxTravelTimeAlpha); + + double fare = 0; + if (drtConfig.getDrtFareParams().isPresent()) { + DrtFareParams fareParams = drtConfig.getDrtFareParams().get(); + fare = fareParams.distanceFare_m * route.getDistance() + + fareParams.timeFare_h * route.getDirectRideTime() / 3600.0 + + fareParams.baseFare; + + fare = Math.max(fare, fareParams.minFarePerTrip); + } + + // for distance, also use the max travel time alpha + return new Estimate(route.getDistance() * drtConfig.maxTravelTimeAlpha, travelTime, drtConfig.maxWaitTime, fare, 0); + } + + double fare = 0; + if (this.fare != null) + fare = this.fare.getParameterEstimate(0) + this.fare.getParameterEstimate(1) * route.getDistance(); + + if (drtConfig.getDrtFareParams().isPresent()) { + fare = Math.max(fare, drtConfig.getDrtFareParams().get().minFarePerTrip); + } + + double detour = Math.max(1, rnd.nextGaussian(currentEst.meanDetour, config.randomization * currentEst.stdDetour)); + double waitTime = Math.max(0, rnd.nextGaussian(currentEst.meanWait, config.randomization * currentEst.stdWait)); + + return new Estimate(route.getDistance() * detour, route.getDirectRideTime() * detour, waitTime, fare, currentEst.rejectionRate); + } + + /** + * Helper class to hold statistics. + */ + private static final class GlobalEstimate { + + private final SummaryStatistics waitTime = new SummaryStatistics(); + private final SummaryStatistics detour = new SummaryStatistics(); + private final SimpleRegression fare = new SimpleRegression(true); + + private double meanWait; + private double stdWait; + private double meanDetour; + private double stdDetour; + private double rejectionRate; + + @Override + public String toString() { + return "GlobalEstimate{" + + "meanWait=" + meanWait + + ", stdWait=" + stdWait + + ", meanDetour=" + meanDetour + + ", stdDetour=" + stdDetour + + ", rejectionRate=" + rejectionRate + + '}'; + } + } +} diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/DrtEstimateAnalyzer.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/DrtEstimateAnalyzer.java new file mode 100644 index 00000000000..2d87f7ea7b8 --- /dev/null +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/DrtEstimateAnalyzer.java @@ -0,0 +1,126 @@ +package org.matsim.contrib.drt.extension.estimator; + +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.events.PersonMoneyEvent; +import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector; +import org.matsim.contrib.drt.extension.estimator.run.DrtEstimatorConfigGroup; +import org.matsim.contrib.drt.routing.DrtRoute; +import org.matsim.core.controler.events.AfterMobsimEvent; +import org.matsim.core.controler.events.ShutdownEvent; +import org.matsim.core.controler.events.StartupEvent; +import org.matsim.core.controler.listener.AfterMobsimListener; +import org.matsim.core.controler.listener.ShutdownListener; +import org.matsim.core.controler.listener.StartupListener; +import org.matsim.core.utils.misc.OptionalTime; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +/** + * Analyzes and outputs drt estimates errors metrics based on daily requests. + */ +public final class DrtEstimateAnalyzer implements StartupListener, ShutdownListener, AfterMobsimListener { + + private static final Logger log = LogManager.getLogger(DrtEstimateAnalyzer.class); + + // Might be useful but not needed currently + //private final DefaultMainLegRouter.RouteCreator creator; + private final DrtEstimator estimator; + private final DrtEventSequenceCollector collector; + private final DrtEstimatorConfigGroup config; + + private CSVPrinter csv; + + public DrtEstimateAnalyzer(DrtEstimator estimator, DrtEventSequenceCollector collector, DrtEstimatorConfigGroup config) { + this.estimator = estimator; + this.collector = collector; + this.config = config; + } + + @Override + public void notifyStartup(StartupEvent event) { + + String filename = event.getServices().getControlerIO().getOutputFilename("drt_estimates_" + config.getMode() + ".csv"); + + try { + csv = new CSVPrinter(Files.newBufferedWriter(Path.of(filename), StandardCharsets.UTF_8), CSVFormat.DEFAULT); + csv.printRecord("iteration", + "wait_time_mae", "wait_time_err_q5", "wait_time_err_q50", "wait_time_err_q95", + "travel_time_mae", "travel_time_err_q5", "travel_time_err_q50", "travel_time_err_q95", + "fare_mae", "fare_err_q5", "fare_err_q50", "fare_err_q95" + ); + + } catch (IOException e) { + throw new UncheckedIOException("Could not open output file for estimates.", e); + } + } + + @Override + public void notifyShutdown(ShutdownEvent event) { + try { + csv.close(); + } catch (IOException e) { + log.warn("Could not close drt estimate file", e); + } + } + + /** + * Needs to run before any estimators updates. + */ + @Override + public void notifyAfterMobsim(AfterMobsimEvent event) { + + try { + csv.printRecord(calcMetrics(event.getIteration())); + csv.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Return row of metrics for the csv file. + */ + private Iterable calcMetrics(int iteration) { + + DescriptiveStatistics waitTime = new DescriptiveStatistics(); + DescriptiveStatistics travelTime = new DescriptiveStatistics(); + DescriptiveStatistics fare = new DescriptiveStatistics(); + + for (DrtEventSequenceCollector.EventSequence seq : collector.getPerformedRequestSequences().values()) { + if (seq.getPickedUp().isPresent() && seq.getDroppedOff().isPresent()) { + + // many attributes are not filled, when using the constructor + DrtRoute route = new DrtRoute(seq.getSubmitted().getFromLinkId(), seq.getSubmitted().getToLinkId()); + route.setDirectRideTime(seq.getSubmitted().getUnsharedRideTime()); + route.setDistance(seq.getSubmitted().getUnsharedRideDistance()); + + double valWaitTime = seq.getPickedUp().get().getTime() - seq.getSubmitted().getTime(); + double valTravelTime = seq.getDroppedOff().get().getTime() - seq.getPickedUp().get().getTime(); + double valFare = seq.getDrtFares().stream().mapToDouble(PersonMoneyEvent::getAmount).sum(); + + DrtEstimator.Estimate estimate = estimator.estimate(route, OptionalTime.defined(seq.getSubmitted().getTime())); + + waitTime.addValue(Math.abs(estimate.waitingTime() - valWaitTime)); + travelTime.addValue(Math.abs(estimate.travelTime() - valTravelTime)); + fare.addValue(Math.abs(estimate.fare() - valFare)); + } + } + + return List.of( + iteration, + waitTime.getMean(), waitTime.getPercentile(5), waitTime.getPercentile(50), waitTime.getPercentile(95), + travelTime.getMean(), travelTime.getPercentile(5), travelTime.getPercentile(50), travelTime.getPercentile(95), + fare.getMean(), fare.getPercentile(5), fare.getPercentile(50), fare.getPercentile(95) + ); + } + +} diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/DrtEstimator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/DrtEstimator.java new file mode 100644 index 00000000000..fef209ed211 --- /dev/null +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/DrtEstimator.java @@ -0,0 +1,35 @@ +package org.matsim.contrib.drt.extension.estimator; + +import org.matsim.contrib.drt.routing.DrtRoute; +import org.matsim.core.controler.listener.ControlerListener; +import org.matsim.core.utils.misc.OptionalTime; + +/** + * Interface to estimate a DRT service's detour, waiting time and costs. + */ +public interface DrtEstimator extends ControlerListener { + + /** + * Provide an estimate for a drt route with specific pickup and dropoff point. + * + * @param route drt route + * @param departureTime estimated departure time + * @return An {@link Estimate} instance + */ + Estimate estimate(DrtRoute route, OptionalTime departureTime); + + + /** + * Estimate for various attributes for a drt trip. + * + * @param distance travel distance in meter + * @param travelTime travel time in seconds + * @param waitingTime waiting time in seconds + * @param fare money, which is negative if the customer needs to pay it + * @param rejectionRate probability of a trip being rejected + */ + record Estimate(double distance, double travelTime, double waitingTime, double fare, double rejectionRate) { + + } + +} diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/MultiModalDrtLegEstimator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/MultiModalDrtLegEstimator.java new file mode 100644 index 00000000000..ed1771ae34f --- /dev/null +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/MultiModalDrtLegEstimator.java @@ -0,0 +1,58 @@ +package org.matsim.contrib.drt.extension.estimator; + +import com.google.inject.Inject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.population.Leg; +import org.matsim.contrib.drt.routing.DrtRoute; +import org.matsim.contrib.dvrp.run.DvrpMode; +import org.matsim.core.scoring.functions.ModeUtilityParameters; +import org.matsim.core.utils.misc.OptionalTime; +import org.matsim.modechoice.EstimatorContext; +import org.matsim.modechoice.ModeAvailability; +import org.matsim.modechoice.estimators.LegEstimator; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + + +/** + * Aggregate class for informed-mode-choice that makes sure to invoke the correct estimator for each drt mode. + */ +public class MultiModalDrtLegEstimator implements LegEstimator { + + private static final Logger log = LogManager.getLogger(MultiModalDrtLegEstimator.class); + + protected final Map estimators = new HashMap<>(); + + @Inject + public MultiModalDrtLegEstimator(Map estimators) { + for (Map.Entry e : estimators.entrySet()) { + this.estimators.put(e.getKey().value(), e.getValue()); + } + } + + @Override + public double estimate(EstimatorContext context, String mode, Leg leg, ModeAvailability option) { + + if (!(leg.getRoute() instanceof DrtRoute route)) + throw new IllegalStateException("Drt leg routes must be of type DrtRoute."); + + OptionalTime departureTime = leg.getDepartureTime(); + + DrtEstimator estimator = Objects.requireNonNull(estimators.get(mode), String.format("No drt estimator found for mode %s. Check warnings for errors.", mode)); + + DrtEstimator.Estimate est = estimator.estimate(route, departureTime); + ModeUtilityParameters params = context.scoring.modeParams.get(mode); + + // By default, waiting time is scored as travel time + return params.constant + + params.marginalUtilityOfDistance_m * est.distance() + + params.marginalUtilityOfTraveling_s * est.travelTime() + + params.marginalUtilityOfTraveling_s * est.waitingTime() + + context.scoring.marginalUtilityOfMoney * params.monetaryDistanceCostRate * est.distance() + + context.scoring.marginalUtilityOfMoney * est.fare(); + + } +} diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/run/DrtEstimatorConfigGroup.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/run/DrtEstimatorConfigGroup.java new file mode 100644 index 00000000000..107aa2eb53a --- /dev/null +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/run/DrtEstimatorConfigGroup.java @@ -0,0 +1,61 @@ +package org.matsim.contrib.drt.extension.estimator.run; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.contrib.dvrp.run.Modal; +import org.matsim.contrib.util.ReflectiveConfigGroupWithConfigurableParameterSets; + +public class DrtEstimatorConfigGroup extends ReflectiveConfigGroupWithConfigurableParameterSets implements Modal { + + /** + * Type of estimator, which will be installed in {@link DrtEstimatorModule}. + */ + public enum EstimatorType { + BASIC, + + /** + * Custom estimator, that needs to provided via binding. + */ + CUSTOM + } + + public static final String GROUP_NAME = "drtEstimator"; + + public DrtEstimatorConfigGroup() { + super(GROUP_NAME); + } + + public DrtEstimatorConfigGroup(String mode) { + super(GROUP_NAME); + this.mode = mode; + } + + @Parameter + @Comment("Mode of the drt service to estimate.") + @NotBlank + public String mode = TransportMode.drt; + + @Parameter + @Comment("Estimator typed to be used. In case of 'CUSTOM', guice bindings needs to be provided.") + @NotNull + public EstimatorType estimator = EstimatorType.BASIC; + + @Parameter + @Comment("Decay of the exponential moving average.") + @Positive + public double decayFactor = 0.7; + + @Parameter + @Comment("Factor multiplied with standard deviation to randomize estimates.") + @PositiveOrZero + public double randomization = 0.1; + + @Override + public String getMode() { + return mode; + } + +} diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/run/DrtEstimatorModule.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/run/DrtEstimatorModule.java new file mode 100644 index 00000000000..14bc2ae330d --- /dev/null +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/run/DrtEstimatorModule.java @@ -0,0 +1,80 @@ +package org.matsim.contrib.drt.extension.estimator.run; + +import com.google.inject.Singleton; +import com.google.inject.multibindings.MapBinder; +import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector; +import org.matsim.contrib.drt.extension.estimator.BasicDrtEstimator; +import org.matsim.contrib.drt.extension.estimator.DrtEstimateAnalyzer; +import org.matsim.contrib.drt.extension.estimator.DrtEstimator; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; +import org.matsim.contrib.dvrp.run.DvrpMode; +import org.matsim.contrib.dvrp.run.DvrpModes; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.controler.AbstractModule; + +import java.util.Optional; + +/** + * Main module that needs to be installed if any estimator is to be used. + */ +public class DrtEstimatorModule extends AbstractModule { + + @Override + public void install() { + + MultiModeDrtConfigGroup drtConfigs = MultiModeDrtConfigGroup.get(getConfig()); + MultiModeDrtEstimatorConfigGroup configs = ConfigUtils.addOrGetModule(getConfig(), MultiModeDrtEstimatorConfigGroup.class); + + for (DrtConfigGroup cfg : drtConfigs.getModalElements()) { + + Optional estCfg = configs.getModalElement(cfg.mode); + + estCfg.ifPresent(drtEstimatorConfigGroup -> install(new ModeModule(cfg, drtEstimatorConfigGroup))); + } + } + + static final class ModeModule extends AbstractDvrpModeModule { + + private final DrtConfigGroup cfg; + private final DrtEstimatorConfigGroup group; + + public ModeModule(DrtConfigGroup cfg, DrtEstimatorConfigGroup group) { + super(group.mode); + this.cfg = cfg; + this.group = group; + } + + @Override + public void install() { + + // try with default injections and overwrite + if (group.estimator == DrtEstimatorConfigGroup.EstimatorType.BASIC) { + bindModal(DrtEstimator.class).toProvider(modalProvider( + getter -> new BasicDrtEstimator(getter.getModal(DrtEventSequenceCollector.class), group, cfg) + )).in(Singleton.class); + } + + // DRT Estimators will be available as Map + MapBinder.newMapBinder(this.binder(), DvrpMode.class, DrtEstimator.class) + .addBinding(DvrpModes.mode(getMode())) + .to(modalKey(DrtEstimator.class)); + + addControlerListenerBinding().to(modalKey(DrtEstimator.class)); + + bindModal(DrtEstimatorConfigGroup.class).toInstance(group); + + // Needs to run before estimators + bindModal(DrtEstimateAnalyzer.class) + .toProvider( + modalProvider(getter -> new DrtEstimateAnalyzer(getter.getModal(DrtEstimator.class), getter.getModal(DrtEventSequenceCollector.class), group)) + ) + .in(Singleton.class); + + addControlerListenerBinding().to(modalKey(DrtEstimateAnalyzer.class)); + + } + + } +} diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/run/MultiModeDrtEstimatorConfigGroup.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/run/MultiModeDrtEstimatorConfigGroup.java new file mode 100644 index 00000000000..b998bf78648 --- /dev/null +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/run/MultiModeDrtEstimatorConfigGroup.java @@ -0,0 +1,103 @@ +/* + * *********************************************************************** * + * project: org.matsim.* + * *********************************************************************** * + * * + * copyright : (C) 2018 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.contrib.drt.extension.estimator.run; + +import java.util.Collection; +import java.util.Optional; +import java.util.function.Supplier; + +import org.matsim.contrib.dvrp.run.MultiModal; +import org.matsim.contrib.dvrp.run.MultiModals; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigGroup; +import org.matsim.core.config.ReflectiveConfigGroup; + +import com.google.common.base.Verify; + +/** + * @author Michal Maciejewski (michalm) + */ +public final class MultiModeDrtEstimatorConfigGroup extends ReflectiveConfigGroup implements MultiModal { + public static final String GROUP_NAME = "drtEstimators"; + + /** + * @param config + * @return MultiModeDrtConfigGroup if exists. Otherwise fails + */ + public static MultiModeDrtEstimatorConfigGroup get(Config config) { + return (MultiModeDrtEstimatorConfigGroup)config.getModule(GROUP_NAME); + } + + private final Supplier drtConfigSupplier; + + public MultiModeDrtEstimatorConfigGroup() { + this(DrtEstimatorConfigGroup::new); + } + + public MultiModeDrtEstimatorConfigGroup(Supplier drtConfigSupplier) { + super(GROUP_NAME); + this.drtConfigSupplier = drtConfigSupplier; + } + + @Override + protected void checkConsistency(Config config) { + super.checkConsistency(config); + Verify.verify(config.getModule(DrtEstimatorConfigGroup.GROUP_NAME) == null, + "In the multi-mode DRT setup, DrtEstimatorConfigGroup must not be defined at the config top level"); + MultiModals.requireAllModesUnique(this); + } + + @Override + public ConfigGroup createParameterSet(String type) { + if (type.equals(DrtEstimatorConfigGroup.GROUP_NAME)) { + return drtConfigSupplier.get(); + } else { + throw new IllegalArgumentException("Unsupported parameter set type: " + type); + } + } + + @Override + public void addParameterSet(ConfigGroup set) { + if (set instanceof DrtEstimatorConfigGroup) { + super.addParameterSet(set); + } else { + throw new IllegalArgumentException("Unsupported parameter set class: " + set); + } + } + + public void addParameterSet(DrtEstimatorConfigGroup set) { + addParameterSet((ConfigGroup) set); + } + + @Override + @SuppressWarnings("unchecked") + public Collection getModalElements() { + return (Collection)getParameterSets(DrtEstimatorConfigGroup.GROUP_NAME); + } + + /** + * Find estimator config for specific mode. + */ + public Optional getModalElement(String mode) { + return getModalElements().stream().filter(m -> m.getMode().equals(mode)).findFirst(); + } + +} diff --git a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/DrtTestScenario.java b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/DrtTestScenario.java index db01301303c..7131c33a368 100644 --- a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/DrtTestScenario.java +++ b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/DrtTestScenario.java @@ -24,12 +24,14 @@ import org.matsim.core.utils.io.IOUtils; import org.matsim.examples.ExamplesUtils; import org.matsim.simwrapper.SimWrapperModule; +import org.matsim.modechoice.InformedModeChoiceConfigGroup; import org.matsim.testcases.MatsimTestUtils; import org.matsim.vehicles.VehicleType; import javax.annotation.Nullable; import java.net.URL; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.function.Consumer; @@ -82,6 +84,8 @@ protected Config prepareConfig(Config config) { config.scoring().addActivityParams(new ScoringConfigGroup.ActivityParams("freight_start").setTypicalDuration(60 * 15)); config.scoring().addActivityParams(new ScoringConfigGroup.ActivityParams("freight_end").setTypicalDuration(60 * 15)); + InformedModeChoiceConfigGroup imc = ConfigUtils.addOrGetModule(config, InformedModeChoiceConfigGroup.class); + imc.setModes(Set.of("drt", "av", "car", "pt", "bike", "walk")); MultiModeDrtConfigGroup multiModeDrtConfig = ConfigUtils.addOrGetModule(config, MultiModeDrtConfigGroup.class); ConfigUtils.addOrGetModule(config, DvrpConfigGroup.class); diff --git a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/estimator/MultiModalDrtLegEstimatorTest.java b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/estimator/MultiModalDrtLegEstimatorTest.java new file mode 100644 index 00000000000..8d61ef68123 --- /dev/null +++ b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/estimator/MultiModalDrtLegEstimatorTest.java @@ -0,0 +1,90 @@ +package org.matsim.contrib.drt.extension.estimator; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.matsim.application.MATSimApplication; +import org.matsim.contrib.drt.extension.DrtTestScenario; +import org.matsim.contrib.drt.extension.estimator.run.DrtEstimatorConfigGroup; +import org.matsim.contrib.drt.extension.estimator.run.DrtEstimatorModule; +import org.matsim.contrib.drt.extension.estimator.run.MultiModeDrtEstimatorConfigGroup; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.config.groups.ReplanningConfigGroup; +import org.matsim.core.controler.Controler; +import org.matsim.modechoice.InformedModeChoiceModule; +import org.matsim.modechoice.ModeOptions; +import org.matsim.modechoice.estimators.DefaultLegScoreEstimator; +import org.matsim.modechoice.estimators.FixedCostsEstimator; +import org.matsim.testcases.MatsimTestUtils; + +import java.io.File; +import java.util.List; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MultiModalDrtLegEstimatorTest { + + @Rule + public MatsimTestUtils utils = new MatsimTestUtils(); + + private Controler controler; + + @Before + public void setUp() throws Exception { + + Config config = DrtTestScenario.loadConfig(utils); + + config.controller().setLastIteration(3); + + controler = MATSimApplication.prepare(new DrtTestScenario(MultiModalDrtLegEstimatorTest::prepare, MultiModalDrtLegEstimatorTest::prepare), config); + } + + private static void prepare(Controler controler) { + InformedModeChoiceModule.Builder builder = InformedModeChoiceModule.newBuilder() + .withFixedCosts(FixedCostsEstimator.DailyConstant.class, "car") + .withLegEstimator(DefaultLegScoreEstimator.class, ModeOptions.AlwaysAvailable.class, "bike", "walk", "pt") + .withLegEstimator(DefaultLegScoreEstimator.class, ModeOptions.ConsiderYesAndNo.class, "car") + .withLegEstimator(MultiModalDrtLegEstimator.class, ModeOptions.AlwaysAvailable.class, "drt", "av"); + + controler.addOverridingModule(builder.build()); + controler.addOverridingModule(new DrtEstimatorModule()); + } + + private static void prepare(Config config) { + + MultiModeDrtEstimatorConfigGroup estimators = ConfigUtils.addOrGetModule(config, MultiModeDrtEstimatorConfigGroup.class); + + estimators.addParameterSet(new DrtEstimatorConfigGroup("drt")); + estimators.addParameterSet(new DrtEstimatorConfigGroup("av")); + + // Set subtour mode selection as strategy + List strategies = config.replanning().getStrategySettings().stream() + .filter(s -> !s.getStrategyName().toLowerCase().contains("mode") + ).collect(Collectors.toList()); + + strategies.add(new ReplanningConfigGroup.StrategySettings() + .setStrategyName(InformedModeChoiceModule.SELECT_SUBTOUR_MODE_STRATEGY) + .setSubpopulation("person") + .setWeight(0.2)); + + config.replanning().clearStrategySettings(); + strategies.forEach(s -> config.replanning().addStrategySettings(s)); + + } + + @Test + public void run() { + + String out = utils.getOutputDirectory(); + + controler.run(); + + assertThat(new File(out, "kelheim-mini-drt.drt_estimates_drt.csv")) + .exists() + .isNotEmpty(); + + + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/speedup/DrtTeleportedRouteCalculator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/speedup/DrtTeleportedRouteCalculator.java index 7935a2403e8..eca53f0c0d7 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/speedup/DrtTeleportedRouteCalculator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/speedup/DrtTeleportedRouteCalculator.java @@ -40,6 +40,12 @@ public class DrtTeleportedRouteCalculator implements TeleportedRouteCalculator { this.averageInVehicleBeelineSpeed = averageInVehicleBeelineSpeed; } + // TODO: from discussion from michal and rakow + // speedup is currently using very simple and not exchangeable estimators + // it could be possible to integrate the drt estimators used by the informed mode-choice + // this router should probably not use the beeline distance but the direct travel route + // speed-up would still be significant (oct'23) + @Override public Route calculateRoute(PassengerRequest request) { Link startLink = request.getFromLink(); diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/vrpagent/VrpAgentQueryHelper.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/vrpagent/VrpAgentQueryHelper.java index abdcc4521b3..68d6e327ed5 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/vrpagent/VrpAgentQueryHelper.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/vrpagent/VrpAgentQueryHelper.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; +import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.TransportMode; import org.matsim.api.core.v01.population.Activity; import org.matsim.api.core.v01.population.Leg; @@ -158,6 +159,36 @@ public String getType() { public void setType(String type) { throw new UnsupportedOperationException(); } + + @Override + public int getIterationCreated() { + throw new UnsupportedOperationException(); + } + + @Override + public void setIterationCreated(int iteration) { + throw new UnsupportedOperationException(); + } + + @Override + public Id getId() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPlanId(Id planId) { + throw new UnsupportedOperationException(); + } + + @Override + public String getPlanMutator() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPlanMutator(String planMutator) { + throw new UnsupportedOperationException(); + } @Override public void setScore(Double score) { diff --git a/contribs/freight/test/input/org/matsim/freight/carriers/usecases/chessboard/RunChessboardIT/runChessboard/output_events.xml.gz b/contribs/freight/test/input/org/matsim/freight/carriers/usecases/chessboard/RunChessboardIT/runChessboard/output_events.xml.gz index cd5ffc78e82..8057a55fbcc 100644 Binary files a/contribs/freight/test/input/org/matsim/freight/carriers/usecases/chessboard/RunChessboardIT/runChessboard/output_events.xml.gz and b/contribs/freight/test/input/org/matsim/freight/carriers/usecases/chessboard/RunChessboardIT/runChessboard/output_events.xml.gz differ diff --git a/contribs/freight/test/input/org/matsim/freight/carriers/usecases/chessboard/RunChessboardIT/runChessboard/output_plans.xml.gz b/contribs/freight/test/input/org/matsim/freight/carriers/usecases/chessboard/RunChessboardIT/runChessboard/output_plans.xml.gz index f2c6b3d4e47..3ca1e52a85a 100644 Binary files a/contribs/freight/test/input/org/matsim/freight/carriers/usecases/chessboard/RunChessboardIT/runChessboard/output_plans.xml.gz and b/contribs/freight/test/input/org/matsim/freight/carriers/usecases/chessboard/RunChessboardIT/runChessboard/output_plans.xml.gz differ diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/InformedModeChoiceModule.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/InformedModeChoiceModule.java index d151624afe2..a6229390386 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/InformedModeChoiceModule.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/InformedModeChoiceModule.java @@ -167,7 +167,7 @@ public > Builder withFixedCosts(Class> Builder withLegEstimator(Class> estimator, Class> option, + public > Builder withLegEstimator(Class> estimator, Class> option, String... modes) { for (String mode : modes) { diff --git a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/PlanModelService.java b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/PlanModelService.java index 4930197c262..93c2ca21a0f 100644 --- a/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/PlanModelService.java +++ b/contribs/informed-mode-choice/src/main/java/org/matsim/modechoice/PlanModelService.java @@ -157,6 +157,7 @@ public List allowedModes(PlanModel planModel) { /** * Calculate the estimates for all options. Note that plan model has to be routed before computing estimates. */ + @SuppressWarnings("rawtypes") public void calculateEstimates(EstimatorContext context, PlanModel planModel) { for (Map.Entry> e : planModel.getEstimates().entrySet()) { @@ -208,7 +209,7 @@ public void calculateEstimates(EstimatorContext context, PlanModel planModel) { if (tripEst != null && legMode.equals(c.getMode())) continue; - LegEstimator> legEst = (LegEstimator>) legEstimators.get(legMode); + LegEstimator legEst = legEstimators.get(legMode); if (legEst == null) throw new IllegalStateException("No leg estimator defined for mode: " + legMode); diff --git a/contribs/locationchoice/src/main/java/org/matsim/contrib/locationchoice/zzunused/LCPlan.java b/contribs/locationchoice/src/main/java/org/matsim/contrib/locationchoice/zzunused/LCPlan.java index 75da3189c3a..f1ba43fd0f5 100644 --- a/contribs/locationchoice/src/main/java/org/matsim/contrib/locationchoice/zzunused/LCPlan.java +++ b/contribs/locationchoice/src/main/java/org/matsim/contrib/locationchoice/zzunused/LCPlan.java @@ -285,4 +285,35 @@ else if (planElement instanceof Leg) { destPlan.score = srcPlan.getScore(); } + @Override + public void setPlanId(Id planId) { + throw new UnsupportedOperationException(); + + } + + @Override + public Id getId() { + throw new UnsupportedOperationException(); + } + + @Override + public int getIterationCreated() { + throw new UnsupportedOperationException(); + } + + @Override + public void setIterationCreated(int iteration) { + throw new UnsupportedOperationException(); + } + + @Override + public String getPlanMutator() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPlanMutator(String planMutator) { + throw new UnsupportedOperationException(); + } + } diff --git a/contribs/pseudosimulation/src/main/java/org/matsim/contrib/pseudosimulation/distributed/plans/PlanGenome.java b/contribs/pseudosimulation/src/main/java/org/matsim/contrib/pseudosimulation/distributed/plans/PlanGenome.java index 12cd0a7023f..33ee42fbc42 100644 --- a/contribs/pseudosimulation/src/main/java/org/matsim/contrib/pseudosimulation/distributed/plans/PlanGenome.java +++ b/contribs/pseudosimulation/src/main/java/org/matsim/contrib/pseudosimulation/distributed/plans/PlanGenome.java @@ -219,6 +219,24 @@ public String getType() { public void setType(final String type) { this.type = type; } + + @Override + public Id getId() { return null; } + + @Override + public void setPlanId(Id planId) { /* nothing to do here */ } + + @Override + public int getIterationCreated() { return -1; } + + @Override + public void setIterationCreated(int iteration) { /* nothing to do here */ } + + @Override + public String getPlanMutator() { return null; } + + @Override + public void setPlanMutator(String planMutator) { /* nothing to do here */ } @Override public final List getPlanElements() { diff --git a/contribs/pseudosimulation/src/test/java/org/matsim/contrib/pseudosimulation/RunPSimTest.java b/contribs/pseudosimulation/src/test/java/org/matsim/contrib/pseudosimulation/RunPSimTest.java index 08c4ca3cba5..2a82fc13096 100644 --- a/contribs/pseudosimulation/src/test/java/org/matsim/contrib/pseudosimulation/RunPSimTest.java +++ b/contribs/pseudosimulation/src/test/java/org/matsim/contrib/pseudosimulation/RunPSimTest.java @@ -1,10 +1,6 @@ package org.matsim.contrib.pseudosimulation; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.Assert; @@ -29,15 +25,18 @@ import org.matsim.core.replanning.strategies.DefaultPlanStrategiesModule; import org.matsim.core.utils.io.IOUtils; import org.matsim.examples.ExamplesUtils; -import org.matsim.pt.config.TransitConfigGroup.TransitRoutingAlgorithmType; import org.matsim.testcases.MatsimTestUtils; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class RunPSimTest { @Rule public MatsimTestUtils utils = new MatsimTestUtils(); - private Logger logger = LogManager.getLogger(RunPSimTest.class ); + private final Logger logger = LogManager.getLogger(RunPSimTest.class ); private final Config config = ConfigUtils.loadConfig(IOUtils.extendUrl(ExamplesUtils.getTestScenarioURL("pt-tutorial" ),"0.config.xml" ) ); @@ -57,10 +56,7 @@ public void testA() { //identify selector strategies Field[] selectors = DefaultPlanStrategiesModule.DefaultSelector.class.getDeclaredFields(); List selectorNames = new ArrayList<>(); -// for (Field selector : selectors) { -// selectorNames.add(selector.toString()); -// logger.warn( selector.toString() ); -// } + // yyyyyy does not work as designed selectorNames.add( DefaultPlanStrategiesModule.DefaultSelector.ChangeExpBeta ); selectorNames.add( DefaultPlanStrategiesModule.DefaultSelector.BestScore ); @@ -107,7 +103,8 @@ public void install() { Population popActual = PopulationUtils.createPopulation( config ); PopulationUtils.readPopulation( popActual, outDir + "/output_plans.xml.gz" ); new PopulationComparison().compare( popExpected, popActual ) ; - Assert.assertEquals("RunPsim score changed.", 138.88788052033888, psimScore, MatsimTestUtils.EPSILON); + Assert.assertEquals("RunPsim score changed.", 138.86084460860525, psimScore, MatsimTestUtils.EPSILON); + } /** @@ -130,8 +127,9 @@ public void testB() { controler.run(); double qsimScore = execScoreTracker.executedScore; - logger.info("Default controller score was " + qsimScore ); - Assert.assertEquals("Default controller score changed.", 131.85545404187428, qsimScore, MatsimTestUtils.EPSILON); + logger.info("Default controler score was " + qsimScore ); +// Assert.assertEquals("Default controler score changed.", 131.84309487251033d, qsimScore, MatsimTestUtils.EPSILON); + Assert.assertEquals("Default controler score changed.", 131.8303325803256, qsimScore, MatsimTestUtils.EPSILON); } class ExecScoreTracker implements ShutdownListener { diff --git a/contribs/shared_mobility/src/test/java/org/matsim/contrib/shared_mobility/RunIT.java b/contribs/shared_mobility/src/test/java/org/matsim/contrib/shared_mobility/RunIT.java index 975e64b1a88..4afd46b468d 100644 --- a/contribs/shared_mobility/src/test/java/org/matsim/contrib/shared_mobility/RunIT.java +++ b/contribs/shared_mobility/src/test/java/org/matsim/contrib/shared_mobility/RunIT.java @@ -157,18 +157,18 @@ public final void test() throws UncheckedIOException, ConfigurationException, UR OutputData data = countLegs(controller.getControlerIO().getOutputPath() + "/output_events.xml.gz"); - Assert.assertEquals(82689, (long) data.counts.get("car")); - Assert.assertEquals(29890, (long) data.counts.get("walk")); - Assert.assertEquals(30, (long) data.counts.get("bike")); - Assert.assertEquals(19115, (long) data.counts.get("pt")); + Assert.assertEquals(82629, (long) data.counts.get("car")); + Assert.assertEquals(29739, (long) data.counts.get("walk")); + Assert.assertEquals(31, (long) data.counts.get("bike")); + Assert.assertEquals(19029, (long) data.counts.get("pt")); Assert.assertEquals(21, (long) data.pickupCounts.get("wheels")); Assert.assertEquals(2, (long) data.pickupCounts.get("mobility")); - Assert.assertEquals(9, (long) data.pickupCounts.get("velib")); + Assert.assertEquals(10, (long) data.pickupCounts.get("velib")); Assert.assertEquals(21, (long) data.dropoffCounts.get("wheels")); Assert.assertEquals(0, (long) data.dropoffCounts.getOrDefault("mobility", 0L)); - Assert.assertEquals(9, (long) data.dropoffCounts.get("velib")); + Assert.assertEquals(10, (long) data.dropoffCounts.get("velib")); Assert.assertEquals(0, (long) data.failedPickupCounts.getOrDefault("wheels",0L)); Assert.assertEquals(0, (long) data.failedPickupCounts.getOrDefault("mobility",0L)); diff --git a/contribs/signals/src/test/resources/test/input/org/matsim/contrib/signals/integration/SignalSystemsIT/testSignalSystems/10.events.xml.gz b/contribs/signals/src/test/resources/test/input/org/matsim/contrib/signals/integration/SignalSystemsIT/testSignalSystems/10.events.xml.gz index c7ef25fe7c6..3493cb13c84 100644 Binary files a/contribs/signals/src/test/resources/test/input/org/matsim/contrib/signals/integration/SignalSystemsIT/testSignalSystems/10.events.xml.gz and b/contribs/signals/src/test/resources/test/input/org/matsim/contrib/signals/integration/SignalSystemsIT/testSignalSystems/10.events.xml.gz differ diff --git a/contribs/signals/src/test/resources/test/input/org/matsim/contrib/signals/integration/SignalSystemsIT/testSignalSystems/10.plans.xml.gz b/contribs/signals/src/test/resources/test/input/org/matsim/contrib/signals/integration/SignalSystemsIT/testSignalSystems/10.plans.xml.gz index 9b71cf15cb5..2b90066faf5 100644 Binary files a/contribs/signals/src/test/resources/test/input/org/matsim/contrib/signals/integration/SignalSystemsIT/testSignalSystems/10.plans.xml.gz and b/contribs/signals/src/test/resources/test/input/org/matsim/contrib/signals/integration/SignalSystemsIT/testSignalSystems/10.plans.xml.gz differ diff --git a/contribs/signals/src/test/resources/test/input/org/matsim/contrib/signals/integration/SignalSystemsIT/testSignalSystemsWTryEndTimeThenDuration/10.events.xml.gz b/contribs/signals/src/test/resources/test/input/org/matsim/contrib/signals/integration/SignalSystemsIT/testSignalSystemsWTryEndTimeThenDuration/10.events.xml.gz index e2ea8e8111c..3493cb13c84 100644 Binary files a/contribs/signals/src/test/resources/test/input/org/matsim/contrib/signals/integration/SignalSystemsIT/testSignalSystemsWTryEndTimeThenDuration/10.events.xml.gz and b/contribs/signals/src/test/resources/test/input/org/matsim/contrib/signals/integration/SignalSystemsIT/testSignalSystemsWTryEndTimeThenDuration/10.events.xml.gz differ diff --git a/contribs/signals/src/test/resources/test/input/org/matsim/contrib/signals/integration/SignalSystemsIT/testSignalSystemsWTryEndTimeThenDuration/10.plans.xml.gz b/contribs/signals/src/test/resources/test/input/org/matsim/contrib/signals/integration/SignalSystemsIT/testSignalSystemsWTryEndTimeThenDuration/10.plans.xml.gz index d66ff6e80af..d8733aca4db 100644 Binary files a/contribs/signals/src/test/resources/test/input/org/matsim/contrib/signals/integration/SignalSystemsIT/testSignalSystemsWTryEndTimeThenDuration/10.plans.xml.gz and b/contribs/signals/src/test/resources/test/input/org/matsim/contrib/signals/integration/SignalSystemsIT/testSignalSystemsWTryEndTimeThenDuration/10.plans.xml.gz differ diff --git a/contribs/socnetsim/src/main/java/org/matsim/contrib/socnetsim/framework/population/PlanWithCachedJointPlan.java b/contribs/socnetsim/src/main/java/org/matsim/contrib/socnetsim/framework/population/PlanWithCachedJointPlan.java index c1def957531..5d5990a7237 100644 --- a/contribs/socnetsim/src/main/java/org/matsim/contrib/socnetsim/framework/population/PlanWithCachedJointPlan.java +++ b/contribs/socnetsim/src/main/java/org/matsim/contrib/socnetsim/framework/population/PlanWithCachedJointPlan.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Map; +import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.population.Activity; import org.matsim.api.core.v01.population.Leg; import org.matsim.api.core.v01.population.Person; @@ -172,6 +173,24 @@ public String getType() { public void setType(String type) { this.delegate.setType(type); } + + @Override + public Id getId() { return null; } + + @Override + public void setPlanId(Id planId) { /* nothing to do here */ } + + @Override + public int getIterationCreated() { return -1; } + + @Override + public void setIterationCreated(int iteration) { /* nothing to do here */ } + + @Override + public String getPlanMutator() { return null; } + + @Override + public void setPlanMutator(String planMutator) { /* nothing to do here */ } @Override public Person getPerson() { diff --git a/contribs/vsp/pom.xml b/contribs/vsp/pom.xml index 96a0cd8ca37..99614d63e78 100644 --- a/contribs/vsp/pom.xml +++ b/contribs/vsp/pom.xml @@ -174,12 +174,12 @@ org.openjfx javafx-graphics - 21 + 21.0.1 com.graphhopper graphhopper-core - 7.0 + 8.0 diff --git a/matsim/src/main/java/org/matsim/api/core/v01/population/Plan.java b/matsim/src/main/java/org/matsim/api/core/v01/population/Plan.java index e06412da59e..6329b62a626 100644 --- a/matsim/src/main/java/org/matsim/api/core/v01/population/Plan.java +++ b/matsim/src/main/java/org/matsim/api/core/v01/population/Plan.java @@ -23,6 +23,8 @@ import java.util.List; import org.matsim.api.core.v01.Customizable; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Identifiable; import org.matsim.core.api.internal.MatsimPopulationObject; import org.matsim.utils.objectattributes.attributable.Attributable; @@ -34,7 +36,7 @@ * The only thing which is not "expected" in the same sense is the score. * */ -public interface Plan extends MatsimPopulationObject, Customizable, BasicPlan, Attributable { +public interface Plan extends MatsimPopulationObject, Customizable, BasicPlan, Attributable, Identifiable { public abstract List getPlanElements(); @@ -49,6 +51,18 @@ public interface Plan extends MatsimPopulationObject, Customizable, BasicPlan, A public abstract String getType(); public abstract void setType(final String type); + + public abstract void setPlanId(Id planId); + + public abstract Id getId(); + + public abstract int getIterationCreated(); + + public abstract void setIterationCreated(int iteration); + + public abstract String getPlanMutator(); + + public abstract void setPlanMutator(String planMutator); public abstract Person getPerson(); @@ -59,5 +73,6 @@ public interface Plan extends MatsimPopulationObject, Customizable, BasicPlan, A * you are using this method!. */ public abstract void setPerson(Person person); + } diff --git a/matsim/src/main/java/org/matsim/core/config/Config.java b/matsim/src/main/java/org/matsim/core/config/Config.java index ecff0be5678..e6ee24c19be 100644 --- a/matsim/src/main/java/org/matsim/core/config/Config.java +++ b/matsim/src/main/java/org/matsim/core/config/Config.java @@ -45,10 +45,10 @@ import org.matsim.core.config.groups.HouseholdsConfigGroup; import org.matsim.core.config.groups.LinkStatsConfigGroup; import org.matsim.core.config.groups.NetworkConfigGroup; +import org.matsim.core.config.groups.PlanInheritanceConfigGroup; import org.matsim.core.config.groups.ReplanningConfigGroup; import org.matsim.core.config.groups.ScoringConfigGroup; -import org.matsim.core.config.groups.RoutingConfigGroup; -import org.matsim.core.config.groups.PlansConfigGroup; +import org.matsim.core.config.groups.RoutingConfigGroup;import org.matsim.core.config.groups.PlansConfigGroup; import org.matsim.core.config.groups.QSimConfigGroup; import org.matsim.core.config.groups.ScenarioConfigGroup; import org.matsim.core.config.groups.SubtourModeChoiceConfigGroup; @@ -123,7 +123,6 @@ public void addCoreModules() { this.modules.put(QSimConfigGroup.GROUP_NAME, new QSimConfigGroup()); this.modules.put(CountsConfigGroup.GROUP_NAME, new CountsConfigGroup()); - this.modules.put(ScoringConfigGroup.GROUP_NAME, new ScoringConfigGroup()); this.modules.put(NetworkConfigGroup.GROUP_NAME, new NetworkConfigGroup()); @@ -147,6 +146,7 @@ public void addCoreModules() { this.modules.put(TimeAllocationMutatorConfigGroup.GROUP_NAME, new TimeAllocationMutatorConfigGroup()); this.modules.put(VspExperimentalConfigGroup.GROUP_NAME, new VspExperimentalConfigGroup()); + this.modules.put(TransitConfigGroup.GROUP_NAME, new TransitConfigGroup()); @@ -168,6 +168,8 @@ public void addCoreModules() { this.modules.put(HermesConfigGroup.NAME, new HermesConfigGroup()); this.modules.put(ReplanningAnnealerConfigGroup.GROUP_NAME, new ReplanningAnnealerConfigGroup()); + + this.modules.put(PlanInheritanceConfigGroup.GROUP_NAME, new PlanInheritanceConfigGroup()); this.addConfigConsistencyChecker(new VspConfigConsistencyCheckerImpl()); this.addConfigConsistencyChecker(new UnmaterializedConfigGroupChecker()); @@ -488,6 +490,10 @@ public HermesConfigGroup hermes() { public ReplanningAnnealerConfigGroup replanningAnnealer() { return (ReplanningAnnealerConfigGroup) this.getModule(ReplanningAnnealerConfigGroup.GROUP_NAME); } + + public PlanInheritanceConfigGroup planInheritance() { + return (PlanInheritanceConfigGroup) this.getModule(PlanInheritanceConfigGroup.GROUP_NAME); + } // other: diff --git a/matsim/src/main/java/org/matsim/core/config/groups/PlanInheritanceConfigGroup.java b/matsim/src/main/java/org/matsim/core/config/groups/PlanInheritanceConfigGroup.java new file mode 100644 index 00000000000..b889df2fa15 --- /dev/null +++ b/matsim/src/main/java/org/matsim/core/config/groups/PlanInheritanceConfigGroup.java @@ -0,0 +1,59 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * copyright : (C) 2011 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.core.config.groups; + +import java.util.Map; + +import org.matsim.core.config.ReflectiveConfigGroup; + +/** + * @author awagner + */ +public final class PlanInheritanceConfigGroup extends ReflectiveConfigGroup { + + public static final String GROUP_NAME = "planInheritance"; + + private static final String ENABLED = "enabled"; + + private boolean enabled = false; + + public PlanInheritanceConfigGroup() { + super(GROUP_NAME); + } + + @Override + public Map getComments() { + Map comments = super.getComments(); + comments.put(ENABLED, "Specifies whether or not PlanInheritance Information should be tracked."); + return comments; + } + + + @StringSetter( ENABLED ) + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + + @StringGetter( ENABLED ) + public boolean getEnabled() { + return this.enabled; + } +} diff --git a/matsim/src/main/java/org/matsim/core/config/groups/TimeAllocationMutatorConfigGroup.java b/matsim/src/main/java/org/matsim/core/config/groups/TimeAllocationMutatorConfigGroup.java index f9e7d9b2718..1a4b33e942e 100644 --- a/matsim/src/main/java/org/matsim/core/config/groups/TimeAllocationMutatorConfigGroup.java +++ b/matsim/src/main/java/org/matsim/core/config/groups/TimeAllocationMutatorConfigGroup.java @@ -25,11 +25,13 @@ import org.matsim.core.config.ConfigGroup; import org.matsim.core.config.ReflectiveConfigGroup; +import org.matsim.core.utils.misc.Time; public final class TimeAllocationMutatorConfigGroup extends ReflectiveConfigGroup { public static final String GROUP_NAME = "timeAllocationMutator"; + public TimeAllocationMutatorConfigGroup() { super(GROUP_NAME); } @@ -39,15 +41,49 @@ public Map getComments() { Map comments = super.getComments(); comments.put(MUTATION_RANGE, "Default:1800.0; Defines how many seconds a time mutation can maximally shift a time."); comments.put(MUTATION_AFFECTS_DURATION, "Default:true; Defines whether time mutation changes an activity's duration."); - comments.put(USE_INDIVIDUAL_SETTINGS_FOR_SUBPOPULATIONS, "false; Use individual settings for each subpopulation. " - + "If enabled but no settings are found, regular settings are uses as fallback."); + comments.put(LATEST_ACTIVITY_END_TIME,"Latest Activity End Time. Default = 24:00:00"); + comments.put(MUTATION_RANGE_STEP,"Mutation Range Step, default = 1 second"); + comments.put(MUTATE_AROUND_INITIAL_END_TIME_ONLY,"Mutates times only around the initially defined end times."); return comments; } // --- + private static final String LATEST_ACTIVITY_END_TIME = "latestActivityEndTime"; + private static final String MUTATE_AROUND_INITIAL_END_TIME_ONLY = "mutateAroundInitialEndTimeOnly"; + private static final String MUTATION_RANGE_STEP = "mutationRangeStep"; private static final String MUTATION_RANGE = "mutationRange"; private double mutationRange = 1800.0; + private double latestActivityEndTime = 24*3600.0; + private boolean mutateAroundInitialEndTimeOnly = false; + private double mutationRangeStep = 1.0; + + @StringGetter(MUTATION_RANGE_STEP) + public double getMutationRangeStep() { + return mutationRangeStep; + } + @StringSetter(MUTATION_RANGE_STEP) + public void setMutationRangeStep(double mutationRangeStep) { + this.mutationRangeStep = mutationRangeStep; + } + @StringGetter(LATEST_ACTIVITY_END_TIME) + public double getLatestActivityEndTime() { + return latestActivityEndTime; + } + @StringSetter(LATEST_ACTIVITY_END_TIME) + public void setLatestActivityEndTime(String latestActivityEndTime) { + this.latestActivityEndTime = Time.parseTime(latestActivityEndTime); + } + + @StringGetter(MUTATE_AROUND_INITIAL_END_TIME_ONLY) + public boolean isMutateAroundInitialEndTimeOnly() { + return mutateAroundInitialEndTimeOnly; + } + @StringSetter(MUTATE_AROUND_INITIAL_END_TIME_ONLY) + public void setMutateAroundInitialEndTimeOnly(boolean mutateAroundInitialEndTimeOnly) { + this.mutateAroundInitialEndTimeOnly = mutateAroundInitialEndTimeOnly; + } + @StringGetter(MUTATION_RANGE) public double getMutationRange() { return this.mutationRange; @@ -71,112 +107,4 @@ public void setAffectingDuration(boolean affectingDuration) { } // --- - - private static final String USE_INDIVIDUAL_SETTINGS_FOR_SUBPOPULATIONS = "useIndividualSettingsForSubpopulations"; - private boolean useIndividualSettingsForSubpopulations = false; -// @StringGetter(USE_INDIVIDUAL_SETTINGS_FOR_SUBPOPULATIONS) // I am disabling the xml usage for the time being, see comment below. kai, may'19 - public boolean isUseIndividualSettingsForSubpopulations() { - // yyyy is it really so plausible to have this? If we continue in this direction, we will eventually have the PTV behaviorally homogeneous - // groups. I would find it more natural to have behavior that depends continuously on person type, and then use Java in order to define that - // behaviour. Rather than to overload the config mechanism. kai, may'19 - return this.useIndividualSettingsForSubpopulations; - } -// @StringSetter(USE_INDIVIDUAL_SETTINGS_FOR_SUBPOPULATIONS) // I am disabling the xml usage for the time being, see comments. kai, may'19 - public void setUseIndividualSettingsForSubpopulations(boolean useIndividualSettingsForSubpopulations) { - // yyyy see comment under isUseIndividualSettingsForSubpopulations. kai, may'10 - this.useIndividualSettingsForSubpopulations = useIndividualSettingsForSubpopulations; - } - - // --- - - public TimeAllocationMutatorSubpopulationSettings getTimeAllocationMutatorSubpopulationSettings(String subpopulation) { - - if (subpopulation == null) return null; - - Collection configGroups = this.getParameterSets(TimeAllocationMutatorSubpopulationSettings.SET_NAME); - for (ConfigGroup group : configGroups) { - if (group instanceof TimeAllocationMutatorSubpopulationSettings) { - TimeAllocationMutatorSubpopulationSettings subpopulationSettings = (TimeAllocationMutatorSubpopulationSettings) group; - if (subpopulation.equals(subpopulationSettings.subpopulation)) return subpopulationSettings; - } - } - - return null; - } - - @Override - public ConfigGroup createParameterSet(final String type) { - switch (type) { - case TimeAllocationMutatorSubpopulationSettings.SET_NAME: - return new TimeAllocationMutatorSubpopulationSettings(); - default: - throw new IllegalArgumentException("unknown set type '" + type + "'"); - } - } - - @Override - public void addParameterSet(final ConfigGroup set) { - switch (set.getName()) { - case TimeAllocationMutatorSubpopulationSettings.SET_NAME: - super.addParameterSet(set); - break; - default: - throw new IllegalArgumentException( set.getName() ); - } - } - - public static class TimeAllocationMutatorSubpopulationSettings extends ReflectiveConfigGroup { - - public static final String SET_NAME = "subpopulationSettings"; - private static final String MUTATION_RANGE = "mutationRange"; - private static final String MUTATION_AFFECTS_DURATION = "mutationAffectsDuration"; - private static final String SUBPOPULATION = "subpopulation"; - - private double mutationRange = 1800.0; - private boolean affectingDuration = true; - private String subpopulation = null; - - public TimeAllocationMutatorSubpopulationSettings() { - super(SET_NAME); - } - - @Override - public final Map getComments() { - Map comments = super.getComments(); - comments.put(MUTATION_RANGE, "Default:1800.0; Defines how many seconds a time mutation can maximally shift a time."); - comments.put(MUTATION_AFFECTS_DURATION, "Default:true; Defines whether time mutation changes an activity's duration."); - comments.put(SUBPOPULATION, "Subpopulation to which the values from this parameter set are applied."); - return comments; - } - - @StringGetter(MUTATION_RANGE) - public double getMutationRange() { - return this.mutationRange; - } - - @StringSetter(MUTATION_RANGE) - public void setMutationRange(final double val) { - this.mutationRange = val; - } - - @StringGetter(MUTATION_AFFECTS_DURATION) - public boolean isAffectingDuration() { - return affectingDuration; - } - - @StringSetter(MUTATION_AFFECTS_DURATION) - public void setAffectingDuration(boolean affectingDuration) { - this.affectingDuration = affectingDuration; - } - - @StringSetter(SUBPOPULATION) - public void setSubpopulation(final String subpopulation) { - this.subpopulation = subpopulation; - } - - @StringGetter(SUBPOPULATION) - public String getSubpopulation() { - return this.subpopulation; - } - } } diff --git a/matsim/src/main/java/org/matsim/core/controler/ControlerDefaultsModule.java b/matsim/src/main/java/org/matsim/core/controler/ControlerDefaultsModule.java index bd64b584d38..fc5c43da358 100755 --- a/matsim/src/main/java/org/matsim/core/controler/ControlerDefaultsModule.java +++ b/matsim/src/main/java/org/matsim/core/controler/ControlerDefaultsModule.java @@ -37,6 +37,7 @@ import org.matsim.core.population.VspPlansCleanerModule; import org.matsim.core.replanning.StrategyManagerModule; import org.matsim.core.replanning.annealing.ReplanningAnnealer; +import org.matsim.core.replanning.inheritance.PlanInheritanceModule; import org.matsim.core.router.TripRouterModule; import org.matsim.core.router.costcalculators.TravelDisutilityModule; import org.matsim.core.scoring.functions.CharyparNagelScoringFunctionModule; @@ -76,6 +77,7 @@ public void install() { install(new VspPlansCleanerModule()); install(new SnapshotWritersModule()); install(new DependencyGraphModule()); + install(new PlanInheritanceModule()); // Comment by Tarek Chouaki. // To make sure the cache files used under ChartUtils are located in tmp folder in the output directory diff --git a/matsim/src/main/java/org/matsim/core/controler/PrepareForSimImpl.java b/matsim/src/main/java/org/matsim/core/controler/PrepareForSimImpl.java index 628a67c532a..3278f06e295 100644 --- a/matsim/src/main/java/org/matsim/core/controler/PrepareForSimImpl.java +++ b/matsim/src/main/java/org/matsim/core/controler/PrepareForSimImpl.java @@ -19,9 +19,11 @@ * * * *********************************************************************** */ - package org.matsim.core.controler; +package org.matsim.core.controler; +import jakarta.inject.Inject; +import jakarta.inject.Provider; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.Id; @@ -49,7 +51,6 @@ 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; @@ -57,12 +58,8 @@ import org.matsim.vehicles.VehicleType; import org.matsim.vehicles.VehicleUtils; -import jakarta.inject.Inject; -import jakarta.inject.Provider; - import javax.annotation.Nullable; import java.util.*; -import java.util.stream.Collectors; import static org.matsim.core.config.groups.QSimConfigGroup.VehiclesSource.modeVehicleTypesFromVehiclesData; @@ -230,13 +227,35 @@ 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> modeToVehicle = new HashMap<>(); + + // optional attribute, that can be null + Map> modeTypes = VehicleUtils.getVehicleTypes(person); + + for (Map.Entry modeType : modeVehicleTypes.entrySet()) { + + String mode = modeType.getKey(); + + Id vehicleId = createVehicleId(person, mode); + + // get the type + VehicleType type = modeType.getValue(); + + // Use the person attribute to map to a more specific vehicle type + if (modeTypes != null && modeTypes.containsKey(mode)) { + Id typeId = modeTypes.get(mode); + type = scenario.getVehicles().getVehicleTypes().get(typeId); + if (type == null) { + throw new IllegalStateException("Vehicle type " + typeId + " specified for person " + person.getId() + ", but not found in scenario."); + } + } + + createAndAddVehicleIfNecessary(vehicleId, type); + + // write mode-string to vehicle-id into a map + modeToVehicle.put(mode, vehicleId); + } VehicleUtils.insertVehicleIdsIntoAttributes(person, modeToVehicle); } diff --git a/matsim/src/main/java/org/matsim/core/population/PlanImpl.java b/matsim/src/main/java/org/matsim/core/population/PlanImpl.java index 9c8957cd480..8477a25b7dc 100644 --- a/matsim/src/main/java/org/matsim/core/population/PlanImpl.java +++ b/matsim/src/main/java/org/matsim/core/population/PlanImpl.java @@ -27,17 +27,22 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.Customizable; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.Activity; 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.PlanElement; +import org.matsim.core.replanning.inheritance.PlanInheritanceModule; import org.matsim.core.scenario.CustomizableUtils; import org.matsim.utils.objectattributes.attributable.Attributes; import org.matsim.utils.objectattributes.attributable.AttributesImpl; /* deliberately package */ final class PlanImpl implements Plan { + private Id id= null; + private ArrayList actsLegs = new ArrayList<>(); private Double score = null; @@ -121,6 +126,44 @@ public String getType() { public void setType(final String type) { this.type = type; } + + @Override + public Id getId() { + if(this.id!=null) + return this.id; + else { + if(this.getAttributes().getAttribute(PlanInheritanceModule.PLAN_ID)!=null) + return Id.create(this.getAttributes().getAttribute(PlanInheritanceModule.PLAN_ID).toString(),Plan.class); + else return null; + } + + } + + @Override + public void setPlanId(Id planId) { + this.getAttributes().putAttribute(PlanInheritanceModule.PLAN_ID, planId.toString()); + this.id = planId; + } + + @Override + public int getIterationCreated() { + return (int) this.getAttributes().getAttribute(PlanInheritanceModule.ITERATION_CREATED); + } + + @Override + public void setIterationCreated(int iteration) { + this.getAttributes().putAttribute(PlanInheritanceModule.ITERATION_CREATED, iteration); + } + + @Override + public String getPlanMutator() { + return (String) this.getAttributes().getAttribute(PlanInheritanceModule.PLAN_MUTATOR); + } + + @Override + public void setPlanMutator(String planMutator) { + this.getAttributes().putAttribute(PlanInheritanceModule.PLAN_MUTATOR, planMutator); + } @Override public final List getPlanElements() { @@ -164,6 +207,8 @@ public final Map getCustomAttributes() { return this.customizableDelegate.getCustomAttributes(); } + + // public final void setLocked() { // for ( PlanElement pe : this.actsLegs ) { // if ( pe instanceof ActivityImpl ) { diff --git a/matsim/src/main/java/org/matsim/core/population/PopulationUtils.java b/matsim/src/main/java/org/matsim/core/population/PopulationUtils.java index 739b890ce91..9a572ead6ca 100644 --- a/matsim/src/main/java/org/matsim/core/population/PopulationUtils.java +++ b/matsim/src/main/java/org/matsim/core/population/PopulationUtils.java @@ -381,6 +381,36 @@ public String getType() { public void setType(String type) { throw new UnsupportedOperationException(); } + + @Override + public Id getId() { + return this.delegate.getId(); + } + + @Override + public void setPlanId(Id planId) { + throw new UnsupportedOperationException(); + } + + @Override + public int getIterationCreated() { + return this.delegate.getIterationCreated(); + } + + @Override + public void setIterationCreated(int iteration) { + throw new UnsupportedOperationException(); + } + + @Override + public String getPlanMutator() { + return this.delegate.getPlanMutator(); + } + + @Override + public void setPlanMutator(String planMutator) { + throw new UnsupportedOperationException(); + } @Override public void addLeg(Leg leg) { diff --git a/matsim/src/main/java/org/matsim/core/population/algorithms/MutateActivityTimeAllocation.java b/matsim/src/main/java/org/matsim/core/population/algorithms/MutateActivityTimeAllocation.java new file mode 100644 index 00000000000..e658570f8ea --- /dev/null +++ b/matsim/src/main/java/org/matsim/core/population/algorithms/MutateActivityTimeAllocation.java @@ -0,0 +1,122 @@ +/* *********************************************************************** * + * project: org.matsim.* + * PlanMutateTimeAllocation.java + * * + * *********************************************************************** * + * * + * copyright : (C) 2007, 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.core.population.algorithms; + +import java.util.Random; + +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.router.TripStructureUtils; +import org.matsim.core.router.TripStructureUtils.StageActivityHandling; + +/** + * Mutates the duration of activities randomly within a specified range. + * + * @author knagel, jbischoff + */ +public final class MutateActivityTimeAllocation implements PlanAlgorithm { + + + public static final String INITIAL_END_TIME_ATTRIBUTE = "initialEndTime"; + private final double mutationRange; + private final Random random; + private final boolean affectingDuration; + private final double latestActivityEndTime; + private final boolean mutateAroundInitialEndTimeOnly; + private final double mutationRangeStep; + + + public MutateActivityTimeAllocation(final double mutationRange, boolean affectingDuration, final Random random, double latestActivityEndTime, boolean mutateAroundInitialEndTimeOnly, double mutationRangeStep) { + this.mutationRange = mutationRange; + this.affectingDuration = affectingDuration; + this.random = random; + this.latestActivityEndTime = latestActivityEndTime; + this.mutateAroundInitialEndTimeOnly = mutateAroundInitialEndTimeOnly; + this.mutationRangeStep = Math.max(1.0,mutationRangeStep); + + } + + @Override + public void run(final Plan plan) { + for ( Activity act : TripStructureUtils.getActivities( plan , StageActivityHandling.ExcludeStageActivities ) ) { + if (act.getEndTime().isDefined()) { + double endTime = act.getEndTime().seconds(); + if (mutateAroundInitialEndTimeOnly){ + Object initialEndtime = act.getAttributes().getAttribute(INITIAL_END_TIME_ATTRIBUTE); + if (initialEndtime!=null) { + endTime = (double) initialEndtime; + } else { + act.getAttributes().putAttribute(INITIAL_END_TIME_ATTRIBUTE,endTime); + } + } + double newEndTime = Math.min(mutateTime(endTime, mutationRange),this.latestActivityEndTime); + act.setEndTime(newEndTime); + act.setStartTimeUndefined(); + } + else if ( affectingDuration ) { + if ( act.getMaximumDuration().isDefined()) { + act.setMaximumDuration(mutateTime(act.getMaximumDuration().seconds(), mutationRange)); + } + } + } + setLegDepartureTimes(plan); + + } + + private void setLegDepartureTimes(Plan plan) { + //setting leg departure times can only be an estimate and might be useful for certain dynamic modes. + //In general, it is best to trigger a reroute after mutating time. + double now = 0; + for (PlanElement planElement : plan.getPlanElements()){ + if (planElement instanceof Activity activity){ + if (activity.getEndTime().isDefined()){ + now = activity.getEndTime().seconds(); + } + else if (activity.getMaximumDuration().isDefined()){ + now = now + activity.getMaximumDuration().seconds(); + } + } else if (planElement instanceof Leg leg){ + if (leg.getDepartureTime().isDefined()) { + leg.setDepartureTime(now); + if (leg.getTravelTime().isDefined()) { + now = now + leg.getTravelTime().seconds(); + } + } + } + } + } + + private double mutateTime(final double time, double mutationRange) { + double t = time; + int mutationRangeBins = (int) Math.ceil( mutationRange/mutationRangeStep); + t = t - mutationRange + (2*this.random.nextInt(mutationRangeBins)*mutationRangeStep) ; + if (t < 0) { + t = 0; + } + // note that this also affects duration + return t; + } + + + +} diff --git a/matsim/src/main/java/org/matsim/core/population/algorithms/PlanMutateTimeAllocation.java b/matsim/src/main/java/org/matsim/core/population/algorithms/PlanMutateTimeAllocation.java deleted file mode 100644 index b078ed95700..00000000000 --- a/matsim/src/main/java/org/matsim/core/population/algorithms/PlanMutateTimeAllocation.java +++ /dev/null @@ -1,142 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* - * PlanMutateTimeAllocation.java - * * - * *********************************************************************** * - * * - * copyright : (C) 2007, 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.core.population.algorithms; - -import java.util.List; -import java.util.Random; - -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.utils.misc.OptionalTime; - -/** - * Mutates the duration of activities randomly within a specified range. - * For the first act, the end-time is mutated. For the last act, duration and end-time are set to UNDEFINED. - * For all other acts, the duration is mutated, and the end-time is updated accordingly. - * If an activity has no duration set (UNDEFINED_TIME), a random time between 0 and 24h will be chosen. - * Departure and arrival times of legs are updated according to the activity durations, but the - * leg travel time is not modified (e.g. updated according to new expected departure time). - * - * @author mrieser - */ -public final class PlanMutateTimeAllocation implements PlanAlgorithm { - - private final double mutationRange; - private final Random random; - - public PlanMutateTimeAllocation(final double mutationRange, final Random random) { - this.mutationRange = mutationRange; - this.random = random; - } - - @Override - public void run(final Plan plan) { - mutatePlan(plan); - } - - private void mutatePlan(final Plan plan) { - - List planElements = plan.getPlanElements(); - int max = planElements.size(); - - double now = 0; - - // apply mutation to all activities except the last home activity - for (int i = 0; i < max; i++) { - - PlanElement pe = planElements.get(i); - - if (pe instanceof Activity) { - Activity act = (Activity) pe; - - // handle first activity - if (i == 0) { - // set start to midnight - act.setStartTime(now); - // mutate the end time of the first activity - act.setEndTime(mutateTime(act.getEndTime())); - // calculate resulting duration - act.setMaximumDuration(act.getEndTime().seconds() - act.getStartTime().seconds()); - // move now pointer - now += act.getEndTime().seconds(); - - // handle middle activities - } else if (i < (max - 1)) { - - // assume that there will be no delay between arrival time and activity start time - act.setStartTime(now); - if (act.getMaximumDuration().isDefined()) { - // mutate the durations of all 'middle' activities - act.setMaximumDuration(mutateTime(act.getMaximumDuration())); - now += act.getMaximumDuration().seconds(); - // set end time accordingly - act.setEndTime(now); - } else { - double newEndTime = mutateTime(act.getEndTime()); - if (newEndTime < now) { - newEndTime = now; - } - act.setEndTime(newEndTime); - now = newEndTime; - } - // handle last activity - } else { - - // assume that there will be no delay between arrival time and activity start time - act.setStartTime(now); - // invalidate duration and end time because the plan will be interpreted 24 hour wrap-around - act.setMaximumDurationUndefined(); - act.setEndTimeUndefined(); - - } - - } else if (pe instanceof Leg) { - - Leg leg = (Leg) pe; - - // assume that there will be no delay between end time of previous activity and departure time - leg.setDepartureTime(now); - // let duration untouched. if defined add it to now - if (leg.getTravelTime().isDefined()) { - now += leg.getTravelTime().seconds(); - } - final double arrTime = now; - // set planned arrival time accordingly - leg.setTravelTime( arrTime - leg.getDepartureTime().seconds()); - - } - } - } - - private double mutateTime(final OptionalTime time) { - if (time.isDefined()) { - double t = time.seconds() + (int)((this.random.nextDouble() * 2.0 - 1.0) * this.mutationRange); - if (t < 0) t = 0; - if (t > 24*3600) t = 24*3600; - return t; - } else { - return this.random.nextInt(24*3600); - } - } - -} diff --git a/matsim/src/main/java/org/matsim/core/population/algorithms/PlanMutateTimeAllocationSimplified.java b/matsim/src/main/java/org/matsim/core/population/algorithms/PlanMutateTimeAllocationSimplified.java deleted file mode 100644 index 7e4434a9f94..00000000000 --- a/matsim/src/main/java/org/matsim/core/population/algorithms/PlanMutateTimeAllocationSimplified.java +++ /dev/null @@ -1,96 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* - * PlanMutateTimeAllocation.java - * * - * *********************************************************************** * - * * - * copyright : (C) 2007, 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.core.population.algorithms; - -import java.util.Random; - -import org.matsim.api.core.v01.population.Activity; -import org.matsim.api.core.v01.population.Plan; -import org.matsim.core.router.TripStructureUtils; -import org.matsim.core.router.TripStructureUtils.StageActivityHandling; - -/** - * Mutates the duration of activities randomly within a specified range. - *
- * Other than the "full" version, this version just mutates activity end time and activity duration if they are defined, - * without thinking any further. If this produces invalid plans, they will eventually be removed through the selection - * process. kai, jun'12 - * - * @author knagel - */ -public final class PlanMutateTimeAllocationSimplified implements PlanAlgorithm { - - private final StageActivityHandling stageActivityHandling; - private final double mutationRange; - private final Random random; - private final boolean affectingDuration; - - /** - * Initializes an instance mutating all activities in a plan - * @param mutationRange - * @param affectingDuration - * @param random - */ - public PlanMutateTimeAllocationSimplified(final double mutationRange, boolean affectingDuration, final Random random) { - this( StageActivityHandling.StagesAsNormalActivities , mutationRange , affectingDuration, random ); - } - /** - * Initializes an instance mutating all non-stage activities in a plan - * @param mutationRange - * @param affectingDuration - * @param random - */ - public PlanMutateTimeAllocationSimplified(final StageActivityHandling stageActivityHandling, final double mutationRange, boolean affectingDuration, final Random random) { - this.stageActivityHandling = stageActivityHandling; - this.mutationRange = mutationRange; - this.affectingDuration = affectingDuration; - this.random = random; - } - - @Override - public void run(final Plan plan) { - for ( Activity act : TripStructureUtils.getActivities( plan , stageActivityHandling ) ) { - // this is deliberately simplistic. Cleanup up of the time information should be done somewhere else. - if (act.getEndTime().isDefined()) { - act.setEndTime(mutateTime(act.getEndTime().seconds())); - } - if ( affectingDuration ) { - if ( act.getMaximumDuration().isDefined()) { - act.setMaximumDuration(mutateTime(act.getMaximumDuration().seconds())); - } - } - } - // the legs are not doing anything. kai, jun'12 - } - - private double mutateTime(final double time) { - double t = time; - t = t + (int)((this.random.nextDouble() * 2.0 - 1.0) * this.mutationRange); - - if (t < 0) { - t = 0; - } - // note that this also affects duration - - return t; - } - -} diff --git a/matsim/src/main/java/org/matsim/core/population/algorithms/TripPlanMutateTimeAllocation.java b/matsim/src/main/java/org/matsim/core/population/algorithms/TripPlanMutateTimeAllocation.java index b9e5ed82d7d..05940f1c8bd 100644 --- a/matsim/src/main/java/org/matsim/core/population/algorithms/TripPlanMutateTimeAllocation.java +++ b/matsim/src/main/java/org/matsim/core/population/algorithms/TripPlanMutateTimeAllocation.java @@ -69,12 +69,11 @@ public TripPlanMutateTimeAllocation(final double mutationRange, final boolean af this.mutationRange = mutationRange; this.affectingDuration = affectingDuration; this.random = random; -// this.subpopulationAttribute = subpopulationAttribute; this.subpopulationMutationRanges = subpopulationMutationRanges; this.subpopulationAffectingDuration = subpopulationAffectingDuration; this.latestEndTime = latestEndTime; } - + @Override public void run(final Plan plan) { mutatePlan(plan); @@ -89,12 +88,11 @@ private void mutatePlan(final Plan plan) { final String subpopulation = this.getSubpopulation(plan); final boolean affectingDuration = this.isAffectingDuration(subpopulation); final double mutationRange = this.getMutationRange(subpopulation); - + // apply mutation to all activities except the last home activity for (PlanElement pe : plan.getPlanElements()) { - if (pe instanceof Activity) { - Activity act = (Activity)pe; + if (pe instanceof Activity act) { // handle first activity if (isFirst) { @@ -124,7 +122,7 @@ private void mutatePlan(final Plan plan) { } now += act.getMaximumDuration().seconds(); // (may feel a bit disturbing since it was not mutated but it is just using the "old" value which is perfectly ok. kai, jan'14) - + // set end time accordingly act.setEndTime(now); } else { @@ -191,30 +189,28 @@ private double mutateTime(final OptionalTime time, final double mutationRange) { public void setUseActivityDurations(final boolean useActivityDurations) { this.useActivityDurations = useActivityDurations; } - - private final String getSubpopulation(final Plan plan) { -// if (this.subpopulationAttribute == null) return null; + + private String getSubpopulation(final Plan plan) { if (plan.getPerson() == null) return null; -// return (String) PopulationUtils.getPersonAttribute(plan.getPerson(), this.subpopulationAttribute); return PopulationUtils.getSubpopulation( plan.getPerson() ); } - - private final boolean isAffectingDuration(final String subpopulation) { + + private boolean isAffectingDuration(final String subpopulation) { if (subpopulation != null) { Boolean isAffectingDuration = this.subpopulationAffectingDuration.get(subpopulation); if (isAffectingDuration != null) return isAffectingDuration.booleanValue(); } - + // fallback solution: no subpopulation attribute was found return this.affectingDuration; } - - private final double getMutationRange(final String subpopulation) { + + private double getMutationRange(final String subpopulation) { if (subpopulation != null) { Double mutationRange = this.subpopulationMutationRanges.get(subpopulation); if (mutationRange != null) return mutationRange.doubleValue(); } - + // fallback solution: no subpopulation attribute was found return this.mutationRange; } diff --git a/matsim/src/main/java/org/matsim/core/replanning/GenericPlanStrategyImpl.java b/matsim/src/main/java/org/matsim/core/replanning/GenericPlanStrategyImpl.java index db83e5d4749..b9063b0878d 100644 --- a/matsim/src/main/java/org/matsim/core/replanning/GenericPlanStrategyImpl.java +++ b/matsim/src/main/java/org/matsim/core/replanning/GenericPlanStrategyImpl.java @@ -22,6 +22,7 @@ import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.population.BasicPlan; import org.matsim.api.core.v01.population.HasPlansAndId; +import org.matsim.api.core.v01.population.Plan; import org.matsim.core.replanning.modules.GenericPlanStrategyModule; import org.matsim.core.replanning.selectors.PlanSelector; import org.matsim.core.replanning.selectors.RandomUnscoredPlanSelector; @@ -92,6 +93,13 @@ public void run(final HasPlansAndId person) { // set the working plan to a copy of the selected plan: plan = person.createCopyOfSelectedPlanAndMakeSelected(); + //Id is only set inside planInheritance -> if null planInheritance is disabled + if (plan instanceof Plan && ((Plan) plan).getId() != null) { + // add plan inheritance flags + ((Plan) plan).setIterationCreated(this.replanningContext.getIteration()); + ((Plan) plan).setPlanMutator(this.toString()); + } + // add new plan to container that contains the plans that are handled by this PlanStrategy: this.plans.add(plan); diff --git a/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceModule.java b/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceModule.java new file mode 100644 index 00000000000..0a0a48f961d --- /dev/null +++ b/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceModule.java @@ -0,0 +1,269 @@ +package org.matsim.core.replanning.inheritance; + +/* *********************************************************************** * + * project: org.matsim.* + * ParallelPopulationReaderMatsimV6.java + * * + * *********************************************************************** * + * * + * copyright : (C) 2023 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 * + * * + * *********************************************************************** */ + +import java.io.BufferedWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.core.config.groups.ControllerConfigGroup.CompressionType; +import org.matsim.core.config.groups.ReplanningConfigGroup.StrategySettings; +import org.matsim.core.controler.AbstractModule; +import org.matsim.core.controler.events.BeforeMobsimEvent; +import org.matsim.core.controler.events.ShutdownEvent; +import org.matsim.core.controler.events.StartupEvent; +import org.matsim.core.controler.listener.BeforeMobsimListener; +import org.matsim.core.controler.listener.ShutdownListener; +import org.matsim.core.controler.listener.StartupListener; +import org.matsim.core.population.PersonUtils; +import org.matsim.core.replanning.GenericPlanStrategy; +import org.matsim.core.replanning.StrategyManager; +import org.matsim.core.utils.io.IOUtils; + +import com.google.inject.Singleton; + +/** + * The core plan inheritance module responsible for + *
  • Initialization of initially read plans and default writers + *
  • Book-keeping, i.e. setting additional plan attributes not stored in the plan itself + *
  • Calculation default stats like the distribution of mutators among (selected) plans + * + * @author neuma, alex94263 + */ +@Singleton +public class PlanInheritanceModule extends AbstractModule implements StartupListener, BeforeMobsimListener, ShutdownListener { + + public static final String PLAN_ID = "planId"; + public static final String ITERATION_CREATED = "iterationCreated"; + public static final String PLAN_MUTATOR = "planMutator"; + + public static final String INITIAL_PLAN = "initialPlan"; + public static final String NONE = "NONE"; + + public static final String FILENAME_PLAN_INHERITANCE_RECORDS = "planInheritanceRecords"; + + long numberOfPlanInheritanceRecordsCreated = 0; + Map, PlanInheritanceRecord> planId2planInheritanceRecords = new ConcurrentHashMap<>(); + + PlanInheritanceRecordWriter planInheritanceRecordWriter; + private ArrayList strategies; + + private final Character DELIMITER = '\t'; + private BufferedWriter selectedPlanStrategyShareWriter; + private BufferedWriter planStrategyShareWriter; + + @Override + public void notifyStartup(StartupEvent event) { + // initialize all default writers + CompressionType compressionType = event.getServices().getConfig().controller().getCompressionType(); + this.planInheritanceRecordWriter = new PlanInheritanceRecordWriter(event.getServices().getControlerIO().getOutputFilename(FILENAME_PLAN_INHERITANCE_RECORDS + ".csv", compressionType)); + this.strategies = this.getActiveStrategies(event.getServices().getConfig().replanning().getStrategySettings(), event.getServices().getStrategyManager()); + this.selectedPlanStrategyShareWriter = this.initializeDistributionWriter(this.strategies, event.getServices().getControlerIO().getOutputFilename(FILENAME_PLAN_INHERITANCE_RECORDS + "_shares_selected.csv")); + this.planStrategyShareWriter = this.initializeDistributionWriter(this.strategies, event.getServices().getControlerIO().getOutputFilename(FILENAME_PLAN_INHERITANCE_RECORDS + "_shares.csv")); + + // reset all plan attributes that might be present from a previously performed matsim run + for (Person person : event.getServices().getScenario().getPopulation().getPersons().values()) { + for (Plan plan : person.getPlans()) { + plan.setPlanId(Id.create(NONE, Plan.class)); + plan.setPlanMutator(INITIAL_PLAN); + plan.setIterationCreated(0); + } + } + } + + /** + * Retrieve all active plan strategies with their correct name in alphabetical order + */ + private ArrayList getActiveStrategies(Collection strategySettings, StrategyManager strategyManager) { + Set activeSubpopulations = new HashSet<>(); + for (StrategySettings strategySetting : strategySettings) { + activeSubpopulations.add(strategySetting.getSubpopulation()); + } + + Set planStrategiesNames = new HashSet<>(); + for (String subpopulation : activeSubpopulations) { + for (GenericPlanStrategy planStrategy : strategyManager.getStrategies(subpopulation)) { + planStrategiesNames.add(planStrategy.toString()); + } + } + + ArrayList strategies = new ArrayList<>(planStrategiesNames.size() + 1); + strategies.addAll(planStrategiesNames); + Collections.sort(strategies); + strategies.add(0, INITIAL_PLAN); + + return strategies; + } + + /** + * Initialize the writer with the active strategies + */ + private BufferedWriter initializeDistributionWriter(ArrayList strategies, String filename) { + + BufferedWriter planStrategyShareWriter = IOUtils.getBufferedWriter(filename); + + StringBuffer header = new StringBuffer(); + header.append("iteration"); header.append(DELIMITER); + for (int i = 0; i < strategies.size(); i++) { + if (i > 0) { + header.append(DELIMITER); + } + header.append(strategies.get(i)); + } + + try { + planStrategyShareWriter.write(header.toString()); + planStrategyShareWriter.newLine(); + } catch (IOException e) { + throw new RuntimeException("Could not initialize the plan strategy share writer!", e); + } + + return planStrategyShareWriter; + } + + @Override + public void notifyBeforeMobsim(BeforeMobsimEvent event) { + // check the plans of the population and all currently stored plan records - do the actual book-keeping + + Set> activePlanIds = new HashSet<>(); + Set> selectedPlanIds = new HashSet<>(); + + for (Person person : event.getServices().getScenario().getPopulation().getPersons().values()) { + for (Plan plan : person.getPlans()) { + + if (plan.getPlanMutator() == null) { + // initial plan - set initial plan defaults + plan.setPlanMutator(INITIAL_PLAN); + plan.setIterationCreated(event.getIteration()); + } + + if (plan.getIterationCreated() == event.getIteration()) { + // it's a new plan created in this iteration - create a new record + + PlanInheritanceRecord planInheritanceRecord = new PlanInheritanceRecord(); + planInheritanceRecord.setAgentId(person.getId()); + planInheritanceRecord.setPlanId(Id.create(Long.toString(++this.numberOfPlanInheritanceRecordsCreated, 36), Plan.class)); + planInheritanceRecord.setAncestorId(plan.getId()); //works because new plan is copy of old selected and attributes are copied -> thus current attribute plan id is old selected plan id + plan.setPlanId(planInheritanceRecord.getPlanId()); + planInheritanceRecord.setIterationCreated(plan.getIterationCreated()); + planInheritanceRecord.setMutatedBy(plan.getPlanMutator()); + + this.planId2planInheritanceRecords.put(planInheritanceRecord.getPlanId(), planInheritanceRecord); + } + + if (PersonUtils.isSelected(plan)) { + this.planId2planInheritanceRecords.get(plan.getId()).getIterationsSelected().add(event.getIteration()); + selectedPlanIds.add(plan.getId()); + } + + activePlanIds.add(plan.getId()); + } + } + + List> deletedPlans = new ArrayList<>(); + for (Id planId : this.planId2planInheritanceRecords.keySet()) { + if (!activePlanIds.contains(planId)) { + deletedPlans.add(planId); + } + } + + for (Id deletedPlanId : deletedPlans) { + PlanInheritanceRecord deletedPlanInheritanceRecord = this.planId2planInheritanceRecords.remove(deletedPlanId); + deletedPlanInheritanceRecord.setIterationRemoved(event.getIteration()); + this.planInheritanceRecordWriter.write(deletedPlanInheritanceRecord); + } + + this.planInheritanceRecordWriter.flush(); + + this.calculateAndWriteDistribution(event.getIteration(), this.strategies, this.planId2planInheritanceRecords, selectedPlanIds, this.selectedPlanStrategyShareWriter); + this.calculateAndWriteDistribution(event.getIteration(), this.strategies, this.planId2planInheritanceRecords, this.planId2planInheritanceRecords.keySet(), this.planStrategyShareWriter); + } + + /** + * Updates the default plan stats - namely the distribution of plan mutators based on the given plan ids. + */ + private void calculateAndWriteDistribution(int currentIteration, ArrayList strategies, Map, PlanInheritanceRecord> planId2planInheritanceRecords, Set> planIds, BufferedWriter writer) { + Map strategy2count = new HashMap<>(); + for (String strategyName : strategies) { + strategy2count.put(strategyName, new AtomicLong(0)); + } + for (Id planId : planIds) { + String mutatedBy = planId2planInheritanceRecords.get(planId).getMutatedBy(); + strategy2count.get(mutatedBy).incrementAndGet(); + } + long sum = strategy2count.values().stream().mapToLong(count -> count.get()).sum(); + StringBuffer line = new StringBuffer(); + line.append(currentIteration); + line.append(DELIMITER); + for (int i = 0; i < strategies.size(); i++) { + if (i > 0) { + line.append(DELIMITER); + } + line.append(String.valueOf(strategy2count.get(strategies.get(i)).doubleValue() / sum)); + } + try { + writer.write(line.toString()); + writer.newLine(); + } catch (IOException e) { + throw new RuntimeException("Could not initialize the plan strategy share writer!", e); + } + } + + @Override + public void notifyShutdown(ShutdownEvent event) { + // flush all pending plan inheritance records and close the readers + + for (PlanInheritanceRecord planInheritanceRecord : this.planId2planInheritanceRecords.values()) { + this.planInheritanceRecordWriter.write(planInheritanceRecord); + } + + this.planInheritanceRecordWriter.flush(); + this.planInheritanceRecordWriter.close(); + + try { + this.selectedPlanStrategyShareWriter.flush(); + this.selectedPlanStrategyShareWriter.close(); + this.planStrategyShareWriter.flush(); + this.planStrategyShareWriter.close(); + } catch (IOException e) { + new RuntimeException(e); + } + + this.planId2planInheritanceRecords.clear(); + } + + @Override + public void install() { + if (getConfig().planInheritance().getEnabled()) addControlerListenerBinding().to(PlanInheritanceModule.class); + } +} diff --git a/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceRecord.java b/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceRecord.java new file mode 100644 index 00000000000..7212f337896 --- /dev/null +++ b/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceRecord.java @@ -0,0 +1,129 @@ +package org.matsim.core.replanning.inheritance; + +/* *********************************************************************** * + * project: org.matsim.* + * ParallelPopulationReaderMatsimV6.java + * * + * *********************************************************************** * + * * + * copyright : (C) 2023 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 * + * * + * *********************************************************************** */ + +import java.util.ArrayList; +import java.util.List; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Plan; + +/** + * Data container storing the data of a single plan. + * + * @author neuma, alex94263 + */ +public class PlanInheritanceRecord { + + /** + * Id of the person that plan record belongs to. + */ + private Id agentId; + + /** + * The globally unique plan id. + */ + private Id planId; + + /** + * Id of the plan that this plan had been copied from before mutating. + */ + private Id ancestorId; + + /** + * The name of the strategy that altered this plan. + */ + private String mutatedBy; + + /** + * Iteration in which this plan had been created. May be {@linkplain PlanInheritanceModule#INITIAL_PLAN} if the plan had been in the choice-set from the very beginning. + */ + private int iterationCreated; + + /** + * Iteration in which the plan had been removed from the choice-set. + */ + private int iterationRemoved = -1; + + /** + * Collection of iterations this plan had been the selected plan. + * Initialize this with one since each plan is selected at least once. + */ + private List iterationsSelected = new ArrayList<>(1); + + public Id getPlanId() { + return planId; + } + + public void setPlanId(Id planId) { + this.planId = planId; + } + + public Id getAncestorId() { + return ancestorId; + } + + public void setAncestorId(Id ancestorId) { + this.ancestorId = ancestorId; + } + + public String getMutatedBy() { + return mutatedBy; + } + + public void setMutatedBy(String mutatedBy) { + this.mutatedBy = mutatedBy; + } + + public int getIterationCreated() { + return iterationCreated; + } + + public void setIterationCreated(int iterationCreated) { + this.iterationCreated = iterationCreated; + } + + public int getIterationRemoved() { + return iterationRemoved; + } + + public void setIterationRemoved(int iterationRemoved) { + this.iterationRemoved = iterationRemoved; + } + + public List getIterationsSelected() { + return iterationsSelected; + } + + public void setIterationsSelected(List iterationsSelected) { + this.iterationsSelected = iterationsSelected; + } + + public Id getAgentId() { + return agentId; + } + + public void setAgentId(Id agentId) { + this.agentId = agentId; + } + +} diff --git a/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceRecordReader.java b/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceRecordReader.java new file mode 100644 index 00000000000..8d83d6ef1de --- /dev/null +++ b/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceRecordReader.java @@ -0,0 +1,96 @@ +package org.matsim.core.replanning.inheritance; + +import java.io.BufferedReader; + +/* *********************************************************************** * + * project: org.matsim.* + * ParallelPopulationReaderMatsimV6.java + * * + * *********************************************************************** * + * * + * copyright : (C) 2023 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 * + * * + * *********************************************************************** */ + +import java.io.BufferedWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.core.utils.io.IOUtils; + +/** + * Writes {@linkplain PlanInheritanceRecord} to file in a fixed column sequence. + * + * @author alex94263 + */ +public class PlanInheritanceRecordReader { + + + private final String DELIMITER = "\t"; + private final BufferedReader reader; + + public PlanInheritanceRecordReader(String filename) { + this.reader = IOUtils.getBufferedReader(filename); + + + + } + public Map buildIdx(String[] header) { + Map lookup = new HashMap(); + for (int i=0; i read() { + List records = new ArrayList(); + try { + Map lookUp = buildIdx(reader.readLine().split(DELIMITER)); + String lineString = reader.readLine(); + while(lineString !=null) { + String[] line = lineString.split(DELIMITER); + PlanInheritanceRecord planInheritanceRecord = new PlanInheritanceRecord(); + planInheritanceRecord.setAgentId(Id.createPersonId(line[lookUp.get(PlanInheritanceRecordWriter.AGENT_ID)])); + planInheritanceRecord.setPlanId(Id.create(line[lookUp.get(PlanInheritanceRecordWriter.PLAN_ID)], Plan.class)); + planInheritanceRecord.setAncestorId(Id.create(line[lookUp.get(PlanInheritanceRecordWriter.ANCESTOR_ID)], Plan.class)); + planInheritanceRecord.setMutatedBy(line[lookUp.get(PlanInheritanceRecordWriter.MUTATED_BY)]); + planInheritanceRecord.setIterationCreated(Integer.parseInt(line[lookUp.get(PlanInheritanceRecordWriter.ITERATION_CREATED)])); + planInheritanceRecord.setIterationRemoved(Integer.parseInt(line[lookUp.get(PlanInheritanceRecordWriter.ITERATION_REMOVED)])); + String iterationsSelected = line[lookUp.get(PlanInheritanceRecordWriter.ITERATIONS_SELECTED)]; + planInheritanceRecord.setIterationsSelected(Arrays.asList(iterationsSelected.substring(1, iterationsSelected.length()-1).split(", ")).stream() + .map(Integer::parseInt) + .collect(Collectors.toList())); + records.add(planInheritanceRecord); + lineString = reader.readLine(); + } + return records; + + } catch (IOException e) { + throw new RuntimeException("Could not read the plan inheritance records!", e); + } + + + + } + + +} diff --git a/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceRecordWriter.java b/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceRecordWriter.java new file mode 100644 index 00000000000..1400a915406 --- /dev/null +++ b/matsim/src/main/java/org/matsim/core/replanning/inheritance/PlanInheritanceRecordWriter.java @@ -0,0 +1,99 @@ +package org.matsim.core.replanning.inheritance; + +/* *********************************************************************** * + * project: org.matsim.* + * ParallelPopulationReaderMatsimV6.java + * * + * *********************************************************************** * + * * + * copyright : (C) 2023 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 * + * * + * *********************************************************************** */ + +import java.io.BufferedWriter; +import java.io.IOException; + +import org.matsim.core.utils.io.IOUtils; + +/** + * Writes {@linkplain PlanInheritanceRecord} to file in a fixed column sequence. + * + * @author neuma, alex94263 + */ +public class PlanInheritanceRecordWriter { + + public static final String AGENT_ID = "agentId"; + public static final String PLAN_ID = "planId"; + public static final String ANCESTOR_ID = "ancestorId"; + public static final String MUTATED_BY = "mutatedBy"; + public static final String ITERATION_CREATED = "iterationCreated"; + public static final String ITERATION_REMOVED = "iterationRemoved"; + public static final String ITERATIONS_SELECTED = "iterationsSelected"; + + private final Character DELIMITER = '\t'; + private final BufferedWriter writer; + + public PlanInheritanceRecordWriter(String filename) { + this.writer = IOUtils.getBufferedWriter(filename); + + StringBuffer header = new StringBuffer(); + header.append(AGENT_ID); header.append(DELIMITER); + header.append(PLAN_ID); header.append(DELIMITER); + header.append(ANCESTOR_ID); header.append(DELIMITER); + header.append(MUTATED_BY); header.append(DELIMITER); + header.append(ITERATION_CREATED); header.append(DELIMITER); + header.append(ITERATION_REMOVED); header.append(DELIMITER); + header.append(ITERATIONS_SELECTED); + + try { + this.writer.write(header.toString()); + this.writer.newLine(); + } catch (IOException e) { + throw new RuntimeException("Could not initialize the plan inheritance writer!", e); + } + } + + public void write(PlanInheritanceRecord planInheritanceRecord) { + StringBuffer line = new StringBuffer(); + line.append(planInheritanceRecord.getAgentId()); line.append(DELIMITER); + line.append(planInheritanceRecord.getPlanId()); line.append(DELIMITER); + line.append(planInheritanceRecord.getAncestorId()); line.append(DELIMITER); + line.append(planInheritanceRecord.getMutatedBy()); line.append(DELIMITER); + line.append(planInheritanceRecord.getIterationCreated()); line.append(DELIMITER); + line.append(planInheritanceRecord.getIterationRemoved()); line.append(DELIMITER); + line.append(planInheritanceRecord.getIterationsSelected()); + + try { + this.writer.write(line.toString()); + this.writer.newLine(); + } catch (IOException e) { + throw new RuntimeException("Could not initialize the plan inheritance writer!", e); + } + } + + public void flush() { + try { + this.writer.flush(); + } catch (IOException e) { + throw new RuntimeException("Failed to flush plan inheritance writer!", e); + } + } + + public void close() { + try { + this.writer.close(); + } catch (IOException e) { + throw new RuntimeException("Failed to close plan inheritance writer!", e); + } + } +} diff --git a/matsim/src/main/java/org/matsim/core/replanning/modules/TripTimeAllocationMutator.java b/matsim/src/main/java/org/matsim/core/replanning/modules/TripTimeAllocationMutator.java deleted file mode 100644 index a30266f7821..00000000000 --- a/matsim/src/main/java/org/matsim/core/replanning/modules/TripTimeAllocationMutator.java +++ /dev/null @@ -1,89 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* - * TransitTimeAllocationMutator.java - * * - * *********************************************************************** * - * * - * copyright : (C) 2009 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.core.replanning.modules; - -import com.google.inject.Inject; -import org.matsim.core.config.Config; -import org.matsim.core.config.groups.PlansConfigGroup; -import org.matsim.core.config.groups.PlansConfigGroup.ActivityDurationInterpretation; -import org.matsim.core.gbl.MatsimRandom; -import org.matsim.core.population.algorithms.PlanAlgorithm; -import org.matsim.core.population.algorithms.TripPlanMutateTimeAllocation; -import org.matsim.core.router.TripRouter; - -import jakarta.inject.Provider; - -/** - * Copy/Paste of TimeAllocationMutator, that calls TransitPlanMutateTimeAllocation instead - * of PlanMutateTimeAllocation. - * - * @author mrieser - */ -public class TripTimeAllocationMutator extends AbstractMultithreadedModule { - - private final Provider tripRouterProvider; - - public final static String CONFIG_GROUP = "TimeAllocationMutator"; - public final static String CONFIG_MUTATION_RANGE = "mutationRange"; - - private double mutationRange = 1800.0; - private boolean useActivityDurations = true; - private final boolean affectingDuration; - - /** - * Creates a new TimeAllocationMutator with a mutation range as defined in - * the configuration (module "TimeAllocationMutator", param "mutationRange"). - */ - public TripTimeAllocationMutator(Config config, Provider tripRouterProvider) { - super(config.global()); - this.tripRouterProvider = tripRouterProvider; - this.mutationRange = config.timeAllocationMutator().getMutationRange() ; - this.affectingDuration = config.timeAllocationMutator().isAffectingDuration() ; - PlansConfigGroup.ActivityDurationInterpretation actDurInterpr = ( config.plans().getActivityDurationInterpretation() ) ; - if ( actDurInterpr == PlansConfigGroup.ActivityDurationInterpretation.minOfDurationAndEndTime ) { - useActivityDurations = true ; - } else if ( actDurInterpr == PlansConfigGroup.ActivityDurationInterpretation.endTimeOnly ) { - useActivityDurations = false ; - } else if ( actDurInterpr == PlansConfigGroup.ActivityDurationInterpretation.tryEndTimeThenDuration ) { - throw new UnsupportedOperationException( "need to clarify the correct setting here. Probably not a big deal, but not done yet. kai, aug'10") ; - } else { - throw new IllegalStateException( "beahvior not defined for this configuration setting") ; - } - } - - public TripTimeAllocationMutator(Config config, Provider tripRouterProvider, final double mutationRange, boolean affectingDuration) { - super(config.global()); - this.tripRouterProvider = tripRouterProvider; - this.mutationRange = mutationRange; - this.affectingDuration = affectingDuration; - } - - @Override - public PlanAlgorithm getPlanAlgoInstance() { - TripPlanMutateTimeAllocation pmta = - new TripPlanMutateTimeAllocation( - this.mutationRange, - affectingDuration, MatsimRandom.getLocalInstance()); - pmta.setUseActivityDurations(this.useActivityDurations); - return pmta; - } - -} diff --git a/matsim/src/main/java/org/matsim/core/replanning/strategies/TimeAllocationMutator.java b/matsim/src/main/java/org/matsim/core/replanning/strategies/TimeAllocationMutator.java index 0d7ab5c75c6..c1fbe50e538 100644 --- a/matsim/src/main/java/org/matsim/core/replanning/strategies/TimeAllocationMutator.java +++ b/matsim/src/main/java/org/matsim/core/replanning/strategies/TimeAllocationMutator.java @@ -35,14 +35,11 @@ public class TimeAllocationMutator implements Provider { @Inject private GlobalConfigGroup globalConfigGroup; @Inject private TimeAllocationMutatorConfigGroup timeAllocationMutatorConfigGroup; @Inject private PlansConfigGroup plansConfigGroup; - @Inject private Provider tripRouterProvider; - @Inject private Population population; @Override public PlanStrategy get() { PlanStrategyImpl strategy = new PlanStrategyImpl(new RandomPlanSelector()); - TimeAllocationMutatorModule tam = new TimeAllocationMutatorModule(this.tripRouterProvider, - this.plansConfigGroup, this.timeAllocationMutatorConfigGroup, this.globalConfigGroup, population); + TimeAllocationMutatorModule tam = new TimeAllocationMutatorModule( this.timeAllocationMutatorConfigGroup, this.globalConfigGroup); strategy.addStrategyModule(tam); return strategy; } diff --git a/matsim/src/main/java/org/matsim/core/replanning/strategies/TimeAllocationMutatorModule.java b/matsim/src/main/java/org/matsim/core/replanning/strategies/TimeAllocationMutatorModule.java index 2486e8dbf77..485089a53ee 100644 --- a/matsim/src/main/java/org/matsim/core/replanning/strategies/TimeAllocationMutatorModule.java +++ b/matsim/src/main/java/org/matsim/core/replanning/strategies/TimeAllocationMutatorModule.java @@ -22,121 +22,45 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.matsim.api.core.v01.population.Population; import org.matsim.api.core.v01.replanning.PlanStrategyModule; -import org.matsim.core.config.Config; -import org.matsim.core.config.ConfigGroup; import org.matsim.core.config.groups.GlobalConfigGroup; -import org.matsim.core.config.groups.PlansConfigGroup; import org.matsim.core.config.groups.TimeAllocationMutatorConfigGroup; -import org.matsim.core.config.groups.TimeAllocationMutatorConfigGroup.TimeAllocationMutatorSubpopulationSettings; import org.matsim.core.gbl.MatsimRandom; import org.matsim.core.population.algorithms.PlanAlgorithm; -import org.matsim.core.population.algorithms.PlanMutateTimeAllocationSimplified; -import org.matsim.core.population.algorithms.TripPlanMutateTimeAllocation; +import org.matsim.core.population.algorithms.MutateActivityTimeAllocation; import org.matsim.core.replanning.modules.AbstractMultithreadedModule; -import org.matsim.core.router.TripRouter; -import org.matsim.core.router.TripStructureUtils.StageActivityHandling; - -import jakarta.inject.Provider; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; /** - * Wraps the {@link org.matsim.core.population.algorithms.PlanMutateTimeAllocation}- + * Wraps the {@link org.matsim.core.population.algorithms.TripPlanMutateTimeAllocation}- * PlanAlgorithm into a {@link PlanStrategyModule} so it can be used for plans * replanning. Supports multiple threads. * * @author mrieser - * @see org.matsim.core.population.algorithms.PlanMutateTimeAllocation + * @see org.matsim.core.population.algorithms.TripPlanMutateTimeAllocation */ class TimeAllocationMutatorModule extends AbstractMultithreadedModule{ - private static boolean ACTIVITY_DURATION_WARNING_SHOWN = false; private static final Logger log = LogManager.getLogger( TimeAllocationMutatorModule.class ); - private final double mutationRange; private final boolean affectingDuration; -// private final String subpopulationAttribute; - private final Map subpopulationMutationRanges; - private final Map subpopulationAffectingDuration; - private final PlansConfigGroup.ActivityDurationInterpretation activityDurationInterpretation; - - /** - * Creates a new TimeAllocationMutator with a mutation range as defined in - * the configuration (module "TimeAllocationMutator", param "mutationRange"). - */ - @Deprecated - TimeAllocationMutatorModule( Config config, Provider tripRouterProvider, final double mutationRange, boolean affectingDuration ) { - super(config.global()); - this.affectingDuration = affectingDuration; - this.mutationRange = mutationRange; - this.activityDurationInterpretation = (config.plans().getActivityDurationInterpretation()); -// this.subpopulationAttribute = null; - this.subpopulationMutationRanges = null; - this.subpopulationAffectingDuration = null; - log.warn("deprecated constructor was used - individual time allocation mutator settings for subpopulations is not supported!"); - } + private final TimeAllocationMutatorConfigGroup timeAllocationMutatorConfigGroup; - TimeAllocationMutatorModule( Provider tripRouterProvider, PlansConfigGroup plansConfigGroup, TimeAllocationMutatorConfigGroup timeAllocationMutatorConfigGroup, GlobalConfigGroup globalConfigGroup ) { - this(tripRouterProvider, plansConfigGroup, timeAllocationMutatorConfigGroup, globalConfigGroup, null); - } - TimeAllocationMutatorModule( Provider tripRouterProvider, PlansConfigGroup plansConfigGroup, TimeAllocationMutatorConfigGroup timeAllocationMutatorConfigGroup, GlobalConfigGroup globalConfigGroup, - final Population population ) { + TimeAllocationMutatorModule( TimeAllocationMutatorConfigGroup timeAllocationMutatorConfigGroup, GlobalConfigGroup globalConfigGroup) { super(globalConfigGroup); - this.activityDurationInterpretation = plansConfigGroup.getActivityDurationInterpretation(); this.mutationRange = timeAllocationMutatorConfigGroup.getMutationRange(); this.affectingDuration = timeAllocationMutatorConfigGroup.isAffectingDuration(); + this.timeAllocationMutatorConfigGroup = timeAllocationMutatorConfigGroup; - // in case we have subpopulations and individual settings for them - if ( -// plansConfigGroup.getSubpopulationAttributeName() != null && - timeAllocationMutatorConfigGroup.isUseIndividualSettingsForSubpopulations() && population != null) { -// this.subpopulationAttribute = plansConfigGroup.getSubpopulationAttributeName(); - this.subpopulationMutationRanges = new HashMap<>(); - this.subpopulationAffectingDuration = new HashMap<>(); - - Collection settings = timeAllocationMutatorConfigGroup.getParameterSets(TimeAllocationMutatorSubpopulationSettings.SET_NAME); - for (ConfigGroup group : settings) { - TimeAllocationMutatorSubpopulationSettings subpopulationSettings = (TimeAllocationMutatorSubpopulationSettings) group; - String subpopulation = subpopulationSettings.getSubpopulation(); - this.subpopulationMutationRanges.put(subpopulation, subpopulationSettings.getMutationRange()); - this.subpopulationAffectingDuration.put(subpopulation, subpopulationSettings.isAffectingDuration()); - log.info("Found individual time mutator settings for subpopulation: " + subpopulation); - } - } else { -// this.subpopulationAttribute = null; - this.subpopulationMutationRanges = null; - this.subpopulationAffectingDuration = null; - } } @Override public PlanAlgorithm getPlanAlgoInstance() { - PlanAlgorithm pmta; - switch (this.activityDurationInterpretation) { - case minOfDurationAndEndTime: - pmta = new TripPlanMutateTimeAllocation(this.mutationRange, this.affectingDuration, MatsimRandom.getLocalInstance(), - this.subpopulationMutationRanges, this.subpopulationAffectingDuration); - break; - default: - if(this.affectingDuration) { - if (!ACTIVITY_DURATION_WARNING_SHOWN) { - log.warn("Please be aware that durations of activities now can mutate freely and possibly become negative." + - "This might be a problem if you have \n" + - "a) short activities that are only provided with duration and not with endtime AND\n" + - "b) agents with only one or two initial plans.\n" + - "This can have impact on scoring and maybe even on qsim execution. It is recommended to set affectingDuration=false for such set up."); - ACTIVITY_DURATION_WARNING_SHOWN = true; - } - } - pmta = new PlanMutateTimeAllocationSimplified( - // TODO: is StageActivityHandling.ExcludeStageActivities right here? - StageActivityHandling.ExcludeStageActivities, this.mutationRange, this.affectingDuration, MatsimRandom.getLocalInstance()); - } + PlanAlgorithm pmta = new MutateActivityTimeAllocation + (this.mutationRange, this.affectingDuration, MatsimRandom.getLocalInstance(), + timeAllocationMutatorConfigGroup.getLatestActivityEndTime(), timeAllocationMutatorConfigGroup.isMutateAroundInitialEndTimeOnly(), + timeAllocationMutatorConfigGroup.getMutationRangeStep()); return pmta; } } diff --git a/matsim/src/main/java/org/matsim/core/replanning/strategies/TimeAllocationMutatorReRoute.java b/matsim/src/main/java/org/matsim/core/replanning/strategies/TimeAllocationMutatorReRoute.java index 009e9e0199c..f430d0d3004 100644 --- a/matsim/src/main/java/org/matsim/core/replanning/strategies/TimeAllocationMutatorReRoute.java +++ b/matsim/src/main/java/org/matsim/core/replanning/strategies/TimeAllocationMutatorReRoute.java @@ -43,13 +43,12 @@ public class TimeAllocationMutatorReRoute implements Provider { @Inject private TimeAllocationMutatorConfigGroup timeAllocationMutatorConfigGroup; @Inject private PlansConfigGroup plansConfigGroup; @Inject private ActivityFacilities activityFacilities; - @Inject private Population population; @Inject private TimeInterpretation timeInterpretation; @Override public PlanStrategy get() { final PlanStrategyImpl strategy = new PlanStrategyImpl(new RandomPlanSelector()); - strategy.addStrategyModule(new TimeAllocationMutatorModule(this.tripRouterProvider, this.plansConfigGroup, this.timeAllocationMutatorConfigGroup, this.globalConfigGroup, this.population) ); + strategy.addStrategyModule(new TimeAllocationMutatorModule(this.timeAllocationMutatorConfigGroup, this.globalConfigGroup) ); strategy.addStrategyModule(new ReRoute(this.activityFacilities, this.tripRouterProvider, this.globalConfigGroup, this.timeInterpretation)); return strategy; } diff --git a/matsim/src/main/java/org/matsim/utils/objectattributes/ObjectAttributesConverter.java b/matsim/src/main/java/org/matsim/utils/objectattributes/ObjectAttributesConverter.java index ee7eed06f03..6c0e0afbfe3 100644 --- a/matsim/src/main/java/org/matsim/utils/objectattributes/ObjectAttributesConverter.java +++ b/matsim/src/main/java/org/matsim/utils/objectattributes/ObjectAttributesConverter.java @@ -26,6 +26,7 @@ import org.apache.logging.log4j.Logger; import org.matsim.utils.objectattributes.attributeconverters.*; import org.matsim.api.core.v01.Coord; +import org.matsim.vehicles.PersonVehicleTypes; import org.matsim.vehicles.PersonVehicles; import java.util.*; @@ -60,6 +61,7 @@ public ObjectAttributesConverter() { this.converters.put(Coord.class.getName(), new CoordConverter()); this.converters.put(Coord[].class.getName(), new CoordArrayConverter()); this.converters.put(PersonVehicles.class.getName(), new PersonVehiclesAttributeConverter()); + this.converters.put(PersonVehicleTypes.class.getName(), new PersonVehicleTypesAttributeConverter()); } //this is for reading diff --git a/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/PersonVehicleTypesAttributeConverter.java b/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/PersonVehicleTypesAttributeConverter.java new file mode 100644 index 00000000000..9c01e8ab9c6 --- /dev/null +++ b/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/PersonVehicleTypesAttributeConverter.java @@ -0,0 +1,42 @@ +package org.matsim.utils.objectattributes.attributeconverters; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.Id; +import org.matsim.utils.objectattributes.AttributeConverter; +import org.matsim.vehicles.PersonVehicleTypes; +import org.matsim.vehicles.VehicleType; + +import java.util.HashMap; +import java.util.Map; + +/** + * Converter to store vehicle types as person attribute. + */ +public class PersonVehicleTypesAttributeConverter implements AttributeConverter { + + private final Logger logger = LogManager.getLogger(PersonVehicleTypesAttributeConverter.class); + + @Override + public PersonVehicleTypes convert(String value) { + PersonVehicleTypes vehicles = new PersonVehicleTypes(); + Map stringMap = new StringStringMapConverter().convert(value); + for (Map.Entry entry: stringMap.entrySet()) { + vehicles.addModeVehicleType(entry.getKey(), Id.create(entry.getValue(), VehicleType.class)); + } + return vehicles; + } + + @Override + public String convertToString(Object o) { + if(!(o instanceof PersonVehicleTypes vehicles)){ + logger.error("Object is not of type PersonVehicles: " + o.getClass()); + return null; + } + Map stringMap = new HashMap<>(); + for (Map.Entry> entry: vehicles.getModeVehicleTypes().entrySet()) { + stringMap.put(entry.getKey(), entry.getValue().toString()); + } + return new StringStringMapConverter().convertToString(stringMap); + } +} diff --git a/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/PersonVehiclesAttributeConverter.java b/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/PersonVehiclesAttributeConverter.java index dd5440df404..96484c502a2 100644 --- a/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/PersonVehiclesAttributeConverter.java +++ b/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/PersonVehiclesAttributeConverter.java @@ -26,12 +26,11 @@ public PersonVehicles convert(String value) { @Override public String convertToString(Object o) { - if(!(o instanceof PersonVehicles)){ - logger.error("Object is not of type PersonVehicles: " + o.getClass().toString()); + if(!(o instanceof PersonVehicles vehicles)){ + logger.error("Object is not of type PersonVehicles: " + o.getClass()); return null; } - PersonVehicles vehicles = (PersonVehicles)o; - Map stringMap = new HashMap<>(); + Map stringMap = new HashMap<>(); for (Map.Entry> entry: vehicles.getModeVehicles().entrySet()) { stringMap.put(entry.getKey(), entry.getValue().toString()); } diff --git a/matsim/src/main/java/org/matsim/vehicles/PersonVehicleTypes.java b/matsim/src/main/java/org/matsim/vehicles/PersonVehicleTypes.java new file mode 100644 index 00000000000..e07198587bb --- /dev/null +++ b/matsim/src/main/java/org/matsim/vehicles/PersonVehicleTypes.java @@ -0,0 +1,33 @@ +package org.matsim.vehicles; + +import org.matsim.api.core.v01.Id; + +import java.util.HashMap; +import java.util.Map; + +/** + * Container class to store mode specific person vehicle types. + */ +public final class PersonVehicleTypes { + + private final Map> modeVehicleTypes = new HashMap<>(); + + public PersonVehicleTypes() { + } + + public void addModeVehicleType(String mode, Id vehicleType) { + modeVehicleTypes.put(mode, vehicleType); + } + + public Id getVehicleType(String mode) { + return modeVehicleTypes.get(mode); + } + + public Map> getModeVehicleTypes() { + return modeVehicleTypes; + } + + public void putModeVehicleTypes(Map> vehicleTypes) { + modeVehicleTypes.putAll(vehicleTypes); + } +} diff --git a/matsim/src/main/java/org/matsim/vehicles/VehicleUtils.java b/matsim/src/main/java/org/matsim/vehicles/VehicleUtils.java index e3594e65885..0141e814600 100644 --- a/matsim/src/main/java/org/matsim/vehicles/VehicleUtils.java +++ b/matsim/src/main/java/org/matsim/vehicles/VehicleUtils.java @@ -41,6 +41,7 @@ public final class VehicleUtils { private static final VehicleType DEFAULT_VEHICLE_TYPE = VehicleUtils.getFactory().createVehicleType(Id.create("defaultVehicleType", VehicleType.class)); private static final String VEHICLE_ATTRIBUTE_KEY = "vehicles"; + private static final String VEHICLE_TYPES_ATTRIBUTE_KEY = "vehicleTypes"; // should remain under the hood --> should remain private private static final String DOOR_OPERATION_MODE = "doorOperationMode" ; @@ -117,11 +118,11 @@ public static void copyFromTo( VehicleType in, VehicleType out ) { /** * Checks whether a person has a vehicle id for mode - without throwing an * exception if not. - * + * * @param person the person one wants to check for a vehicle id * @param mode the mode for the vehicle id to check * @return whether person has a vehicle id for that mode - * + * * @see {@link VehicleUtils#getVehicleId(Person, String)} */ public static boolean hasVehicleId(Person person, String mode) { @@ -147,6 +148,15 @@ public static Map> getVehicleIds(Person person) { return personVehicles.getModeVehicles(); } + /** + * Retrieve the optional vehicle types per mode that might be assigned to a person. + * Returns null if this is not defined. + */ + public static Map> getVehicleTypes(Person person) { + var personVehicles = (PersonVehicleTypes) person.getAttributes().getAttribute(VehicleUtils.VEHICLE_TYPES_ATTRIBUTE_KEY); + return personVehicles != null ? personVehicles.getModeVehicleTypes() : null; + } + /** * Retrieves a vehicleId from the person's attributes. * @@ -179,13 +189,32 @@ public static void insertVehicleIdsIntoAttributes(Person person, Map> modeToVehicleCopy = new HashMap<>(modeToVehicle); PersonVehicles personVehicles; if (attr == null) { - personVehicles = new PersonVehicles(modeToVehicleCopy); + personVehicles = new PersonVehicles(); } else { personVehicles = (PersonVehicles) attr; } personVehicles.addModeVehicleList(modeToVehicleCopy); person.getAttributes().putAttribute(VEHICLE_ATTRIBUTE_KEY, personVehicles); } + + /** + * Attaches vehicle types to a person, so that the router knows which vehicle to use for which mode and person. + * @param modeToVehicleType mode string mapped to vehicle type ids. The provided map is copied and stored as unmodifiable map. + */ + public static void insertVehicleTypesIntoAttributes(Person person, Map> modeToVehicleType) { + Object attr = person.getAttributes().getAttribute(VEHICLE_TYPES_ATTRIBUTE_KEY); + + Map> modeToTypesCopy = new HashMap<>(modeToVehicleType); + PersonVehicleTypes personVehiclesTypes; + if (attr == null) { + personVehiclesTypes = new PersonVehicleTypes(); + } else { + personVehiclesTypes = (PersonVehicleTypes) attr; + } + personVehiclesTypes.putModeVehicleTypes(modeToTypesCopy); + person.getAttributes().putAttribute(VEHICLE_TYPES_ATTRIBUTE_KEY, personVehiclesTypes); + } + //******** general VehicleType attributes ************ public static VehicleType.DoorOperationMode getDoorOperationMode( VehicleType vehicleType ){ diff --git a/matsim/src/test/java/org/matsim/core/controler/PrepareForSimImplTest.java b/matsim/src/test/java/org/matsim/core/controler/PrepareForSimImplTest.java index b01bd15d4b7..d711ebceaaa 100644 --- a/matsim/src/test/java/org/matsim/core/controler/PrepareForSimImplTest.java +++ b/matsim/src/test/java/org/matsim/core/controler/PrepareForSimImplTest.java @@ -19,10 +19,7 @@ package org.matsim.core.controler; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import org.junit.Assert; import org.junit.Test; @@ -55,6 +52,11 @@ import org.matsim.core.utils.timing.TimeInterpretation; import com.google.inject.Provider; +import org.matsim.vehicles.Vehicle; +import org.matsim.vehicles.VehicleType; +import org.matsim.vehicles.VehicleUtils; + +import static org.assertj.core.api.Assertions.assertThat; /** * Mostly tests adaptation of old plans to routing mode and the related replacement of helper modes for access and egress @@ -760,6 +762,48 @@ public void testOutdatedFallbackAndHelperModesReplacement() { } } + @Test + public void vehicleTypes() { + + Config config = ConfigUtils.createConfig(); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + Scenario scenario = ScenarioUtils.createScenario(config); + createAndAddNetwork(scenario); + Population pop = scenario.getPopulation(); + PopulationFactory f = pop.getFactory(); + + // add truck type + VehicleType truckType = scenario.getVehicles().getFactory().createVehicleType(Id.create("truck", VehicleType.class)); + scenario.getVehicles().addVehicleType(truckType); + + // Create test person + Person p1 = f.createPerson(Id.createPersonId("1")); + { + VehicleUtils.insertVehicleTypesIntoAttributes(p1, Map.of(TransportMode.car, Id.create("truck", VehicleType.class))); + + Plan plan = f.createPlan(); + Activity act = f.createActivityFromCoord("home", new Coord(0, 0)); + act.setEndTime(3600); + plan.addActivity(act); + plan.addLeg(f.createLeg(TransportMode.car)); + plan.addActivity(f.createActivityFromCoord("work", new Coord(1000, 0))); + p1.addPlan(plan); + pop.addPerson(p1); + } + + // run prepare + final PrepareForSimImpl prepareForSimImpl = new PrepareForSimImpl(config.global(), scenario, scenario.getNetwork(), + pop, scenario.getActivityFacilities(), new DummyTripRouterProvider(), config.qsim(), config.facilities(), + config.plans(), new MainModeIdentifierImpl(), TimeInterpretation.create(config)); + + prepareForSimImpl.run(); + + Id id = VehicleUtils.getVehicleId(p1, TransportMode.car); + assertThat(scenario.getVehicles().getVehicles().get(id).getType()) + .isEqualTo(truckType); + + } + private class DummyTripRouterProvider implements Provider { @Override public TripRouter get() { diff --git a/matsim/src/test/java/org/matsim/core/replanning/planInheritance/PlanInheritanceTest.java b/matsim/src/test/java/org/matsim/core/replanning/planInheritance/PlanInheritanceTest.java new file mode 100644 index 00000000000..c15436d3391 --- /dev/null +++ b/matsim/src/test/java/org/matsim/core/replanning/planInheritance/PlanInheritanceTest.java @@ -0,0 +1,129 @@ +package org.matsim.core.replanning.planInheritance; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +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.population.Person; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.core.config.Config; +import org.matsim.core.controler.Controler; +import org.matsim.core.population.algorithms.PersonAlgorithm; +import org.matsim.core.population.io.StreamingPopulationReader; +import org.matsim.core.replanning.inheritance.PlanInheritanceModule; +import org.matsim.core.replanning.inheritance.PlanInheritanceRecord; +import org.matsim.core.replanning.inheritance.PlanInheritanceRecordReader; +import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.testcases.MatsimTestUtils; + + + +public class PlanInheritanceTest { + /** + * @author alex94263 + */ + + + @Rule + public MatsimTestUtils util = new MatsimTestUtils(); + + @Test + public void testPlanInheritanceEnabled() throws IOException { + String outputDirectory = util.getOutputDirectory(); + + Config config = this.util.loadConfig("test/scenarios/equil/config_plans1.xml"); + config.controller().setLastIteration(10); + config.controller().setOutputDirectory(outputDirectory); + config.planInheritance().setEnabled(true); + Controler c = new Controler(config); + + c.run(); + File csv = new File(outputDirectory, "planInheritanceRecords.csv.gz"); + + assertThat(csv).exists(); + + + List personList = new ArrayList(); + final Scenario scenario = ScenarioUtils.createScenario(config); + StreamingPopulationReader spr = new StreamingPopulationReader(scenario); + spr.addAlgorithm(new PersonAlgorithm() { + @Override + public void run(Person person) { + personList.add(person); + } + }); + spr.readFile(util.getOutputDirectory()+"output_plans.xml.gz"); + for(Person per : personList) { + for(Plan p : per.getPlans()) { + assert(p.getAttributes().getAsMap().keySet().contains(PlanInheritanceModule.PLAN_MUTATOR)); + assert(p.getAttributes().getAsMap().keySet().contains(PlanInheritanceModule.ITERATION_CREATED)); + assert(p.getAttributes().getAsMap().keySet().contains(PlanInheritanceModule.PLAN_ID)); + } + + } + + PlanInheritanceRecordReader reader = new PlanInheritanceRecordReader(outputDirectory+"planInheritanceRecords.csv.gz"); + List records = reader.read(); + assert(records.size()==2); + assert( ((PlanInheritanceRecord) records.get(0)).getAgentId().equals(Id.createPersonId("1"))); + assert( ((PlanInheritanceRecord) records.get(0)).getAncestorId().equals(Id.create("NONE",Plan.class))); + assert( ((PlanInheritanceRecord) records.get(0)).getMutatedBy().equals(PlanInheritanceModule.INITIAL_PLAN)); + assert( ((PlanInheritanceRecord) records.get(0)).getIterationCreated() == 0); + assert( ((PlanInheritanceRecord) records.get(0)).getIterationRemoved() == -1); + assert( ((PlanInheritanceRecord) records.get(0)).getPlanId().equals(Id.create("1",Plan.class))); + assert( ((PlanInheritanceRecord) records.get(0)).getIterationsSelected().equals(Arrays.asList(0, 1, 2, 3, 4, 6, 7, 8, 9, 10))); + + assert( ((PlanInheritanceRecord) records.get(1)).getAgentId().equals(Id.createPersonId("1"))); + assert( ((PlanInheritanceRecord) records.get(1)).getAncestorId().equals(Id.create("1",Plan.class))); + assert( ((PlanInheritanceRecord) records.get(1)).getMutatedBy().equals("RandomPlanSelector_ReRoute")); + assert( ((PlanInheritanceRecord) records.get(1)).getIterationCreated() == 5); + assert( ((PlanInheritanceRecord) records.get(1)).getIterationRemoved() == -1); + assert( ((PlanInheritanceRecord) records.get(1)).getPlanId().equals(Id.create("2",Plan.class))); + assert( ((PlanInheritanceRecord) records.get(1)).getIterationsSelected().equals(Arrays.asList(5))); + + + + } + + @Test + public void testPlanInheritanceDisabled() throws IOException { + String outputDirectory = util.getOutputDirectory(); + + Config config = this.util.loadConfig("test/scenarios/equil/config_plans1.xml"); + config.controller().setLastIteration(1); + config.controller().setOutputDirectory(outputDirectory); + Controler c = new Controler(config); + + c.run(); + + File csv = new File(outputDirectory, "planInheritanceRecords.csv.gz"); + + assertThat(csv).doesNotExist(); + + List personList = new ArrayList(); + final Scenario scenario = ScenarioUtils.createScenario(config); + StreamingPopulationReader spr = new StreamingPopulationReader(scenario); + spr.addAlgorithm(new PersonAlgorithm() { + @Override + public void run(Person person) { + personList.add(person); + } + }); + spr.readFile(util.getOutputDirectory()+"output_plans.xml.gz"); + for(Person per : personList) { + for(Plan p : per.getPlans()) { + assert(!p.getAttributes().getAsMap().keySet().contains(PlanInheritanceModule.PLAN_MUTATOR)); + assert(!p.getAttributes().getAsMap().keySet().contains(PlanInheritanceModule.ITERATION_CREATED)); + assert(!p.getAttributes().getAsMap().keySet().contains(PlanInheritanceModule.PLAN_ID)); + } + + } + } +} diff --git a/matsim/src/test/java/org/matsim/core/replanning/strategies/DeterministicMultithreadedReplanningIT.java b/matsim/src/test/java/org/matsim/core/replanning/strategies/DeterministicMultithreadedReplanningIT.java index be3a2df553f..122f9acc428 100644 --- a/matsim/src/test/java/org/matsim/core/replanning/strategies/DeterministicMultithreadedReplanningIT.java +++ b/matsim/src/test/java/org/matsim/core/replanning/strategies/DeterministicMultithreadedReplanningIT.java @@ -78,7 +78,7 @@ public void testTimeAllocationMutator() { config.controller().setOutputDirectory(testUtils.getOutputDirectory() + "/run1/"); TestControler controler = new TestControler(config, strategyManager); PlanStrategyImpl strategy = new PlanStrategyImpl(new RandomPlanSelector()); - strategy.addStrategyModule(new TimeAllocationMutatorModule(TripRouterFactoryBuilderWithDefaults.createDefaultTripRouterFactoryImpl(controler.getScenario() ), config.plans(), config.timeAllocationMutator(), config.global()) ); + strategy.addStrategyModule(new TimeAllocationMutatorModule( config.timeAllocationMutator(), config.global()) ); strategyManager.addStrategy( strategy, null, 1.0 ); controler.run(); } @@ -88,7 +88,7 @@ public void testTimeAllocationMutator() { config.controller().setOutputDirectory(testUtils.getOutputDirectory() + "/run2/"); TestControler controler = new TestControler(config, strategyManager); PlanStrategyImpl strategy = new PlanStrategyImpl(new RandomPlanSelector()); - strategy.addStrategyModule(new TimeAllocationMutatorModule(TripRouterFactoryBuilderWithDefaults.createDefaultTripRouterFactoryImpl(controler.getScenario() ), config.plans(), config.timeAllocationMutator(), config.global()) ); + strategy.addStrategyModule(new TimeAllocationMutatorModule(config.timeAllocationMutator(), config.global()) ); strategyManager.addStrategy( strategy, null, 1.0 ); controler.run(); } @@ -129,7 +129,7 @@ public void testReRouteTimeAllocationMutator() { config.controller().setOutputDirectory(testUtils.getOutputDirectory() + "/run1/"); TestControler controler = new TestControler(config, strategyManager); strategy.addStrategyModule(new ReRoute(controler.getScenario(), TripRouterFactoryBuilderWithDefaults.createDefaultTripRouterFactoryImpl(controler.getScenario()), TimeInterpretation.create(config))); // finish strategy configuration - strategy.addStrategyModule(new TimeAllocationMutatorModule(TripRouterFactoryBuilderWithDefaults.createDefaultTripRouterFactoryImpl(controler.getScenario() ), config.plans(), config.timeAllocationMutator(), config.global()) ); + strategy.addStrategyModule(new TimeAllocationMutatorModule( config.timeAllocationMutator(), config.global()) ); controler.run(); } { @@ -142,7 +142,7 @@ public void testReRouteTimeAllocationMutator() { config.controller().setOutputDirectory(testUtils.getOutputDirectory() + "/run2/"); TestControler controler2 = new TestControler(config, strategyManager2); strategy2.addStrategyModule(new ReRoute(controler2.getScenario(), TripRouterFactoryBuilderWithDefaults.createDefaultTripRouterFactoryImpl(controler2.getScenario()), TimeInterpretation.create(config))); // finish strategy configuration - strategy2.addStrategyModule(new TimeAllocationMutatorModule(TripRouterFactoryBuilderWithDefaults.createDefaultTripRouterFactoryImpl(controler2.getScenario() ), config.plans(), config.timeAllocationMutator(), config.global()) ); + strategy2.addStrategyModule(new TimeAllocationMutatorModule(config.timeAllocationMutator(), config.global()) ); controler2.run(); } @@ -275,7 +275,7 @@ public void testReRoute() { private static class TestControler { Controler controler ; - private StrategyManager manager; + private final StrategyManager manager; public TestControler(final Config config, final StrategyManager manager) { this( ScenarioUtils.loadScenario( config ) , manager ); diff --git a/matsim/src/test/java/org/matsim/core/replanning/strategies/TimeAllocationMutatorModuleTest.java b/matsim/src/test/java/org/matsim/core/replanning/strategies/TimeAllocationMutatorModuleTest.java index 54ff79db0e3..00e5fa6b164 100644 --- a/matsim/src/test/java/org/matsim/core/replanning/strategies/TimeAllocationMutatorModuleTest.java +++ b/matsim/src/test/java/org/matsim/core/replanning/strategies/TimeAllocationMutatorModuleTest.java @@ -22,40 +22,31 @@ import static org.junit.Assert.assertTrue; -import jakarta.inject.Provider; - +import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.matsim.api.core.v01.Coord; 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.Activity; -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.ConfigUtils; -import org.matsim.core.config.groups.TimeAllocationMutatorConfigGroup.TimeAllocationMutatorSubpopulationSettings; +import org.matsim.api.core.v01.population.*; import org.matsim.core.gbl.MatsimRandom; import org.matsim.core.network.NetworkUtils; import org.matsim.core.population.PersonUtils; import org.matsim.core.population.PopulationUtils; import org.matsim.core.population.algorithms.PlanAlgorithm; -import org.matsim.core.population.algorithms.PlanMutateTimeAllocation; -import org.matsim.core.population.algorithms.PlanMutateTimeAllocationSimplified; -import org.matsim.core.population.algorithms.TripPlanMutateTimeAllocation; -import org.matsim.core.router.TripRouter; -import org.matsim.core.router.TripStructureUtils.StageActivityHandling; -import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.core.population.algorithms.MutateActivityTimeAllocation; import org.matsim.core.utils.misc.Time; +import org.matsim.pt.PtConstants; import org.matsim.testcases.MatsimTestUtils; +import java.util.Random; + /** * Tests the functionality of {@link TimeAllocationMutatorModule}, mainly that the - * correct mutation range is handed over to the underlying {@link PlanMutateTimeAllocation}. + * correct mutation range is handed over to the underlying {@link MutateActivityTimeAllocation}. * * @author mrieser */ @@ -65,141 +56,11 @@ public class TimeAllocationMutatorModuleTest { public MatsimTestUtils utils = new MatsimTestUtils(); - /** - * Tests that the mutation range given in the constructor is respected. - * - * @author mrieser - */ - @Test public void testMutationRangeParam() { - boolean affectingDuration = true ; - - runMutationRangeTest(new TripPlanMutateTimeAllocation(750, affectingDuration, MatsimRandom.getLocalInstance()), 750); - runMutationRangeTest(new TripPlanMutateTimeAllocation(7200, affectingDuration, MatsimRandom.getLocalInstance()), 7200); - } - @Test public void testSimplifiedMutation() { boolean affectingDuration = true ; - runSimplifiedMutationRangeTest(new PlanMutateTimeAllocationSimplified(StageActivityHandling.ExcludeStageActivities, 750, affectingDuration, MatsimRandom.getLocalInstance()), 750); - runSimplifiedMutationRangeTest(new PlanMutateTimeAllocationSimplified(StageActivityHandling.ExcludeStageActivities, 7200, affectingDuration, MatsimRandom.getLocalInstance()), 7200); - } - - @Test public void testSubpopulations() { - - String cbSubpopulation = "cb"; - String freightSubpopulation = "freight"; - - Config config = ConfigUtils.createConfig(); -// config.plans().setSubpopulationAttributeName("subpopulation"); - config.timeAllocationMutator().setUseIndividualSettingsForSubpopulations(true); - config.timeAllocationMutator().setMutationRange(1800.0); - - TimeAllocationMutatorSubpopulationSettings cbSettings = (TimeAllocationMutatorSubpopulationSettings) config.timeAllocationMutator().createParameterSet(TimeAllocationMutatorSubpopulationSettings.SET_NAME); - cbSettings.setSubpopulation(cbSubpopulation); - cbSettings.setAffectingDuration(false); - cbSettings.setMutationRange(100.0); - config.timeAllocationMutator().addParameterSet(cbSettings); - - TimeAllocationMutatorSubpopulationSettings freightSettings = (TimeAllocationMutatorSubpopulationSettings) config.timeAllocationMutator().createParameterSet(TimeAllocationMutatorSubpopulationSettings.SET_NAME); - freightSettings.setSubpopulation(freightSubpopulation); - freightSettings.setAffectingDuration(true); - freightSettings.setMutationRange(7200.0); - config.timeAllocationMutator().addParameterSet(freightSettings); - - Provider tripRouterProvider = null; - - TimeAllocationMutatorModule mutator = new TimeAllocationMutatorModule(tripRouterProvider, config.plans(), config.timeAllocationMutator(), config.global()); - - Scenario scenario = ScenarioUtils.createScenario(config); - - TimeAllocationMutatorModule cbMutator = new TimeAllocationMutatorModule(tripRouterProvider, config.plans(), config.timeAllocationMutator(), config.global(), scenario.getPopulation()); -// assertEquals(false, cbMutator.affectingDuration); -// assertEquals(100.0, cbMutator.mutationRange); -// -// TimeAllocationMutator freightMutator = new TimeAllocationMutator(tripRouterProvider, config.plans(), config.timeAllocationMutator(), config.global(), scenario.getPopulation()); -// assertEquals(true, freightMutator.affectingDuration); -// assertEquals(7200.0, freightMutator.mutationRange); - } - - /** - * Internal helper method to run the real test, but with different setups. - * Basically, it creates one plan and calls the given TimeAllocationMutator - * several times with this plans, each time measuring how much the activity - * durations have changed and thus ensuring, the differences are within the - * expected range. - * - * @param tripPlanMutateTimeAllocation A preset TimeAllocationMutator to be used for the tests. - * @param expectedMutationRange The expected range for mutation. - */ - private static void runMutationRangeTest( final PlanAlgorithm tripPlanMutateTimeAllocation, final int expectedMutationRange ) { - // setup network - Network network = NetworkUtils.createNetwork(); - network.setCapacityPeriod(Time.parseTime("01:00:00")); - Node node1 = NetworkUtils.createAndAddNode(network, Id.create("1", Node.class), new Coord((double) 0, (double) 0)); - Node node2 = NetworkUtils.createAndAddNode(network, Id.create("2", Node.class), new Coord((double) 100, (double) 0)); - Node node3 = NetworkUtils.createAndAddNode(network, Id.create("3", Node.class), new Coord((double) 200, (double) 0)); - Node node4 = NetworkUtils.createAndAddNode(network, Id.create("4", Node.class), new Coord((double) 300, (double) 0)); - final Node fromNode = node1; - final Node toNode = node2; - Link link1 = NetworkUtils.createAndAddLink(network,Id.create("0", Link.class), fromNode, toNode, (double) 100, (double) 5, (double) 100, (double) 1 ); - final Node fromNode1 = node2; - final Node toNode1 = node3; - NetworkUtils.createAndAddLink(network,Id.create("1", Link.class), fromNode1, toNode1, (double) 100, (double) 5, (double) 100, (double) 1 ); - final Node fromNode2 = node3; - final Node toNode2 = node4; - NetworkUtils.createAndAddLink(network,Id.create("2", Link.class), fromNode2, toNode2, (double) 100, (double) 5, (double) 100, (double) 1 ); - - // setup person - Plan plan; - Activity act1, act2; - try { - /* The chosen times for the activity durations are such that it is likely - * for the random mutation to reach midnight (either at 00:00:00 or at 24:00:00). - */ - Person person = PopulationUtils.getFactory().createPerson(Id.create("1", Person.class)); - plan = PersonUtils.createAndAddPlan(person, true); - act1 = PopulationUtils.createAndAddActivityFromLinkId(plan, "h", link1.getId()); - act1.setEndTime(4*3600); - PopulationUtils.createAndAddLeg( plan, TransportMode.car ); - act2 = PopulationUtils.createAndAddActivityFromLinkId(plan, "w", link1.getId()); - act2.setMaximumDuration(14*3600); - PopulationUtils.createAndAddLeg( plan, TransportMode.car ); - PopulationUtils.createAndAddActivityFromLinkId(plan, "h", link1.getId()); - } catch (Exception e) { - throw new RuntimeException(e); - } - - // run test - double act1Dur = act1.getEndTime().seconds(); - double minDiff1 = Double.POSITIVE_INFINITY; - double maxDiff1 = Double.NEGATIVE_INFINITY; - double act2Dur = act2.getMaximumDuration().seconds(); - double minDiff2 = Double.POSITIVE_INFINITY; - double maxDiff2 = Double.NEGATIVE_INFINITY; - for (int i = 0; i < 150; i++) { - tripPlanMutateTimeAllocation.run(plan); - // test duration of act1 - double diff = act1Dur - act1.getMaximumDuration().seconds(); - if (diff > maxDiff1) maxDiff1 = diff; - if (diff < minDiff1) minDiff1 = diff; - act1Dur = act1.getMaximumDuration().seconds(); - assertTrue("activity duration cannot be smaller than 0, is " + act1Dur, act1Dur >= 0.0); - // test duration of act2 - diff = act2Dur - act2.getMaximumDuration().seconds(); - if (diff > maxDiff2) maxDiff2 = diff; - if (diff < minDiff2) minDiff2 = diff; - act2Dur = act2.getMaximumDuration().seconds(); - assertTrue("activity duration cannot be smaller than 0, is " + act2Dur, act2Dur >= 0.0); - } - assertTrue("mutation range differences wrong (act1).", minDiff1 <= maxDiff1); - assertTrue("mutation range differences wrong (act2).", minDiff2 <= maxDiff2); - - /* The following asserts are dependent on random numbers. - * But I would still expect that we get up to at least 95% of the limit... */ - assertValueInRange("mutation range out of range (maxDiff1).", maxDiff1, expectedMutationRange*0.95, expectedMutationRange); - assertValueInRange("mutation range out of range (minDiff1).", minDiff1, -expectedMutationRange, -expectedMutationRange*0.95); - assertValueInRange("mutation range out of range (maxDiff2).", maxDiff1, expectedMutationRange*0.95, expectedMutationRange); - assertValueInRange("mutation range out of range (minDiff2).", minDiff2, -expectedMutationRange, -expectedMutationRange*0.95); + runSimplifiedMutationRangeTest(new MutateActivityTimeAllocation( 750, affectingDuration, MatsimRandom.getLocalInstance(),24*3600,false,1), 750); + runSimplifiedMutationRangeTest(new MutateActivityTimeAllocation( 7200, affectingDuration, MatsimRandom.getLocalInstance(),24*3600,false,1), 7200); } /** @@ -216,19 +77,19 @@ private void runSimplifiedMutationRangeTest(final PlanAlgorithm tripPlanMutateTi // setup network Network network = NetworkUtils.createNetwork(); network.setCapacityPeriod(Time.parseTime("01:00:00")); - Node node1 = NetworkUtils.createAndAddNode(network, Id.create("1", Node.class), new Coord((double) 0, (double) 0)); - Node node2 = NetworkUtils.createAndAddNode(network, Id.create("2", Node.class), new Coord((double) 100, (double) 0)); - Node node3 = NetworkUtils.createAndAddNode(network, Id.create("3", Node.class), new Coord((double) 200, (double) 0)); - Node node4 = NetworkUtils.createAndAddNode(network, Id.create("4", Node.class), new Coord((double) 300, (double) 0)); + Node node1 = NetworkUtils.createAndAddNode(network, Id.create("1", Node.class), new Coord(0, 0)); + Node node2 = NetworkUtils.createAndAddNode(network, Id.create("2", Node.class), new Coord(100, 0)); + Node node3 = NetworkUtils.createAndAddNode(network, Id.create("3", Node.class), new Coord(200, 0)); + Node node4 = NetworkUtils.createAndAddNode(network, Id.create("4", Node.class), new Coord(300, 0)); final Node fromNode = node1; final Node toNode = node2; - Link link1 = NetworkUtils.createAndAddLink(network,Id.create("0", Link.class), fromNode, toNode, (double) 100, (double) 5, (double) 100, (double) 1 ); + Link link1 = NetworkUtils.createAndAddLink(network,Id.create("0", Link.class), fromNode, toNode, 100, 5, 100, 1); final Node fromNode1 = node2; final Node toNode1 = node3; - NetworkUtils.createAndAddLink(network,Id.create("1", Link.class), fromNode1, toNode1, (double) 100, (double) 5, (double) 100, (double) 1 ); + NetworkUtils.createAndAddLink(network,Id.create("1", Link.class), fromNode1, toNode1, 100, 5, 100, 1); final Node fromNode2 = node3; final Node toNode2 = node4; - NetworkUtils.createAndAddLink(network,Id.create("2", Link.class), fromNode2, toNode2, (double) 100, (double) 5, (double) 100, (double) 1 ); + NetworkUtils.createAndAddLink(network,Id.create("2", Link.class), fromNode2, toNode2, 100, 5, 100, 1); // setup person Plan plan; @@ -286,4 +147,134 @@ private void runSimplifiedMutationRangeTest(final PlanAlgorithm tripPlanMutateTi private static void assertValueInRange(final String message, final double actual, final double lowerLimit, final double upperLimit) { assertTrue(message + " actual: " + actual + ", range: " + lowerLimit + "..." + upperLimit, (lowerLimit <= actual) && (actual <= upperLimit)); } + + + @Test + public void testRun() { + // setup population with one person + Person person = PopulationUtils.getFactory().createPerson(Id.create(1, Person.class)); + Plan plan = PersonUtils.createAndAddPlan(person, true); + Activity act = PopulationUtils.createAndAddActivityFromCoord(plan, "home", new Coord(0, 0)); + act.setEndTime(8.0 * 3600); + PopulationUtils.createAndAddLeg( plan, TransportMode.transit_walk ); + Activity ptAct1 = PopulationUtils.createAndAddActivityFromCoord(plan, PtConstants.TRANSIT_ACTIVITY_TYPE, new Coord(0, 100)); + ptAct1.setMaximumDuration(0); + PopulationUtils.createAndAddLeg( plan, TransportMode.pt ); + Activity ptAct2 = PopulationUtils.createAndAddActivityFromCoord(plan, PtConstants.TRANSIT_ACTIVITY_TYPE, new Coord(0, 100)); + ptAct2.setMaximumDuration(0); + PopulationUtils.createAndAddLeg( plan, TransportMode.transit_walk ); + act = PopulationUtils.createAndAddActivityFromCoord(plan, "work", new Coord(0, 500)); + act.setEndTime(16*3600); + PopulationUtils.createAndAddLeg( plan, TransportMode.transit_walk ); + Activity ptAct3 = PopulationUtils.createAndAddActivityFromCoord(plan, PtConstants.TRANSIT_ACTIVITY_TYPE, new Coord(0, 100)); + ptAct3.setMaximumDuration(0); + PopulationUtils.createAndAddLeg( plan, TransportMode.pt ); + Activity ptAct4 = PopulationUtils.createAndAddActivityFromCoord(plan, PtConstants.TRANSIT_ACTIVITY_TYPE, new Coord(0, 100)); + ptAct4.setMaximumDuration(0); + PopulationUtils.createAndAddLeg( plan, TransportMode.transit_walk ); + PopulationUtils.createAndAddActivityFromCoord(plan, "work", new Coord(0, 500)); + boolean affectingDuration = true ; + + MutateActivityTimeAllocation mutator = + new MutateActivityTimeAllocation( + 3600., + affectingDuration, new Random(2011),24*3600,false,1); + mutator.run(plan); + + Assert.assertEquals(0.0, ptAct1.getMaximumDuration().seconds(), 1e-8); + Assert.assertEquals(0.0, ptAct2.getMaximumDuration().seconds(), 1e-8); + Assert.assertEquals(0.0, ptAct3.getMaximumDuration().seconds(), 1e-8); + Assert.assertEquals(0.0, ptAct4.getMaximumDuration().seconds(), 1e-8); + } + + @Test + public void testRunLatestEndTime() { + // setup population with one person + Person person = PopulationUtils.getFactory().createPerson(Id.create(1, Person.class)); + Plan plan = PersonUtils.createAndAddPlan(person, true); + Activity act = PopulationUtils.createAndAddActivityFromCoord(plan, "home", new Coord(0, 0)); + act.setEndTime(8.0 * 3600); + PopulationUtils.createAndAddLeg(plan, TransportMode.transit_walk); + Activity ptAct1 = PopulationUtils.createAndAddActivityFromCoord(plan, PtConstants.TRANSIT_ACTIVITY_TYPE, + new Coord(0, 100)); + ptAct1.setMaximumDuration(0); + PopulationUtils.createAndAddLeg(plan, TransportMode.pt); + Activity ptAct2 = PopulationUtils.createAndAddActivityFromCoord(plan, PtConstants.TRANSIT_ACTIVITY_TYPE, + new Coord(0, 100)); + ptAct2.setMaximumDuration(0); + PopulationUtils.createAndAddLeg(plan, TransportMode.transit_walk); + act = PopulationUtils.createAndAddActivityFromCoord(plan, "work", new Coord(0, 500)); + act.setEndTime(38 * 3600); + PopulationUtils.createAndAddLeg(plan, TransportMode.transit_walk); + Activity ptAct3 = PopulationUtils.createAndAddActivityFromCoord(plan, PtConstants.TRANSIT_ACTIVITY_TYPE, + new Coord(0, 100)); + ptAct3.setMaximumDuration(0); + PopulationUtils.createAndAddLeg(plan, TransportMode.pt); + Activity ptAct4 = PopulationUtils.createAndAddActivityFromCoord(plan, PtConstants.TRANSIT_ACTIVITY_TYPE, + new Coord(0, 100)); + ptAct4.setMaximumDuration(0); + PopulationUtils.createAndAddLeg(plan, TransportMode.transit_walk); + PopulationUtils.createAndAddActivityFromCoord(plan, "work", new Coord(0, 500)); + + boolean affectingDuration = true; + final double latestEndTime = 30. * 3600; + + MutateActivityTimeAllocation mutator = + new MutateActivityTimeAllocation( + 3600., + affectingDuration, new Random(2011),latestEndTime,false,1); + + mutator.run(plan); + + Assert.assertEquals(0.0, ptAct1.getMaximumDuration().seconds(), 1e-8); + Assert.assertEquals(0.0, ptAct2.getMaximumDuration().seconds(), 1e-8); + Assert.assertEquals(0.0, ptAct3.getMaximumDuration().seconds(), 1e-8); + Assert.assertEquals(0.0, ptAct4.getMaximumDuration().seconds(), 1e-8); + + // check whether activity times are equal or less than latestEndTime + for (PlanElement pe : plan.getPlanElements()) { + if (pe instanceof Activity activity) { + if (activity.getStartTime().isDefined()) { + Assert.assertTrue(activity.getStartTime().seconds() <= latestEndTime); + } + if (activity.getEndTime().isDefined()) { + Assert.assertTrue(activity.getEndTime().seconds() <= latestEndTime); + } + } + } + } + @Test + public void testLegTimesAreSetCorrectly() { + // setup population with one person + Person person = PopulationUtils.getFactory().createPerson(Id.create(1, Person.class)); + Plan plan = PersonUtils.createAndAddPlan(person, true); + Activity act = PopulationUtils.createAndAddActivityFromCoord(plan, "home", new Coord(0, 0)); + act.setEndTime(8.0 * 3600); + Leg leg1 = PopulationUtils.createAndAddLeg( plan, TransportMode.walk ); + leg1.setDepartureTime(8.0*3600); + leg1.setTravelTime(1800.); + Activity act2 = PopulationUtils.createAndAddActivityFromCoord(plan, "work", new Coord(0, 500)); + act2.setMaximumDuration(8*3600); + Leg leg2 = PopulationUtils.createAndAddLeg( plan, TransportMode.walk ); + leg2.setDepartureTime(16.5*3600); + leg2.setTravelTime(1800.0); + PopulationUtils.createAndAddActivityFromCoord(plan, "work", new Coord(0, 500)); + boolean affectingDuration = true ; + + MutateActivityTimeAllocation mutator = + new MutateActivityTimeAllocation( + 3600., + affectingDuration, new Random(2011),24*3600,false,1); + mutator.run(plan); + + double firstActEndTime = act.getEndTime().seconds(); + double secondActDuration = act2.getMaximumDuration().seconds(); + Assert.assertEquals(firstActEndTime,leg1.getDepartureTime().seconds(), MatsimTestUtils.EPSILON); + Assert.assertEquals(firstActEndTime+secondActDuration+leg1.getTravelTime().seconds(),leg2.getDepartureTime().seconds(), MatsimTestUtils.EPSILON); + + + + } + + } diff --git a/matsim/src/test/java/org/matsim/population/algorithms/TripPlanMutateTimeAllocationTest.java b/matsim/src/test/java/org/matsim/population/algorithms/TripPlanMutateTimeAllocationTest.java deleted file mode 100644 index 4961b6887c0..00000000000 --- a/matsim/src/test/java/org/matsim/population/algorithms/TripPlanMutateTimeAllocationTest.java +++ /dev/null @@ -1,134 +0,0 @@ -/* *********************************************************************** * - * project: org.matsim.* - * * - * *********************************************************************** * - * * - * copyright : (C) 2011 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.population.algorithms; - -import java.util.Random; - -import org.junit.Assert; -import org.junit.Test; -import org.matsim.api.core.v01.Coord; -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.TransportMode; -import org.matsim.api.core.v01.population.Activity; -import org.matsim.api.core.v01.population.Person; -import org.matsim.api.core.v01.population.Plan; -import org.matsim.api.core.v01.population.PlanElement; -import org.matsim.core.population.PersonUtils; -import org.matsim.core.population.PopulationUtils; -import org.matsim.core.population.algorithms.TripPlanMutateTimeAllocation; -import org.matsim.pt.PtConstants; - -/** - * @author mrieser - */ -public class TripPlanMutateTimeAllocationTest { - - @Test - public void testRun() { - // setup population with one person - Person person = (Person) PopulationUtils.getFactory().createPerson(Id.create(1, Person.class)); - Plan plan = PersonUtils.createAndAddPlan(person, true); - Activity act = PopulationUtils.createAndAddActivityFromCoord(plan, "home", new Coord((double) 0, (double) 0)); - act.setEndTime(8.0 * 3600); - PopulationUtils.createAndAddLeg( plan, TransportMode.transit_walk ); - Activity ptAct1 = PopulationUtils.createAndAddActivityFromCoord(plan, PtConstants.TRANSIT_ACTIVITY_TYPE, new Coord((double) 0, (double) 100)); - ptAct1.setMaximumDuration(0); - PopulationUtils.createAndAddLeg( plan, TransportMode.pt ); - Activity ptAct2 = PopulationUtils.createAndAddActivityFromCoord(plan, PtConstants.TRANSIT_ACTIVITY_TYPE, new Coord((double) 0, (double) 100)); - ptAct2.setMaximumDuration(0); - PopulationUtils.createAndAddLeg( plan, TransportMode.transit_walk ); - act = PopulationUtils.createAndAddActivityFromCoord(plan, "work", new Coord((double) 0, (double) 500)); - act.setEndTime(16*3600); - PopulationUtils.createAndAddLeg( plan, TransportMode.transit_walk ); - Activity ptAct3 = PopulationUtils.createAndAddActivityFromCoord(plan, PtConstants.TRANSIT_ACTIVITY_TYPE, new Coord((double) 0, (double) 100)); - ptAct3.setMaximumDuration(0); - PopulationUtils.createAndAddLeg( plan, TransportMode.pt ); - Activity ptAct4 = PopulationUtils.createAndAddActivityFromCoord(plan, PtConstants.TRANSIT_ACTIVITY_TYPE, new Coord((double) 0, (double) 100)); - ptAct4.setMaximumDuration(0); - PopulationUtils.createAndAddLeg( plan, TransportMode.transit_walk ); - PopulationUtils.createAndAddActivityFromCoord(plan, "work", new Coord((double) 0, (double) 500)); - boolean affectingDuration = true ; - - TripPlanMutateTimeAllocation mutator = - new TripPlanMutateTimeAllocation( - 3600., - affectingDuration, new Random(2011)); - mutator.run(plan); - - Assert.assertEquals(0.0, ptAct1.getMaximumDuration().seconds(), 1e-8); - Assert.assertEquals(0.0, ptAct2.getMaximumDuration().seconds(), 1e-8); - Assert.assertEquals(0.0, ptAct3.getMaximumDuration().seconds(), 1e-8); - Assert.assertEquals(0.0, ptAct4.getMaximumDuration().seconds(), 1e-8); - } - - @Test - public void testRunLatestEndTime() { - // setup population with one person - Person person = (Person) PopulationUtils.getFactory().createPerson(Id.create(1, Person.class)); - Plan plan = PersonUtils.createAndAddPlan(person, true); - Activity act = PopulationUtils.createAndAddActivityFromCoord(plan, "home", new Coord((double) 0, (double) 0)); - act.setEndTime(8.0 * 3600); - PopulationUtils.createAndAddLeg(plan, TransportMode.transit_walk); - Activity ptAct1 = PopulationUtils.createAndAddActivityFromCoord(plan, PtConstants.TRANSIT_ACTIVITY_TYPE, - new Coord((double) 0, (double) 100)); - ptAct1.setMaximumDuration(0); - PopulationUtils.createAndAddLeg(plan, TransportMode.pt); - Activity ptAct2 = PopulationUtils.createAndAddActivityFromCoord(plan, PtConstants.TRANSIT_ACTIVITY_TYPE, - new Coord((double) 0, (double) 100)); - ptAct2.setMaximumDuration(0); - PopulationUtils.createAndAddLeg(plan, TransportMode.transit_walk); - act = PopulationUtils.createAndAddActivityFromCoord(plan, "work", new Coord((double) 0, (double) 500)); - act.setEndTime(38 * 3600); - PopulationUtils.createAndAddLeg(plan, TransportMode.transit_walk); - Activity ptAct3 = PopulationUtils.createAndAddActivityFromCoord(plan, PtConstants.TRANSIT_ACTIVITY_TYPE, - new Coord((double) 0, (double) 100)); - ptAct3.setMaximumDuration(0); - PopulationUtils.createAndAddLeg(plan, TransportMode.pt); - Activity ptAct4 = PopulationUtils.createAndAddActivityFromCoord(plan, PtConstants.TRANSIT_ACTIVITY_TYPE, - new Coord((double) 0, (double) 100)); - ptAct4.setMaximumDuration(0); - PopulationUtils.createAndAddLeg(plan, TransportMode.transit_walk); - PopulationUtils.createAndAddActivityFromCoord(plan, "work", new Coord((double) 0, (double) 500)); - - boolean affectingDuration = true; - final double latestEndTime = 30. * 3600; - - TripPlanMutateTimeAllocation mutator = new TripPlanMutateTimeAllocation(3600., affectingDuration, - new Random(2011), null, null, latestEndTime); - mutator.run(plan); - - Assert.assertEquals(0.0, ptAct1.getMaximumDuration().seconds(), 1e-8); - Assert.assertEquals(0.0, ptAct2.getMaximumDuration().seconds(), 1e-8); - Assert.assertEquals(0.0, ptAct3.getMaximumDuration().seconds(), 1e-8); - Assert.assertEquals(0.0, ptAct4.getMaximumDuration().seconds(), 1e-8); - - // check whether activity times are equal or less than latestEndTime - for (PlanElement pe : plan.getPlanElements()) { - if (pe instanceof Activity activity) { - if (activity.getStartTime().isDefined()) { - Assert.assertTrue(activity.getStartTime().seconds() <= latestEndTime); - } - if (activity.getEndTime().isDefined()) { - Assert.assertTrue(activity.getEndTime().seconds() <= latestEndTime); - } - } - } - } -} diff --git a/pom.xml b/pom.xml index 44d423b0f53..0c79b29a8b7 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ 17 - 2.20.0 + 2.21.0 29.2 1.19.0 7.0.0 @@ -469,7 +469,7 @@ org.jacoco jacoco-maven-plugin - 0.8.10 + 0.8.11 default-prepare-agent