diff --git a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/router/TrainRouter.java b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/router/TrainRouter.java index 889d80d84fb..2414bb1859a 100644 --- a/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/router/TrainRouter.java +++ b/contribs/railsim/src/main/java/ch/sbb/matsim/contrib/railsim/qsimengine/router/TrainRouter.java @@ -87,7 +87,10 @@ public void setPosition(TrainPosition position) { @Override public double getLinkTravelDisutility(Link link, double time, Person person, Vehicle vehicle) { // only works with fixed block - return resources.hasCapacity(time, link.getId(), RailResourceManager.ANY_TRACK, position) ? 0 : 1; + int weight = resources.hasCapacity(time, link.getId(), RailResourceManager.ANY_TRACK, position) ? 0 : 1; + + // Small offset in the weight prevents dead-locks in case there are loops within the station + return weight + 0.00001; } @Override diff --git a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/integration/RailsimIntegrationTest.java b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/integration/RailsimIntegrationTest.java index 4bf88ef4c05..31f9571869b 100644 --- a/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/integration/RailsimIntegrationTest.java +++ b/contribs/railsim/src/test/java/ch/sbb/matsim/contrib/railsim/integration/RailsimIntegrationTest.java @@ -35,11 +35,15 @@ import org.matsim.api.core.v01.population.Plan; import org.matsim.api.core.v01.population.PlanElement; import org.matsim.core.api.experimental.events.VehicleArrivesAtFacilityEvent; +import org.matsim.core.api.experimental.events.VehicleDepartsAtFacilityEvent; import org.matsim.core.config.Config; import org.matsim.core.config.ConfigUtils; import org.matsim.core.config.groups.ScoringConfigGroup; import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.Controler; +import org.matsim.core.events.handler.EventHandler; +import org.matsim.core.gbl.MatsimRandom; +import org.matsim.core.mobsim.framework.listeners.MobsimListener; import org.matsim.core.scenario.ScenarioUtils; import org.matsim.core.utils.io.IOUtils; import org.matsim.examples.ExamplesUtils; @@ -47,18 +51,30 @@ import org.matsim.pt.transitSchedule.api.TransitLine; import org.matsim.pt.transitSchedule.api.TransitRoute; import org.matsim.pt.transitSchedule.api.TransitScheduleFactory; +import org.matsim.pt.transitSchedule.api.TransitStopFacility; import org.matsim.testcases.MatsimTestUtils; import org.matsim.testcases.utils.EventsCollector; import org.matsim.vehicles.Vehicle; import org.matsim.vehicles.VehicleType; +import static org.junit.jupiter.api.Assertions.fail; + import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Random; import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -317,6 +333,53 @@ void testMicroStationRerouting() { // These arrive closer together, because both waited assertTrainState(30974, 0, 0, 0, 400, filterTrainEvents(collector, "train3")); assertTrainState(30982, 0, 0, 0, 400, filterTrainEvents(collector, "train4")); + + // collect all arrivals and departures + Map, List>> train2arrivalStops = new HashMap<>(); + Map, List>> train2departureStops = new HashMap<>(); + + for (Event event : collector.getEvents()) { + if (event.getEventType().equals(VehicleArrivesAtFacilityEvent.EVENT_TYPE)) { + + VehicleArrivesAtFacilityEvent vehicleArrivesEvent = (VehicleArrivesAtFacilityEvent) event; + + if (train2arrivalStops.get(vehicleArrivesEvent.getVehicleId()) == null) { + + List> stops = new ArrayList<>(); + stops.add(vehicleArrivesEvent.getFacilityId()); + train2arrivalStops.put(vehicleArrivesEvent.getVehicleId(), stops); + } else { + train2arrivalStops.get(vehicleArrivesEvent.getVehicleId()).add(vehicleArrivesEvent.getFacilityId()); + } + } + + if (event.getEventType().equals(VehicleDepartsAtFacilityEvent.EVENT_TYPE)) { + + VehicleDepartsAtFacilityEvent vehicleDepartsEvent = (VehicleDepartsAtFacilityEvent) event; + + if (train2departureStops.get(vehicleDepartsEvent.getVehicleId()) == null) { + + List> stops = new ArrayList<>(); + stops.add(vehicleDepartsEvent.getFacilityId()); + train2departureStops.put(vehicleDepartsEvent.getVehicleId(), stops); + } else { + train2departureStops.get(vehicleDepartsEvent.getVehicleId()).add(vehicleDepartsEvent.getFacilityId()); + } + } + } + + // test if all trains have the correct number of arrivals and departures + for (Id vehicleId : train2arrivalStops.keySet()) { + Assertions.assertEquals(3, train2arrivalStops.get(vehicleId).size(), MatsimTestUtils.EPSILON, "Wrong number of arrival stops for train " + vehicleId); + } + for (Id vehicleId : train2departureStops.keySet()) { + Assertions.assertEquals(3, train2departureStops.get(vehicleId).size(), MatsimTestUtils.EPSILON, "Wrong number of departure stops for train " + vehicleId); + } + + // test if the trains have the correct arrival / departure stop facilities + Assertions.assertEquals("AB", train2arrivalStops.get(Id.createVehicleId("train3")).get(0).toString(), "Wrong stop facility. This is the start stop facility."); + Assertions.assertEquals("CD", train2arrivalStops.get(Id.createVehicleId("train3")).get(1).toString(), "Wrong stop facility. This is the rerouted stop Id. Train 3 is rerouted from CE to CD."); + Assertions.assertEquals("JK", train2arrivalStops.get(Id.createVehicleId("train3")).get(2).toString(), "Wrong stop facility. This is the final stop facility."); } @Test @@ -344,6 +407,32 @@ void testMicroStationReroutingConcurrent() { EventsCollector collector = runSimulation(new File(utils.getPackageInputDirectory(), "microStationRerouting"), filter); } + + @Test + void testMicroStationReroutingTwoDirections() { +// modifyScheduleAndRunSimulation(new File(utils.getPackageInputDirectory(), "microStationReroutingTwoDirections"), 300, 1800); + + ExecutorService executor = Executors.newSingleThreadExecutor(); + Future future = executor.submit(() -> { + modifyScheduleAndRunSimulation( + new File(utils.getPackageInputDirectory(), "microStationReroutingTwoDirections"), + 300, + 1800 + ); + }); + + try { + future.get(30, TimeUnit.SECONDS); + } catch (TimeoutException e) { + future.cancel(true); + fail("Simulation took too long and was aborted!"); + } catch (ExecutionException | InterruptedException e) { + // other exceptions + fail("An error occurred during the simulation: " + e.getMessage()); + } finally { + executor.shutdownNow(); + } + } @Test void testScenarioKelheim() { @@ -442,6 +531,50 @@ public void install() { return collector; } + + private void modifyScheduleAndRunSimulation(File scenarioDir, int maxRndDelayStepSize, int maxMaxRndDelay) { + Random rnd = MatsimRandom.getRandom(); + rnd.setSeed(1242); + + for (double maxRndDelaySeconds = 0; maxRndDelaySeconds <= maxMaxRndDelay; maxRndDelaySeconds = maxRndDelaySeconds + maxRndDelayStepSize) { + Config config = ConfigUtils.loadConfig(new File(scenarioDir, "config.xml").toString()); + + config.controller().setOutputDirectory(utils.getOutputDirectory() + "maxRndDelay_" + maxRndDelaySeconds); + config.controller().setDumpDataAtEnd(true); + config.controller().setCreateGraphs(false); + config.controller().setLastIteration(0); + + Scenario scenario = ScenarioUtils.loadScenario(config); + + Controler controler = new Controler(scenario); + controler.addOverridingModule(new RailsimModule()); + controler.configureQSimComponents(components -> new RailsimQSimModule().configure(components)); + + // modify scenario + for (TransitLine line : scenario.getTransitSchedule().getTransitLines().values()) { + for (TransitRoute route : line.getRoutes().values()) { + List departuresToRemove = new ArrayList<>(); + List departuresToAdd = new ArrayList<>(); + for (Departure departure : route.getDepartures().values()) { + departuresToRemove.add(departure); + double departureTime = departure.getDepartureTime() + rnd.nextDouble() * maxRndDelaySeconds; + Departure modifiedDeparture = scenario.getTransitSchedule().getFactory().createDeparture(departure.getId(), departureTime); + modifiedDeparture.setVehicleId(departure.getVehicleId()); + departuresToAdd.add(modifiedDeparture); + } + + for (Departure departureToRemove : departuresToRemove) { + route.removeDeparture(departureToRemove); + } + for (Departure departureToAdd : departuresToAdd) { + route.addDeparture(departureToAdd); + } + } + } + + controler.run(); + } + } private double timeToAccelerate(double v0, double v, double a) { return (v - v0) / a; diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationReroutingTwoDirections/config.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationReroutingTwoDirections/config.xml new file mode 100644 index 00000000000..da3761b5c4e --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationReroutingTwoDirections/config.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationReroutingTwoDirections/trainNetwork.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationReroutingTwoDirections/trainNetwork.xml new file mode 100644 index 00000000000..d2bf5887476 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationReroutingTwoDirections/trainNetwork.xml @@ -0,0 +1,399 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 999 + + + + + 999 + + + + + + 999 + + + + + + 999 + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + true + + + + + 1 + 2b_3b + + + + + 1 + 2b_3b + + + + + 1 + + + + + 1 + 4b_5b + + + + + 1 + 4b_5b + + + + + 1 + true + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + true + + + + + + 1 + + + + + 1 + true + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + 1 + 7b_8b + + + + + 1 + 7b_8b + + + + + 1 + 9b_10b + + + + + 1 + 9b_10b + + + + + + 1 + 3b_7b + + + + + 1 + 3b_7b + + + + + + 1 + 7b_9b + + + + + 1 + 7b_9b + + + + + + 1 + 8b_10b + + + + + 1 + 8b_10b + + + + + 1 + 4b_8b + + + + + 1 + 4b_8b + + + + + + 1 + + + + + 1 + + + + + \ No newline at end of file diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationReroutingTwoDirections/transitSchedule.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationReroutingTwoDirections/transitSchedule.xml new file mode 100644 index 00000000000..a1211eb2178 --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationReroutingTwoDirections/transitSchedule.xml @@ -0,0 +1,251 @@ + + + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + rail + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationReroutingTwoDirections/transitVehicles.xml b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationReroutingTwoDirections/transitVehicles.xml new file mode 100644 index 00000000000..6e2ecb5904a --- /dev/null +++ b/contribs/railsim/test/input/ch/sbb/matsim/contrib/railsim/integration/microStationReroutingTwoDirections/transitVehicles.xml @@ -0,0 +1,56 @@ + + + + + + + 0.5 + 0.5 + + + + + + + + + + + + + + + 0.7 + 0.7 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file